Commit Graph

116 Commits

Author SHA1 Message Date
ctan a4814b935b fix(rules): apply cover by attacker's best corner for multi-square attackers 2026-09-11 11:43:10 +02:00
ctan 9fb1c2d75a fix(rules): apply energy resistance per damage component 2026-09-11 11:24:11 +02:00
ctan 1960b8f655 fix(rules): scale Power Attack for two-handed weapons and apply its CMB penalty 2026-09-11 00:19:06 +02:00
ctan 890b7959d4 fix(rules): correct metamagic slots, touch-spell delivery, and defensive/grapple casting 2026-09-11 00:06:06 +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 5afe495c4a fix(rules): correct P1 PF1e conformance bugs (area saves, concentration, prone, slots, helpless, nauseated) 2026-09-10 20:52:28 +02:00
ctan 1ca5aed758 data(spells): add batch 3 of PC spell list (32 spells) 2026-09-10 19:43:07 +02:00
ctan 543da6a931 data(spells): add batch 2 of PC spell list (32 spells) 2026-09-10 19:12:57 +02:00
ctan 07e0d83300 data(spells): add batch 1 of PC spell list (32 spells) 2026-09-10 18:49:54 +02:00
ctan 91f70f19ca feat(conditions): add bleeding and confused rules 2026-09-10 18:15:35 +02:00
ctan b3bff85215 feat(conditions): add blinded/invisible/helpless/fascinated combat rules 2026-09-10 18:00:41 +02:00
ctan 79ac190728 feat(combat): use fly speed for movement
Movement budget is now max(land, fly) speed. Updates the harvie golden: Harvie (land 10 / fly 80) is a flyer and now flies at 80 ft, so he wins vs the goblin instead of being pinned crawling at 10 ft.
2026-09-10 17:46:57 +02:00
ctan ec0397351a test(rules): lock in that size is baked into attack/AC values 2026-09-10 17:25:05 +02:00
ctan 582b6c60d1 feat(observation): expose ranged threat grid 2026-09-10 17:12:54 +02:00
ctan f637f87a8c feat(combat): add aid another and run actions 2026-09-10 16:56:38 +02:00
ctan 06555f7b49 test(llm): demonstrate unblocked engine capabilities via observation-only policy 2026-09-10 15:56:21 +02:00
ctan 26f86f416d feat(combat): allow point-targeted spell areas 2026-09-10 15:19:36 +02:00
ctan 7fafbe43cf feat(observation): expose structured spell areas 2026-09-10 14:14:54 +02:00
ctan 61c9b7fa47 feat(observation): expose remaining attacks of opportunity 2026-09-10 14:08:25 +02:00
ctan 6abd29cbc5 feat(observation): expose attack failure reasons 2026-09-10 13:57:34 +02:00
ctan 8f3bd0d0e0 feat(observation): expose per-pair cover bonus 2026-09-10 13:46:05 +02:00
ctan ed04052244 feat(combat): allow selecting the attacking weapon 2026-09-10 13:36:27 +02:00
ctan b91e1ac215 feat(combat): allow explicit movement destinations 2026-09-10 13:21:54 +02:00
ctan 468c22ff57 feat(combat): add stand up and drop prone 2026-09-10 13:07:58 +02:00
ctan ebdff4f7e5 feat(combat): add total defense and fighting defensively 2026-09-10 12:52:47 +02:00
ctan ab02597bc4 feat(combat): add ready action with triggers 2026-09-10 12:39:28 +02:00
ctan f83e4b8016 feat(combat): add delay action 2026-09-10 12:20:33 +02:00
ctan c5cfa78f87 feat(observation): expose initiative order and totals 2026-09-10 12:04:10 +02:00
ctan 4e115f9176 feat(combat): surface explicit action results and reject invalid actions 2026-09-10 11:51:02 +02:00
ctan e6125b04f6 feat(combat): support partial and improved cover levels 2026-09-10 11:32:08 +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 a1c43e83af feat(combat): enforce spell resistance checks and energy resistances 2026-09-06 19:18:10 +02:00
ctan a9ccdd3a56 feat(combat): add activate_power_attack action and consume Up Close and Deadly deed
- New Action kind activate_power_attack: free action that sets
  state.power_attack = True when the combatant has the Power Attack feat;
  logged no-op otherwise. Routed via _execute_movement (free-action branch)
  to keep _execute under the C901 complexity ceiling.
- Consume deed_up_close_and_deadly in resolve_attack: the first attack
  resolved while the flag is set gains +1d6 if it is a ranged attack within
  30 ft; the flag is reset in all cases (benefit lost if out of range or
  not ranged). Extracted into _consume_up_close_and_deadly helper.
- Document activate_power_attack, corrected deed semantics, and the
  power_attack snapshot key in README.
- Add tests/test_engine_fixes.py (5 tests): PA activation, no-feat no-op,
  deed+ranged within 30ft, deed out-of-range, deed melee.
2026-09-06 18:37:58 +02:00
ctan e29c472a67 feat(combat): expose DR, conditions, creature type, features, and spell save semantics in observation 2026-09-06 18:18:13 +02:00
ctan d2cd6f0702 docs: document enriched observation snapshot and add LLM demo test 2026-09-06 16:50:42 +02:00
ctan cd796ecffb docs: document LLM observation contract and expanded action options 2026-09-06 11:14:18 +02:00
ctan febd408f29 docs: document B5 engine hardening - reach, sizes, elevation, immediate actions 2026-09-05 18:04:35 +02:00
ctan 7cb3653750 fix(combat): apply save negates to targeted spell damage effects 2026-09-05 12:52:58 +02:00
ctan cd75a4f2bf feat(combat): implement overrun, dirty trick, steal, reposition and drag maneuvers 2026-09-05 12:37:06 +02:00
ctan 2f2acd992f fix(combat): evaluate conditional stat modifiers with action context 2026-09-05 12:17:58 +02:00
ctan 31d970dd5f fix(combat): resolve touch attacks against touch AC and firearms 2026-09-05 11:28:46 +02:00
ctan 8e2753e67a chore: final green gate 2026-09-05 09:09:20 +02:00
ctan 22d76e9677 fix(combat): channel energy targets living vs undead 2026-09-05 00:16:17 +02:00
ctan f4fc66a6ab docs: align README with fixed behavior 2026-09-04 23:02:26 +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