Commit Graph

330 Commits

Author SHA1 Message Date
ctan b453b19be2 fix(rules): stop a run from crossing difficult terrain
PF1e CRB 'Movement, Position and Distance - Difficult Terrain': 'You
can't run or charge across difficult terrain.' The run action reused the
move machinery (_descend_path), so it crossed rubble at 2x cost within
the 4x budget. The descent now takes an avoid_difficult option that
refuses every step whose footprint would touch a cell with move cost > 1
(the same rejection charge applies in _charge_path_clear); the run
moves up to the obstacle and stops there (deterministic, documented).
Move and charge behavior are unchanged.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 22:16:54 +02:00
ctan 4d89305881 fix(rules): distinguish natural reach from reach weapons
PF1e CRB 'Big and Little Creatures in Combat': a creature's natural
reach is a disk (a Large creature still threatens and strikes adjacent
squares), while a reach weapon doubles the wielder's natural reach but
'can't strike at their natural reach or less' (a ring whose dead zone is
the natural reach). The engine keyed the ring off reach_ft > 5, so
reach-10 natural attacks (giant belostomatid bite/claw, mounted
hooves) wrongly got a 5-ft dead zone.

AttackSpec gains reach_weapon (parsed from the combatant JSON); the
disk-vs-ring rule is centralized in _melee_reach_zone_cells and applied
consistently to weapon selection, melee usability, threatened squares
and charge end placement. Esha's Lance is the only reach weapon in the
shipped data; Large natural attackers keep the default (disk).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 22:06:46 +02:00
ctan 4f6b076a49 fix(rules): exempt retrieve and repair weapon from the disabled hit-point cost
PF1e (CRB, Disabled): a disabled creature can take move actions without further injuring itself. retrieve_weapon and repair_weapon are budget-classified move actions (_ACTION_ECONOMY_MOVE_KINDS) but were absent from _DISABLED_MOVE_KINDS, so a creature at 0 hp paid 1 hp to draw or mend a weapon. Added both kinds to the exempt set.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 21:22:03 +02:00
ctan fa01ed74c5 fix(rules): stop delay from double-processing the turn start
A freshly-delayed combatant is re-inserted into the round order and _take_turn runs a second time in the same round. That second pass re-ran _decrement_durations and _turn_gate, double-ticking bleed, double-decrementing buff/condition durations and re-firing the fear aura. The start-of-turn block is now skipped when delayed_this_round is already set at entry (cleared at round start, set only by a delay action); the delayed turn still gets its policy call, budget and execution.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 21:15:14 +02:00
ctan 1b5cac234e fix(rules): tick bleed and auto-fail stabilization for a dying creature
PF1e (CRB, Stable Characters and Recovery): characters taking continuous damage such as bleed automatically fail all Constitution checks made to stabilize, losing 1 hp per round in addition to the continuous damage. _process_dying never applied bleed (only _turn_gate, reached by active creatures did), so a bleeding dying creature lost 0 hp per round. It now ticks bleed_damage at the start of the dying turn and auto-fails the check (no d20 consumed), then evaluates death.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 21:06:10 +02:00
ctan 32c09df264 fix(rules): keep swift and free actions for staggered combatants
PF1e (CRB, Staggered): a staggered creature takes a single move OR standard action but can still take free, swift, and immediate actions. _staggered_filter returned only the first non-full-round action, so a disabled combatant with a queued swift bonus (deed/inspire_courage) lost its standard action. It now keeps every swift and free action plus the first move-or-standard, preserving the full_attack -> single attack downgrade.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 20:59:19 +02:00
ctan 529999da69 fix(rules): correct the inspire courage bonus type and saves
PF1e CRB (Bardic Performance - Inspire Courage): the attack/weapon-damage
bonus is a COMPETENCE bonus (not morale), so it stacks with morale sources
like Heroism; the performance also grants a +1 MORALE bonus on saving throws
against charm and fear effects, implemented as two descriptor-conditional
will modifiers (fear / mind_affecting) that dedupe via morale stacking.
Simplifications (documented): flat +1 (no scaling per four bard levels) and
mind_affecting stands in for the missing charm descriptor.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 20:35:01 +02:00
ctan 0fefb9cdd2 fix(rules): apply the -2 spell combat penalty to melee attacks
PF1e CRB (Magus Spell Combat): as a full-round action the magus casts a
standard-action spell and makes all his melee attacks at -2. The penalty is
threaded through the full-attack chain (_execute_attack -> _full_attack ->
_full_attack_weapon -> _attack/_resolve_swing -> resolve_attack bonus_override)
so it applies to every attack roll and crit confirmation. Simplification: the
arcane pool cost is still not modeled.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 20:30:19 +02:00
ctan e321c67779 fix(rules): allow a Will save to halve channeled energy damage
PF1e CRB (Channel Energy): creatures damaged by channeled energy get a Will
save (DC 10 + half the channeler's level + her Charisma modifier) for half
damage (floor). Harm mode only; heal mode unchanged. Charisma is used RAW
even for casters whose spellcasting ability differs (channeling is a
Charisma class feature).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 20:24:51 +02:00
ctan 6f784d3369 fix(rules): require a ranged touch attack for ray spells
PF1e CRB (Magic - Ranged Touch Attacks): spells delivered by ray require a
ranged touch attack (BAB + Dex vs touch AC, no range increment) to hit a
hostile target; a miss wastes the slot. SpellSpec gains requires_ranged_touch,
set on the 8 SRD-confirmed rays (Ray of Frost, Ray of Enfeeblement, Scorching
Ray, Acid Arrow, Acid Splash, Disrupt Undead, Shard of Chaos, Spear of Purity).
Magic Missile and area spells are unaffected. Simplification (documented): no
size modifier / Weapon Finesse on the roll.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 20:19:03 +02:00
ctan 24af1687ab fix(spells): backfill casting times for all imported spells 2026-09-11 15:01:42 +02:00
ctan b3398c16ce feat(observation): expose spell cast time in the snapshot
The per-spell Observation dict gains the additive cast_time key: the
registry spell's budget code on a hit, "standard" on a miss (mirroring
the engine's resolver fallback), so an LLM policy can schedule casts
against the action budget it sees reported back.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 14:13:13 +02:00
ctan c5426ffe34 feat(spells): honor spell cast time in the action budget
SpellSpec gains a cast_time field (seven budget codes; the JSON loader
also accepts RAW phrasings like "1 round" or "1 minute"). The pure
budget filters take a CastTimeResolver so a cast_spell occupies the slot
its casting time maps to: standard->standard, swift->swift (competing
with deed/inspire courage and dropped by a consumed immediate),
immediate/free->free, full_round and one_round->full_round (one_round
clamped: resolved end-of-turn, documented approximation), long->dropped
before the budget filter with the new downgraded:cast_time reason. The
engine binds its registry lookup via _cast_time_of (unknown names fall
back to standard, keeping transcripts byte-identical);
_choose_spell_action never selects long casts; the staggered filter now
drops full-round casts and spell_combat via the effective slot
(_FULL_ROUND_KINDS becomes dead and is deleted). Summon Monster I/II are
the only data spells marked 1 round. Metamagic full-round casting for
spontaneous casters is out of scope (future hook documented in
_cast_time_of).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 14:09:58 +02:00
ctan 0997c8d1b5 feat(rules): allow a move action in place of a standard action
PF1e CRB (Actions in Combat): a move action can be taken instead of the
standard one. The budget filter now converts a second move action onto
the standard slot when the move slot is spent and the standard is still
free, so (move, stand_up) and (move, move) are legal; a third move (or
any further standard) is still surplus. The greedy walk is expressed
with a spent-slot map to stay inside the C901 complexity ceiling.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 13:53:45 +02:00
ctan e44ec75f2b fix(rules): classify run as a full-round action
PF1e CRB: running is a full-round action, not a move action. The budget
filter now classifies run as full-round (consumes the standard AND the
move slot), _FULL_ROUND_KINDS includes it for the staggered filter, and
the nauseated filter drops it (run is no longer move-like). The
moved_this_turn/running flags are unchanged; the _execute_run
moved_this_turn early return stays as defense in depth. default_policy
never emits run, so the golden transcripts are untouched.

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 13:48:58 +02:00
ctan 9c6f5e277e feat(cli): render --transcript output as a table 2026-09-11 13:02:31 +02:00
ctan d6c8f88f77 feat(cli): add --simple-monster-death option to kill monsters at hp <= 0 2026-09-11 12:48:43 +02:00
ctan b7c80babfd fix(rules): enforce the action-economy budget on policy output
Apply the PF1e per-turn action budget (CRB Actions in Combat: one
standard + one move, or one full-round in place of both, plus one
swift) unconditionally to every sequence a policy returns, not only
the immediate/staggered/nauseated specializations.

- _action_economy_filter: greedy in-order walk keeping the earliest
  legal prefix-subsequence; a full-round action is only kept while
  both the standard and move slots are still free.
- Exhaustive kind->slot classification (standard / move / swift /
  full_round / free-conditional) over the Action.kind Literal,
  documented and locked by tests.
- Surplus actions dropped and reported as
  ActionResult(ok=False, reason="downgraded:action_economy") via a
  multiset drop diff (reports duplicate-kind surplus the set-based
  _dropped_kinds cannot).
- Composition order: general budget first, then the existing
  immediate/staggered/nauseated filters (unchanged).
- default_policy untouched and its turns verified legal by the
  validator; golden transcripts byte-identical.
2026-09-11 12:26:49 +02:00
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