Commit Graph

21 Commits

Author SHA1 Message Date
ctan d152d42af9 fix(rules): avoid the firing-into-melee penalty at 10 feet
SRD 'Attack - Shooting or Throwing into a Melee': a target at least 10 ft from the nearest friendly character lets you avoid the -4 penalty entirely (0, not -2). The -2 is reserved for targets two size categories larger than the engaged friendlies (0 at three+), which is not modeled (documented). Golden transcript unaffected (shot still misses).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:21:26 +02:00
ctan e1c9e78b4e fix(rules): stop auto-directed movement cutting corners
PF1e (CRB, Measuring Distance): "You can't move diagonally past a corner
(even by taking a 5-foot step)". _best_move_step (the greedy descent behind
the auto-directed move/run/5-foot-step) now enforces the same
diagonal_allowed corner check _step5_to and _best_flee_step already apply.

tests/test_combat.py::test_ranged_no_line_of_effect_moves_around_wall
pinned the old corner-cutting step (0,1)->(1,2) past the wall at (0,2);
re-scripted for the legal (0,1)->(1,1) path (wall cover now applies).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:48:12 +02:00
ctan 74bfeb5414 fix(rules): allow one movement opportunity per opponent per round
PF1e (CRB Attacks of Opportunity): moving out of more than one square
threatened by the same opponent in the same round counts as one
opportunity for that opponent. Track per-round movement provocation in
_move_aoo_provoked (cleared with _aoo_used at round start); the stand-up
trigger passes movement=False and still provokes. Ranged, casting and
maneuver triggers are untouched.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:08:11 +02:00
ctan 9aee4f6f6b fix(rules): correct P2/P3 PF1e conformance bugs (flanking, touch, blinded, disabled, death, maneuvers, DR, sneak) 2026-09-10 21:45:50 +02:00
ctan 2eed752f7f feat(combat): add dying stabilization and Improved Critical 2026-09-10 10:43:02 +02:00
ctan 97f4429c2b feat(combat): add concealment miss chance, sneak attack, and Precise Shot 2026-09-10 10:22:50 +02:00
ctan 6e4a9d8323 feat(combat): Combat Reflexes extra AoOs 2026-09-04 23:02:24 +02:00
ctan 94521394ef feat(combat): add 5-foot step (free action, no AoO, mutual exclusion with move) 2026-08-17 22:49:50 +02:00
ctan 5b8735f001 fix(combat): separate standard attack (single swing) from full attack (iteratives) 2026-08-17 22:49:50 +02:00
ctan 5b1957152c feat(combat): add withdraw action (double move with AoO protection on first square) 2026-08-17 22:49:50 +02:00
ctan 7543c55ed4 feat(combat): add charge action (straight-line 2x speed, +2 attack, -2 AC) 2026-08-17 22:49:50 +02:00
ctan 004d6f1c2b feat(combat): add attacks of opportunity (movement + ranged-in-melee triggers) 2026-08-17 22:49:50 +02:00
ctan ac18435695 feat(combat): add full attack action with BAB iterative attacks
- New Action kind 'full_attack': manufactured weapons (count=1) gain BAB
  iteratives at +6/+11/+16 (-5/-10/-15 from attack bonus, capped at 4);
  natural weapons (count>1) delegate to _attack (no iteratives).
- Extracted _resolve_swing from _attack: one swing, log, stats, target-down
  check. Both _attack and _full_attack use it. AttackResult gains base_bonus
  field so the transcript shows the actual bonus used per iterative.
- default_policy now returns full_attack instead of attack when in range.
  Non-breaking for BAB < 6 (single swing, identical transcript).
- Refactored round_no to self._current_round instance variable, removing it
  from 6 method signatures and fixing PLR0913 on _resolve_swing.
- 4 new tests: BAB+6 two iteratives, BAB+11 three iteratives, target drops
  mid-full-attack, BAB<6 single swing. README and docstring updated; test
  count 202 -> 206.
2026-08-17 22:49:50 +02:00
ctan b20f845606 feat(combat): apply flanking +2 bonus when ally threatens opposite side
- _flanking_bonus checks for an active same-side ally with a melee weapon
  at the position diametrically opposite the attacker through the target.
