fix(rules): apply cover by attacker's best corner for multi-square attackers

This commit is contained in:
2026-09-11 11:43:10 +02:00
parent 9fb1c2d75a
commit a4814b935b
3 changed files with 185 additions and 14 deletions
+19 -8
View File
@@ -254,9 +254,12 @@ def cover_bonus_cells(
"""Effective cover bonus (0/2/4/8) for an attack between two sets of cells.
0 when there is no cover (see has_cover_cells for the corner rule);
otherwise the highest cover level among the cells blocking the
attacker-corner to target-corner lines. Interposed creatures grant soft
cover (+4) on ranged attacks only, exactly as in has_cover_cells.
otherwise the cover level of the attacker's BEST occupied cell: each
attacker cell is scored by the per-cell corner rule against all of the
target's cells, and the attacker picks the lowest-scoring (clearest)
cell. Interposed creatures grant soft cover (+4) on ranged attacks only,
exactly as in has_cover_cells. For a 1x1 attacker this is exactly the
single-cell value.
"""
if not attacker_cells or not target_cells:
return 0
@@ -271,10 +274,18 @@ def cover_bonus_cells(
occupied if ranged else frozenset(),
bonus_of=terrain_cover_bonus,
)
return max(
lines.bonus(corner_a, corner_t)
# PF1e CRB "Cover", rule of corners: the attacker chooses the corner (and,
# for a multi-square creature, the occupied square) that gives it the best
# shot, so it suffers only the MINIMUM cover over its own cells. The
# target side is unchanged: RAW checks ANY line to ANY target corner, so
# each attacker cell is scored by the MAXIMUM over its corner lines.
per_cell_best = (
max(
lines.bonus(corner_a, corner_t)
for corner_a in _corners(cell_a)
for cell_t in target_cells
for corner_t in _corners(cell_t)
)
for cell_a in attacker_cells
for corner_a in _corners(cell_a)
for cell_t in target_cells
for corner_t in _corners(cell_t)
)
return min(per_cell_best)