# Blind Spots From First Principles

(Strictly speaking for XCOM1, but should generalize to XCOM2 and Apocalypse.)

Zaimoni: It's a parallelogram problem: the two lines of fire are not coincident, and the displacement between the firing point and the center is "just enough" to create the blind spot.

Facing table [firer is at relative coordinates (0,0)]

borderline facing target real facing
N-NE (-2,1) N/0
E-NE (-1,2) NE/1
E-SE (1,2) E/2
S-SE (2,1) SE/3
S-SW (2,-1) S/4
W-SW (1,-2) SW/5
W-NW (-1,-2) W/6
N-NW (-2,-1) NW/7

Partial firing voxel table (from Seb76's work):

facing internal x-y coordinates of voxel
N/0 (8,1)
NE/1 (14,1)
E/2 (15,8)
SE/3 (15,15)
S/4 (8,15)
SW/5 (1,15)
W/6 (1,8)
NW/7 (1,1)

(Following to be revised) Example: north wall in square 1 south of unit, ambusher unit facing SE (facing 3). Defender facing when returning fire is 7. Assuming a "sane" pixel-line drawing algorithm:

• If the resulting firing line has classical SE slope strictly exceeding 2, the ambusher's firing line should hit the north wall and not be an issue.
• If the classical SE slope is exactly 2, we have an edge case and the actual line-tracing algorithm determines whether the ambusher has a line of fire.
• Due SE isn't a blind spot.
• In both cases, we have a 8-pixel diagonal line (containing the endpoints) between the unit's centroid at (8,8) and the firing origin. The defender can return fire if his line of fire goes through the ambusher's firing origin, otherwise he is blind. [This only makes sense because we're analyzing in voxelspace.) The critical classical SE slope is 15/14; for this slope, the actual line-tracing algorithm determines whether the ambusher has a line of fire.

Without loss of generality, consider the ambusher to be at (0,0); we wish to identify squares with range 19 or less in (1..18,1..18) that should be ambushable.

target ambush line of fire SE slope targetable? ambush?
(2,1) ((-15, 15), (-40, 24)) 25/9 no
(3,2) ((-15, 15), (-56, 40)) 41/25 yes yes
(4,2) ((-15, 15), (-72, 40)) 57/25 no
(4,3) ((-15, 15), (-72, 56)) 57/41 yes yes
(5,3) ((-15, 15), (-88, 56)) 73/41 no
(5,4) ((-15, 15), (-88, 72)) 73/57 yes yes
(6,4) ((-15, 15), (-104, 72)) 89/57 yes yes
(7,4) ((-15, 15), (-120, 72)) 105/57 yes yes
(8,4) ((-15, 15), (-136, 72)) 121/57 yes yes
(6,5) ((-15, 15), (-104, 88)) 89/73 yes yes
(7,5) ((-15, 15), (-120, 88)) 105/73 yes yes
(8,5) ((-15, 15), (-136, 88)) 121/73 yes yes
(9,5) ((-15, 15), (-152, 88)) 137/73 yes yes
(10,5) ((-15, 15), (-168, 88)) 153/73 no
(7,6) ((-15, 15), (-120, 104)) 105/87 yes yes
(8,6) ((-15, 15), (-136, 104)) 121/87 yes yes
(9,6) ((-15, 15), (-152, 104)) 137/87 yes yes
(10,6) ((-15, 15), (-168, 104)) 153/87 yes yes
(11,6) ((-15, 15), (-184, 104)) 169/87 yes yes
(12,6) ((-15, 15), (-200, 104)) 185/87 no
(8,7) ((-15, 15), (-136, 120)) 121/103 yes yes
(9,7) ((-15, 15), (-152, 120)) 137/103 yes yes
(10,7) ((-15, 15), (-168, 120)) 153/103 yes yes
(11,7) ((-15, 15), (-184, 120)) 169/103 yes yes
(12,7) ((-15, 15), (-200, 120)) 185/103 yes yes
(13,7) ((-15, 15), (-216, 120)) 201/103 yes yes
(14,7) ((-15, 15), (-232, 120)) 217/103 no
(9,8) ((-15, 15), (-152, 136)) 137/119 yes yes
(10,8) ((-15, 15), (-168, 136)) 153/119 yes yes
(11,8) ((-15, 15), (-184, 136)) 169/119 yes yes
(12,8) ((-15, 15), (-200, 136)) 185/119 yes yes
(13,8) ((-15, 15), (-216, 136)) 201/119 yes yes
(14,8) ((-15, 15), (-232, 136)) 217/119 yes yes
(15,8) ((-15, 15), (-248, 136)) 233/119 yes yes
(10,9) ((-15, 15), (-168, 152)) 153/135 yes yes
(11,9) ((-15, 15), (-184, 152)) 169/135 yes yes
(12,9) ((-15, 15), (-200, 152)) 185/135 yes yes
(13,9) ((-15, 15), (-216, 152)) 201/135 yes yes
(14,9) ((-15, 15), (-232, 152)) 217/135 yes yes
(15,9) ((-15, 15), (-248, 152)) 233/135 yes yes
(11,10) ((-15, 15), (-184, 168)) 169/151 yes yes
(12,10) ((-15, 15), (-200, 168)) 185/151 yes yes
(13,10) ((-15, 15), (-216, 168)) 201/151 yes yes
(14,10) ((-15, 15), (-232, 168)) 217/151 yes yes
(12,11) ((-15, 15), (-200, 184)) 185/167 yes yes
(13,11) ((-15, 15), (-216, 184)) 201/167 yes yes
(14,11) ((-15, 15), (-232, 184)) 217/167 yes yes
(13,12) ((-15, 15), (-216, 200)) 201/183 yes yes

[Yes, strictly speaking all of those classical slopes are negative, but that's implied (for this choice of coordinates) by specifying a southeast bearing.]

We have a dual analysis and table for ambusher with west wall one square east.

Python 2.6 source code for constructing the ambush line of fire for the above table follows (should also be in the next update to my XCOM editor suite as XCOM1.py):

```# Voxelspace: 0..15,0..15,0,23..0 internal coordinates
fire_voxel_x_y_from_facing = ((1,8),(1,14),(8,15),(15,15),(15,8),(15,1),(8,1),(1,1))

# coords are internal-square: row,col format with 0,0 at upper-left
# we need to self-test this
def facing_from_coords(src,dest):
delta = (dest-src,dest-src)
if 0==delta:
if 0<delta:
return 2
else:
return 6
elif 0==delta:
if 0<delta:
return 4
else:
return 0
elif 0<delta:
if 0<delta:
if 2*delta<=delta:
return 2
elif 2*delta<delta:
return 4
else:
return 3
else: #if 0>delta
if 2*delta<-delta:
return 6
elif -2*delta<=delta:
return 4
else:
return 5
else: #if 0>delta
if 0<delta:
if -2*delta<delta:
return 2
elif 2*delta<=-delta:
return 0
else:
return 1
else: #if 0>delta
if -2*delta<=-delta:
return 6
elif -2*delta<-delta:
return 0
else:
return 7

# yes, Voxelspace rows are reverse-order to coordinate rows
def convert_firearm_attack_unit_coords_from_map_to_voxels(src,dest):
fire_offset = fire_voxel_x_y_from_facing[facing_from_coords(src,dest)]
return ((-16*src-fire_offset,16*src+fire_offset),(-16*dest-8,16*dest+8))
```