Commit Graph

385 Commits

Author SHA1 Message Date
ctan 4665a1c245 fix(rules): harmless spells on willing targets forgo the save roll - SpellSpec gains a data-driven 'harmless' flag set on the 29 SRD '(harmless)' spells (verified one by one against the KB); a same-side/self target consumes NO save die (SRD saving-throw.md: '(harmless) ... can attempt a saving throw if it desires' + 'Voluntarily Giving up a Saving Throw'), while hostile targets and undead receiving a cure (harmful to them, Will half for undead damage) keep the roll; snapshot spell summaries expose 'harmless'; golden transcript unchanged (no harmless-save cast in it); +8 tests, 3 fixtures re-pinned for the removed die 2026-09-19 22:27:46 +02:00
ctan ea835293a0 fix(rules): run is bounded by Constitution endurance and the Run feat - after a number of consecutive run rounds equal to the CON score each further round requires a Constitution check (DC 10 +1 per prior check, no auto nat-1/20, undead/constructs exempt with no CON score); failure stops the run and forces the SRD 1-minute (10-round) rest; the Run feat grants +4 on the check and keeps the Dex bonus to AC while running (SRD Full-Round Actions - Run); heavy-armor x3 speed clause documented out-of-scope (no armor field); +5 tests 2026-09-19 22:02:36 +02:00
ctan 28bc624d1e docs(rules): conformance journal round 8 - adversarial verification and 8 new fixes
An independent pass confirmed the sampled High/Med fixes are genuinely
implemented, then found previously-undocumented divergences in the default
policy (spell combat without a slot, out-of-range heal wasting the turn,
wasted grit on a melee deed, fixed hex range), dice (wrong exception for
d1), the channel-heal trigger, and the foundry migration tool. All fixed;
corrects the stale 'goblin dies round 4' note (now round 5) and records the
Bless centering residual.
2026-09-19 19:15:27 +02:00
ctan 1b43f4eef1 fix(docs): README states data/pcs/*.json are the canonical PC sheets and foundry.py is a one-shot incomplete migration tool - re-running it and regenerating the JSON would regress the manual/SRD conformance fixes (natural/secondary/firearm flags, Jeanne hooves reach 10, Ierlieth fly 40, Misty bite 1d6, feats beyond its 8-entry map, spell_slots/resources/casting_ability); refresh dev test count 1634 -> 1647 2026-09-19 19:10:38 +02:00
ctan 238e5f201e fix(combat): witch hex trigger uses the SRD close range scaled by caster level (25 ft + 5 ft per 2 levels, extracted as spells.close_range_ft and shared with spell_range_ft) instead of the fixed _HEX_RANGE_FT 25 - a level-5 witch reaches 35 ft; + scaling regression test 2026-09-19 19:07:41 +02:00
ctan 6f01a8ebdd fix(combat): _find_ally_needing_heal counts only living allies (SRD Channel Energy: heal mode heals living creatures, positive energy never heals undead) - an undead ally below half triggered heal mode, spending the channel to heal nobody; + trigger regression test 2026-09-19 19:01:03 +02:00
ctan c23fc366e4 fix(combat): _find_ally_needing_heal uses hp*2 < hp_max (strictly below half) - the floored hp < hp_max//2 was off by one for odd hp_max (5 -> threshold 2, so 2/5 hp counted healthy); + threshold regression test 2026-09-19 18:58:03 +02:00
ctan f93c55b3fb fix(combat): _swift_bonus_actions emits up_close_and_deadly only when the turn will actually fire a firearm attack within 30 ft - the deed burns 1 grit at execution and its +1d6 rides only a firearm attack, so emitting it alongside a melee swing or a spell cast wasted grit for a lost bonus; + 2 policy tests, deed test now uses a firearm-only tammara 2026-09-19 18:55:17 +02:00
ctan e8b7a73d1f fix(dice): parse_dice validates sides >= _MIN_SIDES so d1/1d1 raise DiceParseError instead of leaking the model validator's pydantic ValidationError; drop the dead not-sides_str guard (regex \\d+ already guarantees non-empty) + 3 rejected-notation cases (d1, 1d1, 2D6) 2026-09-19 18:51:38 +02:00
ctan bdf15aa8fe fix(combat): _choose_spell_action heal branch gates on spell range + line of effect - a touch-range Cure on a distant ally was rejected by _cast_spell (OUT OF RANGE / NO LINE OF EFFECT) and wasted the caster's whole turn; extracted the offensive branch's reach check as _spell_reaches and reused it (the duplicated below-half check in the branch was a no-op copy of _find_ally_needing_heal, dropped) + 3 policy tests 2026-09-19 18:48:58 +02:00
ctan 31d47aee79 fix(combat): _spell_combat_actions gates on _has_slot - with exhausted slots the cast is rejected (NO SPELL SLOT) and spell_combat resolves as a bare full attack at -2, strictly worse than the plain full attack; the policy now falls through (mirrors _choose_spell_action's slot gate) + 2 regression tests 2026-09-19 18:41:46 +02:00
ctan 657f48474c docs(rules): conformance journal round 7 - spell registry closed (108/108) and subsystem fixes
Records the 12 fixes of the final pass (engine Lows, LOS area templates,
runner/metrics/observation/CLI): dying creatures occupy their square,
blinded cannot charge/run/withdraw, cone/line aim and caster footprint,
square_size_ft validation, channel-heal no longer counted as damage dealt,
summon aggregation no longer KeyErrors, non-attack damage counted, README
examples refreshed, ACTION_RESULT_REASONS completed, zone-letter validation.
The remaining 35 spells were field-verified against the KB with zero
outcome divergences, closing the spell coverage gap.
2026-09-19 17:21:54 +02:00
ctan 9a087a444b fix(docs): refresh stale README example numbers against the live engine - Quick start (3 goblins vs 2 orcs, seed 1): 96.2%/962 band [94.4%,98.0%], monsters 3.6%/36, 2 draws, 6.6 rounds; Exemple verifie (1 goblin vs 2 orcs): 33.0% band [28.5%,37.5%], 6 draws, 7.1 rounds, prose adjusted to the new distribution; dev test count 1596 -> 1634 2026-09-19 17:18:31 +02:00
ctan ba5eb4c95f fix(map): load_map rejects two deployment sides bound to the same zone letter (build_states resets its placed set per side, so shared letters deployed camps on top of each other) - fail-fast MapValidationError + README rule, reject/accept tests 2026-09-19 17:12:42 +02:00
ctan 2ee19d34e4 fix(docs): correct the Observation 'frozen' claim - the dataclass is frozen at the top level but the inner per-combatant/pair dicts are mutable copies; wording softened in the docstring and README (deep-freeze rejected: it would change the LLM contract and require touching pinned observation tests; copies are never read back by the engine, so policy mutation is already inert) 2026-09-19 17:09:08 +02:00
ctan 9459adee02 fix(actions): add downgraded:immediate_swift to ACTION_RESULT_REASONS (emitted since conformance L10 but absent from the published vocabulary); grep showed no other emitted-but-missing ActionResult reason (no_charge_path is a ChargeResult field) - drift guard test pins every _REASON_* constant against the frozenset 2026-09-19 17:06:30 +02:00
ctan 0ad627c97c fix(stats): non-attack damage now lands in damage_taken - bleed ticks, recurring spell damage (Acid Arrow) and cure-spells-damage-undead (SRD Cure Light Wounds negative-energy hit, attributed to the caster's damage_dealt) were invisible to attrition while attacks were counted; transcripts and RNG untouched 2026-09-19 17:02:21 +02:00
ctan 7fefce4653 fix(runner): aggregation creates per-id accumulators on demand so summoned creature ids in result.stats no longer KeyError run_encounter (totals were seeded from the deployed roster only); duplicate summons of one creature id share a stats slot - noted as a limitation, pinned by a summon-encounter report test 2026-09-19 16:56:48 +02:00
ctan 2e2873f331 fix(stats): channel-energy heal is no longer counted as damage_dealt (attrition tracks damage only, README 'dégâts infligés/subis'; consistent with spell HealEffect which is counted nowhere) - harm mode still records damage_dealt/damage_taken, pinned by new tests 2026-09-19 16:52:04 +02:00
ctan 24e208ea41 fix(rules): load_map rejects any square_size_ft other than 5 (SRD Measuring Distance: one grid square is 5 feet; the engine's 5-10-5 geometry, step costs and ranges are hard-coded to that scale) - a map declaring 10-ft squares was loaded silently and mis-scaled; now a fail-fast MapValidationError, with reject/accept tests 2026-09-19 16:37:30 +02:00
ctan 1e2de7f89d fix(rules): a cone/line template excludes the caster's whole footprint, not just the anchor cell (SRD aiming-a-spell.md: 'It starts from any corner of your square' - every occupied square is origin space and never inside its own area) - cone_cells/line_cells take caster_cells; combat passes caster.occupied_cells(); 1x1 casters bit-identical; documented simplification: a grazed caster cell still consumes a length slot 2026-09-19 16:34:22 +02:00
ctan 2e468ba619 fix(rules): point-cast cones/lines no longer demand line of effect to the aim point (SRD aiming-a-spell.md: a cone/line 'shoots away from you in the direction you designate' and 'starts from any corner of your square' - the LoE-to-the-space rule binds targets and burst origins only) - _point_target_rejection now requires in-bounds for every area and caster->point LoE only for bursts (and area-less spells, conservatively); template LoE stays enforced by cone_cells/line_cells 2026-09-19 16:29:38 +02:00
ctan 778d6a8620 fix(rules): a blinded creature cannot charge, run, or withdraw (SRD charge.md: 'If you don't have line of sight to the opponent at the start of your turn, you can't charge that opponent'; full-round-actions.md: 'You can't run ... if you can't see where you're going' and 'you can't withdraw from combat if you're blinded') - can_charge gates blinded with no_line_of_effect, _execute_run joins the cant_charge no-op, _withdraw rejects blinded 2026-09-19 16:24:31 +02:00
ctan 792cec140b fix(rules): dying-but-alive creatures still occupy their squares (SRD cover.md: soft cover from 'a square occupied by a creature'; SRD aiming-a-spell.md: a burst 'affects whatever it catches in its area, including creatures that you can't see') - soft-cover interposer sets and area templates filtered on active, so a dying creature's square was ignored; new CombatantState.occupies_space (not dead) gates both; corpses stay excluded (documented); channel energy keeps its active-only filter (pinned) 2026-09-19 16:19:23 +02:00
ctan aa424fa385 docs(rules): conformance journal rounds 5-6 (31 fix commits) and final residual inventory
Rounds 5-6 covered the spell subsystem (metamagic DC, holding the charge,
summon duration, Extend, Combat Casting/entangled, ASF/bonus-spells/dismissal
docs), the 24 feat/racial residuals, the deferred spell effects (mirror image,
bleed, bane, acid arrow, ice slick, owl's wisdom, bear's endurance, aid...)
and the map/LoS area-of-effect findings (line truncation at barriers, cone/
burst line of effect, cover-to-Reflex, adjacent melee cover, charge/run
corner rule, charge footprint terrain). Records the 31 commits
(d0db208..9086038), the deferred/lower-severity residuals, and the audit
coverage gap (oracle quota exhausted).
2026-09-19 15:58:53 +02:00
ctan 9086038bfb fix(rules): charge clear-path tests the whole footprint for difficult terrain (SRD movement-position-and-distance-2.md: 'You can't run or charge across difficult terrain' + 'you can move only as fast as the most difficult terrain you occupy') - _charge_path_clear priced only the anchor cell, so a 2x2 creature with one trailing square in rubble charged freely; the move-descent footprint pricing is deferred (would rewrite the anchor-based Dijkstra cost field) 2026-09-19 15:51:15 +02:00
ctan 5e478bfd27 fix(rules): charge and run walks enforce the diagonal corner rule (SRD measuring-distance.md: 'You can't move diagonally past a corner') - _charge_path_clear and _run_line_path never checked diagonal_allowed, so a charge/run could cut diagonally through a wall corner; both walks now stop/reject a diagonal step whose flanks are not both passable, same predicate the Dijkstra descent uses 2026-09-19 15:44:12 +02:00
ctan 27ce33ba67 fix(rules): adjacent melee cover uses the SRD any-line rule (cover.md: 'any line from any corner of your square to the target's square goes through a wall') - the gate previously applied the ranged best-corner rule to distance-1 melee, systematically under-granting cover; non-adjacent melee (reach) keeps the ranged rule, bonus scoring aligned per branch, README 'pire coin' wording updated 2026-09-19 15:35:56 +02:00
ctan 9f8655da76 fix(rules): README sync for the area-of-effect line-of-effect fixes - line truncation at barriers, cone/burst LoE from the origin, terrain cover Reflex half-bonus (SRD aiming-a-spell.md, cover.md); areas.py architecture entry updated 2026-09-19 14:35:17 +02:00
ctan fb6f58acc1 fix(rules): document the diagonal corner rule as stricter-than-RAW (SRD measuring-distance.md: 'You can't move diagonally past a corner' vs 'You can also move diagonally past other impassable obstacles, such as pits' - RAW blocks only both-flank corners; the engine requires both flanks passable for every diagonal, an accepted conservative approximation because relaxing it would rewrite Dijkstra paths and golden positions) 2026-09-19 14:32:44 +02:00
ctan beb8421600 fix(rules): pin the high-ground +1 as melee-only with the SRD proof (Table 8-5 'Attack Roll Modifiers': 'On higher ground - Melee +1, Ranged +0') - engine verified conformant, no code change 2026-09-19 14:30:46 +02:00
ctan a2ca78fce3 fix(rules): document the blocks_los flag as LINE OF EFFECT semantics (SRD aiming-a-spell.md: 'A line of effect is canceled by a solid barrier. It's like line of sight for ranged weapons, except that it's not blocked by fog, darkness, and other factors that limit normal sight') - vision-only concealment terrain is not modeled; concealment rides conditions (miss_chance), not the map 2026-09-19 14:26:26 +02:00
ctan 8774e51a12 fix(rules): cover grants a Reflex-save bonus against area effects (SRD cover.md 'Cover and Reflex Saves': +2 vs standard cover - half the AC value, so +1 partial/+2 standard/+4 improved; soft cover grants none) - _area_save adds terrain cover//2 from the effect's point of origin (burst center, caster square for cone/line) to Reflex rolls; resolve_save gains an explicit flat_bonus 2026-09-19 14:16:26 +02:00
ctan 948edc1815 fix(rules): bursts cannot affect creatures in total cover from the point of origin (SRD aiming-a-spell.md: a burst "can't affect creatures with total cover from its point of origin (in other words, its effects don't extend around corners)"; Line of Effect: a burst affects only what it has LoE to from its origin) - _area_creatures skips creatures whose occupied cells have no line of effect from the burst center 2026-09-19 14:08:24 +02:00
ctan 389e160627 fix(rules): cones require line of effect from their origin (SRD aiming-a-spell.md: a cone 'affects only an area, creature, or object to which it has line of effect from its origin') - cone_cells drops geometrically-inside cells without corner-to-corner LoE from the caster square; line/cone barrier-cell semantics aligned (both templates include the barrier square the effect strikes, exclude everything behind it) 2026-09-19 14:04:34 +02:00
ctan 5250763c35 fix(rules): line spells stop at a line-of-effect barrier (SRD aiming-a-spell.md: a line 'extends to the limit of its range or until it strikes a barrier that blocks line of effect') - line_cells truncates at the first blocks_los cell, creatures behind a solid wall are never caught 2026-09-19 13:54:25 +02:00
ctan b95533d49f fix(rules): bear's endurance +2 hp per Hit Die via split flat scaling, aid morale scoped to fear-descriptor saves (SRD spells/aid.md), charm person range close per SRD spells/charm-person.md, README synced (29 conditions, acid arrow total cap, mirror image instance-level miss chance) 2026-09-19 13:34:30 +02:00
ctan 35ce41e5f9 fix(rules): owl's wisdom +2 Will enhancement buff (SRD spells/owl-s-wisdom.md); correct and sharpen the deferred-utility docs for guidance, resistance, virtue, stabilize, saving finale, spiritual weapon (verified SRD texts, engine-track reasons, and the cantrip-buff policy degradation that blocks encoding resistance) 2026-09-19 13:27:55 +02:00
ctan 56310b191a fix(rules): mirror image rolled image count with dynamic images/(images+1) concealment miss chance, bleed cantrip gated on the SRD dying-target eligibility, bane -1 fear-scoped penalty via a dedicated bane condition (SRD spells/mirror-image.md, spells/bleed.md, spells/bane.md) 2026-09-19 13:22:19 +02:00
ctan d9b590ebce fix(rules): acid arrow recurring total cap (1+6=7 rounds at 18th, the SRD max counts additional rounds) and ice slick flat scaling 1d6+1/level max +10 (SRD spells/acid-arrow.md, spells/ice-slick.md) 2026-09-19 13:12:36 +02:00
ctan 13e4a2774e fix(rules): cover the feat/trait no-op batch in test_feats (assert the eight documented inert specs, the two traced-noop names, and misty's resistances/bite data encoding) 2026-09-19 12:47:40 +02:00
ctan 2df9dd778c fix(rules): misty Scaled Skin electricity resist 5 and Maw or Claw bite 1d6 (SRD ARG daemon-spawn traits: resistance encoded in resistances, bite upgraded from the sheet's estimated 1d4 to the Medium 1d6 with the discrepancy recorded in notes) 2026-09-19 12:43:38 +02:00
ctan 7262568959 fix(rules): Esha sylph traits and invented names to documented traced no-ops (Whispering Wind/Like the Wind/Breeze-Kissed are real sylph racial traits whose SRD effects the engine cannot resolve - the coded bonuses were fabricated; Fey-touched and Pingre are not PF1e features and move to _NOOP_FEATS; descriptor gating stays covered via local probe specs) 2026-09-19 12:37:32 +02:00
ctan 2dd3fefa1d fix(rules): Larger Than Life / Ambitious / Focused Study to documented traced no-ops (SRD effects gate on Intimidate size, HD-differential Diplomacy, and chosen-skill bonuses the engine never resolves; the flat +1 Intimidate was the wrong effect) 2026-09-19 12:27:07 +02:00
ctan 17c06e18cc fix(rules): document dismissal of (D)-duration spells as out of scope (SRD Cast a Spell: Dismiss a Spell - no dismiss action nor active-spell registry exists in the engine) 2026-09-19 12:06:24 +02:00
ctan 1902877cfe fix(rules): document the spell_slots data convention - JSON slots are final per-encounter budgets, engine computes no bonus spells (evidence: PC sheets carry the base class table without bonus, lich slots match no RAW daily table) 2026-09-19 12:04:30 +02:00
ctan b2739c5747 fix(rules): document arcane spell failure as out of scope (SRD Arcane Spells and Armor - the model has no armor field nor spell components, so ASF has neither source nor filter) 2026-09-19 12:01:40 +02:00
ctan 22724f583f fix(rules): Extend Spell doubles SummonEffect duration (SRD extend-spell: lasts twice as long; summon's 1 round/level is neither instantaneous, concentration nor permanent) 2026-09-19 11:55:30 +02:00
ctan 65a5e24609 fix(rules): Combat Casting's +4 no longer applies to the entangled concentration check (SRD combat-casting: the feat covers only casting defensively or while grappled) 2026-09-19 11:50:53 +02:00
ctan cec3c5f459 fix(rules): summon duration scales with caster level (SRD summon-monster: 1 round/level (D)) - SummonEffect gains duration_rounds_per_level resolved at cast time, summon_monster_i/ii JSON updated 2026-09-19 11:47:34 +02:00