fix(rules): pick the best target square for melee cover
PF1e (CRB Cover, Big Creatures and Cover): when making a melee attack against a multi-square creature you can pick any of the squares it occupies to determine cover. has_cover_cells/cover_bonus_cells now aggregate the target side by the MINIMUM over target cells for melee and keep the maximum (any line to any corner) for ranged; the attacker-side minimum is unchanged. 1x1 geometry is bit-identical; no data case changes today, locked by a unit test (Large target: melee 0, ranged +4). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -150,3 +150,27 @@ def test_ranged_touch_attack_still_gets_cover() -> None:
|
||||
# Touch AC 13 + 4 standard cover: cover is NOT removed for touch.
|
||||
assert result.ac == 17
|
||||
assert result.hit is False # 12 + 4 = 16 < 17
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# (f) A5: melee vs a multi-square target picks the BEST target square
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_melee_vs_large_target_picks_best_target_square() -> None:
|
||||
grid = _grid(("..#...", "......", "......"))
|
||||
attacker = frozenset({(1, 0)})
|
||||
large_target = frozenset({(0, 3), (0, 4), (1, 3), (1, 4)})
|
||||
# Ranged: RAW checks ANY line to ANY target corner — the wall covers the
|
||||
# target's top row, so the worst cell's +4 stands.
|
||||
assert cover_bonus_cells(grid, attacker, large_target, ranged=True) == 4
|
||||
# Melee (CRB "Big Creatures and Cover"): "when making a melee attack
|
||||
# against such a creature, you can pick any of the squares it occupies to
|
||||
# determine if it has cover against you" — the attacker picks a clear
|
||||
# bottom-row square, so the target has no cover.
|
||||
assert cover_bonus_cells(grid, attacker, large_target, ranged=False) == 0
|
||||
# 1x1 regressions: the aggregation choice is invisible for single cells.
|
||||
assert cover_bonus_cells(grid, attacker, frozenset({(0, 3)}), ranged=False) == 4
|
||||
assert cover_bonus_cells(grid, attacker, frozenset({(0, 3)}), ranged=True) == 4
|
||||
assert cover_bonus_cells(grid, attacker, frozenset({(1, 3)}), ranged=False) == 0
|
||||
assert cover_bonus_cells(grid, attacker, frozenset({(1, 3)}), ranged=True) == 0
|
||||
|
||||
Reference in New Issue
Block a user