- +2 applies to melee attack rolls and crit-confirm rolls; ranged attacks
  ignore flanking; allies without melee weapons don't threaten.
- AttackResult gains flank_bonus field; transcript shows '+2(flank)' when
  active.
- 6 new tests: opposite ally grants +2, no ally = no bonus, ranged ignores
  flanking, diagonal flanking, downed ally doesn't flank, enemy on opposite
  side doesn't flank.
- README and module docstring updated; test count 196 -> 202.
2026-08-17 22:49:50 +02:00
ctan 3c31b88b13 feat(combat): activate standard+move economy (full-speed movement, move-then-attack)
- default_policy returns (move, attack) when target out of reach: full-speed
  move along the Dijkstra path (up to speed cells, 5-10-5 diagonals, stops
  adjacent to nearest enemy), then attack if a weapon is usable; returns
  (attack,) when already in range.
- _move logs the full path in one line (start)->(step1)->...->(end); silent
  on empty path. _execute is silent on invalid attacks/moves (no spurious
  wait log); run() adds a wait line only when a combatant's turn produced
  no transcript line.
- Extract _take_turn helper from run() to keep cyclomatic complexity <= 10.
- Module docstring updated for activated economy.
- 4 pinned transcripts regenerated (move-then-attack shifts combat pacing):
  test_default_policy_moves_toward_enemy, test_scripted_2v2_transcript
  (monsters win in R2 instead of R3 — orc-2 moves + attacks same turn),
  test_ranged_no_line_of_effect_moves_around_wall (archer rounds wall and
  shoots in 1 turn), test_ranged_weapon_unusable_beyond_maximum_range
  (archer moves 6 cells then attacks at -14 penalty).
- README: demo numbers refreshed (1v2: 49.6%->22.0%, 11.4->5.2 rounds;
  3v2: 97.8%->91.4%, 6.9->6.0 rounds), rules and non-modeled sections
  updated for the activated economy.
2026-08-17 22:49:50 +02:00
ctan bb53d049ee feat(combat): grant soft cover (+4 AC) to ranged attacks through creatures 2026-08-17 22:49:50 +02:00
ctan f67cd017e4 feat(combat): apply cumulative -2 range penalties per increment 2026-08-17 22:49:50 +02:00
ctan b748980423 feat(combat): honor AttackSpec.count for multi-swing attacks
A weapon with count>1 ('2x Talons') now resolves one independent attack
roll per swing in the same attack action (label '#N'), aggregating stats
per swing. Remaining swings are lost if the target drops mid-routine.
BAB iterative attacks remain out of Phase 0 scope.
2026-08-17 22:49:50 +02:00
ctan e17c571d7e feat(combat): wire corner-rule LoS/cover into attack resolution
- weapon_for: gate attacks on has_line_of_effect (no LoE -> policy moves)
- resolve_attack: +4 AC cover bonus on hit and crit-confirm (ranged flag)
- tests: cover bonus (ranged pillar, melee wall corner), no-LoE move/attack,
  no-LoE unreachable wait; 11 resolve_attack call sites updated
- README: quick-start and verified example re-measured (55.7% 1x2), rules
  and architecture updated (los.py wired)
2026-08-17 22:49:50 +02:00
ctan a21e234b41 feat(runner): Monte Carlo runner, closed-form metrics and balance CLI
Adds the S6 layer on top of the deterministic combat engine:
- metrics.py: closed-form binomial statistics (win rate, standard error,
  3-sigma confidence band) with clamped degenerate proportions.
- runner.py: EncounterSpec/Side definitions, per-run SeededRng streams,
  global duplicate-id disambiguation, attrition averages per combatant.
- cli.py: pf1e-sim entry point producing a French balance report with
  win rates, 3-sigma bands, draws, rounds, and attrition.
- Movement fix: greedy straight-line heuristic could oscillate at walls;
  _step_toward now follows the true shortest path via an unbounded
  Dijkstra cost field from the target (Grid.reachable budget=None).
- Regression tests: melee unit routes around a wall and engages; grid
  unbounded-budget coverage.
2026-08-17 22:49:50 +02:00
ctan 577aec33c4 feat(combat): deterministic engine with attacks, crits, DR, hp states, initiative, rounds, basic policies 2026-08-17 22:49:50 +02:00