feat(abilities): add Power Attack active feat (-X atk / +2X dmg, melee only)

Power Attack is the first active feat: a free-action toggle declared at
turn start. When state.power_attack is True and the combatant has the
POWER_ATTACK feature, melee attacks apply -X to the attack roll and
+2X to damage, where X = BAB//4 + 1 (min 1). Ranged attacks are
unaffected. The flag is cleared at the start of each turn via
_take_turn, like other per-turn state.

- abilities.py: POWER_ATTACK constant (AbilitySpec, no passive effects)
- combat.py: _has_feat helper, _power_attack_amt, resolve_attack
  applies penalty to attack total + crit confirm, bonus to damage
- tests/test_power_attack.py: 14 tests (amount at BAB 1/4/8/12, flag
  false, feat missing, attack penalty, damage bonus, ranged unaffected,
  flag cleared on turn)
- pyproject.toml: add SLF001 + RUF059 to test per-file-ignores (white-box
  testing accesses private helpers, partial tuple unpacking)
- README.md: document Power Attack in abilities.py section, 307 tests
This commit is contained in:
2026-08-17 22:49:50 +02:00
parent ba1c3c2117
commit 97c0431479
5 changed files with 266 additions and 8 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ allowed-confusables = ["σ"]
[tool.ruff.lint.per-file-ignores]
# Tests: assert is expected, magic numbers are fine, annotations are noisy,
# parametrized golden tests legitimately take many arguments
"tests/**" = ["S101", "PLR2004", "ANN", "PLR0913", "PLR0917", "ARG001"]
"tests/**" = ["S101", "PLR2004", "ANN", "PLR0913", "PLR0917", "ARG001", "SLF001", "RUF059"]
# rng.py: `random.Random` is used for reproducible Monte Carlo streams, not cryptography
"src/pf1e_simulator/rng.py" = ["S311"]
# cli.py: stdout printing is the point of a CLI