Commit Graph

63 Commits

Author SHA1 Message Date
ctan 7a00911c55 fix(combat): bound cackle hex extension 2026-09-04 22:55:56 +02:00
ctan 293f20c98a fix(combat): summoned creatures act and expire 2026-09-04 22:55:55 +02:00
ctan d99cd30309 refactor(combat): single source active_buffs/active_conditions 2026-09-04 22:55:55 +02:00
ctan fb413d76a8 feat(combat): class-ability action kinds, engine RNG only 2026-09-04 22:55:54 +02:00
ctan 90b253185c fix(combat): correct hex and AoE DCs, DR bypassed by spells 2026-09-04 22:55:53 +02:00
ctan febfb76e63 fix(combat): correct concentration DCs by situation 2026-09-04 22:55:52 +02:00
ctan 7065d1f990 feat(runner): wire spell registry in CLI and Monte Carlo 2026-09-04 22:55:51 +02:00
ctan aa711c4023 feat(data): per-level spell slots for 8 PCs 2026-09-04 22:55:50 +02:00
ctan 7336109ab2 chore(lint): restore green gate 2026-09-04 22:55:49 +02:00
ctan bc5bf918b5 feat(data): lich monster 2026-09-04 22:55:48 +02:00
ctan 46ac13a91e feat(combat): class action patterns 2026-09-04 22:55:48 +02:00
ctan ba2167526a feat(combat): channel energy and hexes 2026-09-04 22:55:47 +02:00
ctan b300a413be feat(data): companions and mounts 2026-09-04 22:55:46 +02:00
ctan df624cc041 feat(loaders): map 8-PC feats 2026-09-04 22:55:45 +02:00
ctan b5c15ae60b feat(combat): summoning 2026-09-04 22:55:44 +02:00
ctan 03e0d6ecb6 feat(combat): combat maneuvers CMB/CMD 2026-09-04 22:55:43 +02:00
ctan 7e1aa25bc1 feat(combat): area-of-effect spells 2026-09-04 22:55:43 +02:00
ctan cd09aa1087 feat(combat): spellcasting default policy 2026-09-04 22:55:42 +02:00
ctan 842d5aee7e feat(combat): skills system 2026-09-04 22:55:41 +02:00
ctan 223e10b88e feat(combat): AoO and concentration on casting 2026-09-04 22:55:40 +02:00
ctan 67df26e042 feat(combat): enforce behavioral condition flags 2026-09-04 22:55:40 +02:00
ctan d3f7f27c51 feat(combat): metamagic 2026-09-04 22:55:39 +02:00
ctan 1c039ff4f1 feat(models): combat resources 2026-09-04 22:55:38 +02:00
ctan 6e27a2bba1 fix(combat): correct spell DC formula 2026-09-04 22:55:37 +02:00
ctan 8b5ba12f46 feat(combat): effect and condition durations 2026-08-29 20:14:36 +02:00
ctan 2e49aa7293 docs(handoff): add finalize-engine plan for session transfer 2026-08-29 13:37:32 +02:00
ctan 52904a0d03 docs(handoff): add session summary for pf1e-simulator transfer 2026-08-29 13:35:47 +02:00
ctan 37f07d90a7 chore(data): drop Plot Armor from misty features (non-combat feat) 2026-08-18 17:34:48 +02:00
ctan 82148cea1e test(loaders): add unit tests for combatant feat resolution and feature/spell handling
Tests for _resolve_feat (known/unknown/Toughness/Weapon Focus),
_resolve_features (filtering, empty lists), load_combatant with
features+spells integration, and backward-compat aliases.

Gate: 401 tests, ruff clean, basedpyright clean.
2026-08-18 15:11:28 +02:00
ctan 36f72dcb37 chore(clean): cleanup .codegraph 2026-08-17 22:54:53 +02:00
ctan 557bb2304a fix(git): fix gitignore for .codegraph 2026-08-17 22:53:58 +02:00
ctan c88d8fd6f4 refactor(loaders): unify combatant loader, migrate PC features/spells
Rename monster.py → combatant.py with _KNOWN_FEATS lookup table
that resolves feat name strings to AbilitySpec constants (Power Attack,
Iron Will, Toughness, Weapon Focus, etc.). Unknown feats produce
minimal AbilitySpec (data preserved for future implementation).

