Commit Graph

415 Commits

Author SHA1 Message Date
ctan 2b5b39a9f7 fix(rules): jeanne + esha mount hooves 1d4 (SRD light horse) marked natural, add Jeanne's sheet Bite as secondary 2026-09-16 22:06:59 +02:00
ctan b9477a2ba1 fix(rules): ierlieth - bite damage_bonus -1 per sheet STR, add secondary Tail (stored +4, runtime -5 = sheet -1) 2026-09-16 22:02:10 +02:00
ctan 1c94d48803 fix(rules): harvie - Wis 14 per sheet, Talons primary +2 1d4 (2x), Bite secondary +2 with runtime -5 2026-09-16 21:59:29 +02:00
ctan feaab5ee41 fix(rules): celestial wolf - SRD bestiary wolf baseline (hp 13, Con 15, touch 12, Fort +5, Will +1, bite +2) + celestial DR 5/magic, ER 5, SR 6 2026-09-16 21:55:09 +02:00
ctan b3a7c3787d fix(rules): add alchemical/inherent/resistance/size bonus types (UM Table 2-7) as non-stacking 2026-09-16 21:48:40 +02:00
ctan ae037c984f fix(rules): minimum damage - a landed hit reduced below 1 by DR/energy resistance deals 1 (CRB Damage - Minimum Damage, nonlethal approximated as normal) 2026-09-16 21:46:04 +02:00
ctan 1c2663d21c fix(rules): non-stacking types keep best bonus and worst penalty separately (CRB Common Terms - Stacking) 2026-09-16 21:40:30 +02:00
ctan fa63829385 fix(rules): trait and racial are non-stacking bonus types (APG Traits, CRB Combining Magic Effects) 2026-09-16 21:37:03 +02:00
ctan cc9fba3b52 fix(rules): helpless Dex-0 AC adds exactly -5 on flat-footed base; touch base strips full Dex mod when helpless (no negative-Dex double count) 2026-09-16 21:32:19 +02:00
ctan e0bf484394 fix(rules): firing into melee exemption measured target-to-nearest-ally, not attacker-to-target (SRD Shooting into a Melee) 2026-09-16 21:24:48 +02:00
ctan e6f7b1988b docs(rules): conformance journal - round 2 found 4 data/conditions/feats/maneuvers fixes and 16 engine commits
Round-2 audits (feats/class abilities, combat maneuvers, conditions,
spell data) surfaced 6 High / ~28 Med / ~45 Low findings. This commit
records them with a per-finding disposition (fixed / out of scope /
assumed residual) plus the 17 fix commits (b03a61d..e89ea42) and the
single justified golden update (Weapon Focus now applies).
2026-09-16 20:45:51 +02:00
ctan e89ea42168 fix(rules): README/abilities wording - grapple applies only grappled (pin out of scope), pinned/nauseated cant_attack, confused no-AoO, Weapon Focus group matching 2026-09-16 20:32:15 +02:00
ctan 3a5948c35c fix(rules): CMD never adds Dex on top of a Tiny Dex-substituted pre-baked CMB 2026-09-16 20:25:34 +02:00
ctan 0cccea2fa1 fix(rules): combat maneuvers auto-succeed (natural 20) vs incapacitated targets 2026-09-16 20:21:41 +02:00
ctan 06da519fc6 fix(rules): dirty trick RAW list extended to shaken and sickened (APG), with runtime validation 2026-09-16 20:16:35 +02:00
ctan 2b5920c85c fix(rules): disarm drops the wielded weapon (weapon_dropped) instead of a synthetic -4 penalty 2026-09-16 20:08:10 +02:00
ctan fdc9127483 fix(rules): Toughness is +3 hp below 4 Hit Dice (max(3, level)) 2026-09-16 19:58:30 +02:00
ctan 350844fc68 fix(rules): Up Close and Deadly deed requires a firearm attack (Ultimate Combat) 2026-09-16 19:55:53 +02:00
ctan 2807f719ca fix(rules): any concealment denies sneak attack (SRD rogue: 'a creature with concealment') 2026-09-16 19:50:40 +02:00
ctan 4d45409941 fix(rules): Weapon Focus filter matches weapon names case-insensitively (substring)
The filter 'Pistol' now matches 'Braises d'Automne (pistolet ?)' via
case-insensitive substring matching, so the feat is no longer dead.
Golden test_tammara_vs_goblin updated: round-4 pistol shot gains the +1
feat bonus (total 15 -> 16 vs AC 16), turning the MISS into a lethal HIT
(17 damage, goblin 6 -> -11); the goblin dies on round 4 so the old
rounds 4-10 exchange and the draw are gone.
2026-09-16 19:45:56 +02:00
ctan f31cc88124 fix(rules): pinned creatures cannot attack (definition corrected; apply-gap documented) 2026-09-16 19:38:49 +02:00
ctan cf8a07311d fix(rules): grappled/pinned -2 applies to non-grapple/escape combat maneuver checks 2026-09-16 19:33:38 +02:00
ctan 68a03a34f6 fix(rules): confused creatures make no attacks of opportunity (simplification documented) 2026-09-16 19:30:08 +02:00
ctan def8533197 fix(rules): nauseated creatures are denied attacks of opportunity 2026-09-16 19:25:35 +02:00
ctan 6220edcfd7 fix(rules): fatigued -2 Dex now costs -1 AC and -1 Reflex 2026-09-16 19:22:00 +02:00
ctan bfe0b767a9 fix(rules): exhausted -6 Dex now costs -3 AC and -3 Reflex 2026-09-16 19:20:00 +02:00
ctan 317f91a07f fix(rules): entangled casting needs DC 15+level concentration; add -4 dex_skills 2026-09-16 19:17:10 +02:00
ctan b03a61d8ab fix(spells): correct SRD levels, saves, schools, ranges and SR flags (11 spells)
Verified field-by-field against the SRD knowledge base (pathfinder-1e-rules,
spells/):
- levels: abundant_ammunition 0->1, comprehend_languages 0->1, deja_vu 0->1,
  create_water 1->0, enhance_water 2->1
- saves: blindness_deafness will->fort, enhance_water none->fort
- spell resistance: summon_monster_i/ii declared false (SRD RM: no)
- schools: arcane_mark ->universal, color_spray ->illusion,
  dancing_lights ->evocation, deja_vu ->enchantment,
  enhance_water ->transmutation, abundant_ammunition ->conjuration
- ranges: deja_vu personal->medium, dancing_lights close->medium,
  enhance_water personal->touch, create_water medium->close

Registry pins in test_spell_registry_batch1.py updated to match; descriptions
that stated the old values reworded consistently.
2026-09-16 19:04:14 +02:00
ctan 99e9203d8c docs(rules): add the SRD conformance audit journal
Consolidates the round-1 audit: 15 fixed divergences (one commit each,
312f9fd..ea60b2e), the deliberate out-of-scope rules, the assumed
residuals, and the verified-conformant guard rails. Linked from README.
2026-09-16 18:10:05 +02:00
ctan ea60b2e4b3 docs(rules): README - undead death, pass-through movement, squeezing limit
- Paragraphe « Mourir et stabilisation » : les morts-vivants et les
  constructeurs sont detruits des hp <= 0 (SRD traits Undead/Construct :
  'immediately destroyed when reduced to 0 hit points or fewer'), sans
  bande d'invalidite ni agonie - conformance deja codee (59caf67).