Migrate all 8 PC JSONs (data/pcs/) with complete feat/spell lists
extracted from Foundry sheets — including traits, racial abilities,
and all spells. Monsters (goblin, orc) unchanged (no feats/spells).

Update all imports, add backward-compatible aliases (load_monster,
MonsterLoadError), update CLI to use load_combatant.

Gate: 377 tests, ruff clean, basedpyright clean.
2026-08-17 22:49:50 +02:00
ctan e9e8c26d50 feat(loaders): extend Foundry loader with feat/spell extraction
Map Foundry feat strings to AbilitySpec constants (Power Attack, Iron Will,
Alertness, Point-Blank Shot, Precise Shot, Dodge, Weapon Focus (X)).
Extract spell names from spellcasting.spells into Combatant.spells.
Wire features and spells into load_sheet_detailed return.
Add 41 tests (unit + golden on 8 real sheets).

Gate: 377 tests, ruff clean, basedpyright clean.
2026-08-17 22:49:50 +02:00
ctan 828afc15c0 feat(spells): add spell system with 10 combat spells, cast_spell action
Spells are SpellSpec dataclasses with typed effects: DamageEffect
(dice + types, half-on-save), ConditionEffect (condition lookup +
save negates), HealEffect (capped at hp_max), BuffEffect (transient
StatModifiers). Range categories (personal/touch/close/medium/long)
scale with caster level via spell_range_ft. JSON loader
(load_spell/load_spell_registry) reads data/spells/*.json.

The engine integrates spells via Action(kind='cast_spell') and
_cast_spell: range check, line-of-effect gate, save resolution
(resolve_save), effect application (_apply_spell_effects with DR
via _apply_dr_for_types), transcript logging. The spell_registry
is passed to CombatEngine; Combatant.spells holds known spell names.

- spells.py: SpellSpec, 4 effect types, SpellRange/SaveType literals,
  spell_range_ft, JSON loader (uses Json type from foundry.py)
- conditions.py: CONDITIONS_BY_NAME registry for condition lookup
- combat.py: cast_spell Action kind, _execute_cast_spell dispatch,
  _cast_spell (range/LoE/save/effects/log), _apply_spell_effects,
  _apply_dr_for_types, _save_label helper, SpellSpec/BuffEffect/etc
  imports, spell_registry parameter on CombatEngine
- models.py: spells field on Combatant
- data/spells/: 10 spells (Magic Missile, Burning Hands, Fireball,
  Lightning Bolt, Acid Arrow, Cure Light/Moderate Wounds, Hold Person,
  Fear, Bull's Strength)
- tests/test_spells.py: 29 tests (loading, range, damage/save/condition/
  heal/buff/DR, out-of-range, unknown spell, dispatch, DC scaling)
- pyproject.toml: PLR0913 ignore for combat.py (CombatEngine.__init__)
- README.md: spells.py architecture section, Sorts removed from
  non-modeled list, 336 tests
2026-08-17 22:49:50 +02:00
ctan 97c0431479 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
2026-08-17 22:49:50 +02:00
ctan ba1c3c2117 feat(abilities): add passive feats system (Weapon Focus, Iron Will, Dodge, Point-Blank Shot) 2026-08-17 22:49:50 +02:00
ctan a32444f804 feat(conditions): add condition system with 18 PF1e conditions 2026-08-17 22:49:50 +02:00
ctan 2cd8a33a4d feat(combat): add saving throws (fort/ref/will, natural 1/20, effect modifiers) 2026-08-17 22:49:50 +02:00
ctan 88f11de40f refactor(combat): replace ac_penalty with effect system 2026-08-17 22:49:50 +02:00
ctan 8ac6f9eabe feat(effects): add effect system with PF1e bonus stacking rules 2026-08-17 22:49:50 +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 791199150b feat(combat): add action economy framework (6 action types, sequence policy) 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