- Paragraphe « Mouvement » : la traverse des ennuis a 3 categories de
  taille d'ecart avec AoO d'entree (SRD 'Moving Through a Square' : 'Any
  creature can move through a square occupied by a creature three size
  categories larger than itself [...] provoke attacks of opportunity
  from those creatures' ; 'You can't end your movement in the same
  square as another creature unless it is helpless') ; la mention
  'les ennemis bloquent toujours le chemin' est corrigee (blocage total
  seulement a 0-2 categories).
- « Limites hors perimetre » : nouvelle entree squeeze (largeur de
  corridor vs taille non modelisee), entree des approximations du
  pass-through (Acrobatics Tumbling et la clause 'counts as 2 squares'
  des tres grosses creatures inertes non modelisees), et entree de
  l'echec d'incantation du sourd (tache 11, differe).
- « Attaques multiples par action » : verifie conforme aux armes
  naturelles apres e5691bd (pas d'iteratifs, secondaires a -5,
  Multiattack -2) - aucune modification necessaire.
- Compteur de tests de la section « Developpement » : 1408 -> 1491.
2026-09-16 15:53:46 +02:00
ctan cad35e2e13 fix(rules): cure spells damage undead instead of healing
SRD Cure Light Wounds: 'Since undead are powered by negative energy,
this spell deals damage to them instead of curing their wounds. An
undead creature can apply spell resistance, and can attempt a Will save
to take half damage.'

- _apply_heal_effect takes the rolled save; an undead target takes the
  rolled healing as damage (halved on a successful save; SR already
  resolved upstream, DR never applies to spell energy, no bleeding stop
  or stabilization). Living targets are bit-identical.
- cure_light/cure_moderate descriptions fixed: the SRD save line is
  'Will half (harmless)', not 'negates'.
- Deafened 20% verbal spell failure DEFERRED to the README « Limites
  hors perimetre »: SpellSpec carries no components data (would need a
  verbal flag on all 108 spells) and sound-based Perception is not
  modeled either.
2026-09-16 15:48:36 +02:00
ctan 3e19d2974e feat(rules): SRD Mage Armor spell + non-stacking armor bonus type
SRD spells/mage-armor.md: 'An invisible but tangible field of force
surrounds the subject of a mage armor spell, providing a +4 armor bonus
to AC' - 'School conjuration (creation) [force]', level 1, 'Casting Time
1 standard action', 'Range touch', 'Duration 1 hour/level (D)',
'Spell Resistance no'.

- data/spells/mage_armor.json: buff +4 armor to AC, 600 rounds (1
  hour/level modeled fixed, the shield.json convention). Description
  documents the known simplification: the engine bakes worn armor into
  the AC total and cannot see it, so the armor bonus stacks additively
  with that baked value (two mage armors still do not stack).
- effects.py: 'armor' added to BonusType; absent from _STACKING_TYPES it
  behaves as a keep-the-highest type (verified by resolve_modifiers
  tests).
- Spell-count pins 107 -> 108 (test_runner_spells, batch3, test_spells)
  and the README references.
- Golden transcript untouched (buff branch only fires when no offensive
  spell is selected; the lich still opens with Magic Missile).
2026-09-16 15:36:43 +02:00
ctan 881f2ef9b2 fix(rules): lich data conforms to the KB stat block
Verified against the KB (bestiary/lich.md, Bestiary Lich CR 12):
- bab 8 -> 5 ('Base Atk +5')
- Paralyzing Touch attack_bonus 10 -> 5 ('Melee touch +5 (1d8+5 plus
  paralyzing touch)') and marked natural: true (template: 'A lich has a
  touch attack that it can use once per round as a natural weapon [...]
  treated as a primary natural weapon' - no BAB iteratives per e5691bd)
- damage_bonus 5 -> 0: the +5 (1 point per 2 HD) is already in the
  1d8+5 formula and _resolve_swing adds damage_bonus on top (double
  counting: the old data dealt 1d8+10)
- ac 25/14/23 -> 23/14/21 ('AC 23, touch 14, flat-footed 21'; touch was
  already correct)
- saves 10/10/14 -> 6/7/11 ('Fort +6, Ref +7, Will +11')
- con_score 14 -> 10: 'Being undead, a lich has no Constitution score'
  (Con '—'); the int-only schema encodes 'no score' as the neutral 10
  (modifier +0), inert anyway since undead are destroyed at hp <= 0.
Left as intentional engine tuning (residual divergences, documented in
the final report): hp 100 vs 111, Str 12 vs 10, Int 20 vs 22,
initiative +6 vs +2, fear aura 30 ft/DC 22 vs 60 ft/DC 18.
Golden transcript untouched (the lich wins via Magic Missile before any
weapon swing; MM DC depends on Int, unchanged). Pinned a new
test_json_matches_srd_stat_block.
2026-09-16 15:22:10 +02:00
ctan 9a16ca0388 fix(rules): pass through 3+ size categories apart, entry provokes AoO
SRD Moving Through a Square: 'Any creature can move through a square
occupied by a creature three size categories larger than itself. A big
creature can move through a square occupied by a creature three size
categories smaller than it is. Creatures moving through squares occupied
by other creatures provoke attacks of opportunity from those creatures.'

- _can_pass_through: size ranks differ by >= 3 opens the through-gate
  (_blocked_hostile) while _blocked/_cells_free keep the end-gate
  ('You can't end your movement in the same square as another creature
  unless it is helpless').
- _check_enter_aoo wired into _walk_steps (move, explicit-destination
  move, and run all commit steps there): the occupant strikes once per
  fresh entry, capped by the per-round allowance; a dropped mover stops
  the walk. S4c co-occupiable pairs (Tiny-or-smaller) keep no-AoO sharing.
- _has_free_descent generalized to a descending BFS so a walk can cross
  a multi-cell Huge+ creature (one-step lookahead dead-ended inside).
- Golden transcript untouched (all 1480 tests green).
2026-09-16 15:10:21 +02:00
ctan 437507ee2c conformance: block 5-foot step on difficult terrain (SRD Take 5-Foot Step)
A 5-foot step is hampered by difficult terrain: stepping out of OR into a
difficult cell is blocked. _step5 (auto-directed) logs 'wait (cannot step)';
_step5_to (explicit destination) rejects with unreachable_destination.
Engine-level tests cover destination, auto-path, and origin cases.
2026-09-16 13:47:10 +02:00
ctan 7fc7a1d6ec fix(combat): Block run action when cant_charge flag is set
SRD Conditions Fatigued: A fatigued character can neither run nor
charge. _execute_charge already checked cant_charge; _execute_run did
not -- a fatigued/exhausted/entangled combatant could still run.

Added cant_charge guard to _execute_run (mirrors _execute_charge),
logging wait (cannot run) and returning ok. Two engine-level tests:
fatigued cannot run, exhausted cannot run.
2026-09-16 13:28:17 +02:00
ctan 2cc2ba1907 fix(combat): Thread 5-10-5 diagonal parity through all budget-consuming paths
The greedy move/run/flee/charge descent helpers hardcoded diagonal parity 0,
letting a speed-30 walker cover 6 diagonal cells (cell-count budget). Under
5-10-5 (SRD Movement, Tactical) the same walker covers only 4 cells
(1+2+1+2=6 squares).

Threading the running diagonal count:
- _best_move_step / _best_flee_step: accept diagonals_used param
- _descend_path / _run_line_path / _flee_path: track and pass parity
- _charge_end: use _path_cost_5105() instead of len(path) for max check
- _path_cost_5105: new helper; computes total 5-10-5 cost of any path

Updated golden paths in test_aoo_limit_one_per_round (5-cell path now
takes an orthogonal detour to avoid expensive 2nd diagonal), test_run_*
 (speed-30 diagonal move stops at 4 cells), and staggered_withdraw
(single-speed diagonal withdraw stops at 4 cells).
2026-09-16 13:15:49 +02:00
ctan e5691bdb6f fix(rules): natural weapons get no BAB iteratives, secondary attacks at -5
PF1e (CRB "Natural Attacks"):
- Natural weapons (natural=true) get exactly one swing per full attack,
  regardless of BAB (no BAB iteratives for natural attacks).
- Secondary natural attacks (secondary=true) attack at BAB -5; the
  Multiattack feat reduces this to -2.
- Manifested in combat.py (_full_attack_weapon, _resolve_swing),
  models.py (AttackSpec.natural/secondary), 6 data JSONs, 4 new tests,
  and README documentation.

1461 passed, ruff clean, basedpyright 0 errors.
2026-09-16 12:25:30 +02:00
ctan 59caf67909 fix(rules): undead and constructs are destroyed at hp <= 0
SRD Undead/Construct traits: 'immediately destroyed when reduced to 0 hit
points or fewer' — undead and constructs have no Constitution score, so
the PF1e hp <= -CON death rule does not apply to them.

- combat.py: CombatantState.death_threshold returns 0 for creature_type
  undead/construct (in addition to the opt-in dies_at_zero flag)
- tests: TestUndeadDeathThreshold pins undead dead at 0 and below, living
  keeps the disabled band at 0 and the dying band down to -CON
- golden unchanged: tammara-vs-lich has the lich winning in 1 round
  without taking damage
2026-09-16 11:45:23 +02:00
ctan 5e3505dc8c fix(rules): convert bane to a 50-ft enemies-only burst
SRD spells/bane.md: enemies within a 50-ft burst centered on the caster
take a -1 penalty on attack rolls and a -1 penalty on saving throws
against fear effects; Will negates. The -1 penalty is approximated with
the shaken condition (-2), matching the existing Doom approximation.

- bane.json: single-target condition -> AreaEffect burst radius_ft 50,
  targets "enemies", ConditionEffect shaken 10 rounds save_negates
  (range/save kept as medium/will for the registry pin)
- combat.py: _metamagic_transform_sub now copies AreaEffect.targets so
  an Extend Spell on an area spell preserves the allies/enemies filter
- tests: TestBaneEnemiesOnly (enemy in burst debuffed, allies/caster
  unaffected, successful Will save negates)
2026-09-16 11:37:23 +02:00
ctan e43066e0ef feat(rules): restrict bless to allies in a 50-ft burst
SRD spells/bless.md: 'The caster and all allies within a 50-ft. burst,
centered on the caster'. Add an AreaEffect.targets filter (all/allies/
enemies) applied where area creatures are collected; bless.json now
declares radius_ft 50 and targets allies (the caster included).
2026-09-16 11:14:46 +02:00
ctan 312f9fd6b8 fix(rules): conform aid, bladed dash, doom, bulls/bears strength to the SRD 2026-09-13 18:56:17 +02:00
ctan 6c4bdc5d14 docs(rules): document the bladed dash approximation
Bladed Dash's description now matches the SRD (standard action, move up
to 30 ft in a straight line without provoking attacks of opportunity,
single melee attack at highest BAB against any creature adjacent along
the path) and the README's spell-limits section records the engine
approximation: a +30 ft speed buff for 1 round, with the free melee
attack, the no-AoO movement, the straight-line constraint and the
circumstance attack bonus left unmodeled.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 17:43:09 +02:00
ctan 0c141414a9 feat(rules): conform bulls strength, bears endurance and shield to the SRD
Bull's Strength now grants the SRD +4 enhancement bonus to Strength as
+2 melee attack, +2 melee damage and +2 CMB (three modifiers instead of
the previous +1/+1 approximation). Shield now uses a real shield bonus
type (+4 AC) instead of approximating it as deflection: the engine's
BonusType literal and stacking docs gain the shield type, which stays
out of the CMD bonus list (SRD Combat Maneuver Defense). Bear's
Endurance keeps its +2 Fortitude / +2 hp per HD and its description now
cites the SRD and flags the hp modifier as informational. Golden
transcripts updated: the buff log reports 3 mods and Tammara's pistol
attacks gain the extra +1 enhancement (d20=10+9-6=15).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 17:39:52 +02:00
ctan b84238792c feat(rules): scale healing spells with caster level
PF1e (SRD spells/cure-light-wounds.md): 'cures 1d8 points of damage +1
point per caster level (maximum +5)'; (spells/cure-moderate-wounds.md):
'cures 2d8 points of damage +1 point per caster level (maximum +10)'.
A HealEffect now carries the same DamageFlatScaling spec as damage
effects (S1b) and resolves it through effective_formula(caster_level)
at cast time, so the flat bonus grows with the caster and stops at the
stated cap. The engine passes the caster's level into _apply_heal_effect
(required keyword: no silent level-0 under-heal); Maximize Spell
maximizes the scaled formula. Cure Light/Moderate Wounds ship the SRD
values; inflict_light_wounds already carried its scaling_flat.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 17:19:58 +02:00
ctan a2a80b8690 feat(rules): add firearm misfire and the quick clear deed
Ultimate Combat Firearm Rules: a firearm attack rolling a natural 1 gains the
broken condition (weapon_broken, logged MISFIRE). Quick Clear now actually
clears the first misfired held firearm for 1 grit (reported no-op without
grit), and occupies the standard budget slot via _effective_slot; the
swift-consumed filter is slot-based so the standard deed survives.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 16:41:40 +02:00
ctan b579cb4934 fix(rules): give Tiny-or-smaller creatures zero reach and square sharing 2026-09-13 15:35:23 +02:00
ctan 7fb7ec55dd fix(rules): update initiative when a readied action fires
SRD Ready - Initiative Consequences of Readying: 'your initiative result becomes the count on which you took the readied action' and you 'act immediately ahead of the character whose action triggered your readied action'. _check_readied fired the payload off-turn but never touched the readier's initiative. It now calls _ready_shift_initiative, which rewrites the readier's _initiative_keys entry (trigger total + 0.5, keeping its mod and prior index) using the same tuple shape _execute_delay writes, so _build_round_order holds the new position for all following rounds. Deterministic: no RNG consumed, current round order untouched.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 14:54:15 +02:00
ctan 6702efc652 fix(rules): constrain a run to a straight line
SRD Full-Round Actions - Run: 'you can move up to four times your speed in a straight line'. _execute_run reused the Dijkstra move machinery, so it turned corners around walls. It now walks the straight Bresenham line (_run_line_path) and stops at the first obstacle (impassable terrain, difficult terrain, or a blocking creature), mirroring the charge clear-path logic. Keeps the 4x budget, step-by-step AoO provocation, moved_this_turn and the running/Dex-loss flag; a destination the straight line cannot honour is rejected (unreachable_destination).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 14:48:25 +02:00
ctan f5dc9da0c4 feat(rules): resolve one-round casts on the following turn
SRD Casting Time: a spell that takes 1 round is a full-round action
that comes into effect just before the caster's turn in the next round;
the caster then acts normally. _cast_spell now spends the slot and runs
the start-of-casting gates, then records a PendingCast (spell, target,
center, metamagic, slot level, hp at cast); delivery (touch attack, SR,
saves, effects) resolves at the start of the caster's next turn, before
the policy runs. Net hp lost in between forces the injured-while-casting
concentration check (DC 10 + damage + spell level, no Combat Casting) or
the spell fizzles; a caster knocked out of the fight loses it. Exposed
in the observation via pending_cast. Bounded model documented: net-hp
measurement instead of a per-damage-site hook; long (1 minute+) casting
stays out of scope.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 13:39:44 +02:00