Commit Graph

76 Commits

Author SHA1 Message Date
ctan 08f9af9e3e fix(rules): guidance is a one-shot +1 competence bonus on a single attack roll - SRD spells/guidance.md grants '+1 competence bonus on a SINGLE attack roll, saving throw, or skill check' (duration 1 minute or until discharged); the engine had no spell effect that could write the one-shot attack track (aid_attack_bonus is hardwired to aid-another +2 and expires at the start of the beneficiary's next turn), and a persistent BuffEffect would misrepresent the discharge rule (it would boost every roll for a minute) and would become the first buff spell in the casters' known-spell order, degrading default_policy; a new OneShotEffect type is added (track/value/bonus_type/duration_rounds, parser 'one_shot', SpellEffect union, summary 'one-shot +X type on track'), stored as ActiveOneShot on CombatantState (one_shot_bonuses, decremented with the other duration-tracked effects, exposed in the snapshot), discharged by the FIRST roll of the encoded track via _discharge_one_shot (best value wins, each casting is its own charge) and folded into the resolve_modifiers pool so bonus-type stacking applies; the attack track rides the attack roll only, NOT the crit-confirm roll (the SRD grants it to a SINGLE attack roll, so unlike aid another the confirmation is untouched - _attack_roll_modifiers returns (pool_mod, attack_roll_mod) and resolve_attack uses the latter for the total while the confirm keeps the former); the save and skill tracks are wired in the mechanism (resolve_save folds a save-track shot into save_mods, resolve_skill discharges a skill-track shot into _skill_static_bonus via extra_mods) but guidance.json encodes the attack track only - the SRD 'attack OR save OR skill' choice has no declaration channel in the engine, so the attack-roll variant is encoded (documented); _apply_targeted_spell_effect dispatches OneShotEffect to _apply_one_shot_effect (a successful save negates it, 'one-shot negated'); guidance.json now carries the effect (1 minute = 10 rounds) and default_policy never casts it (cantrips have no spell slot and the one-shot type is not a buff/heal/offensive pick); _spell_effect_summary split into _damage_effect_summary/_non_damage_effect_summary to stay under the PLR0911 return cap; test_spell_registry_batch2._EFFECT_TYPES gains OneShotEffect; +14 tests (test_one_shot_guidance.py: cast-and-discharge on the next attack, not on the crit-confirm roll, save/skill tracks, stacking by bonus type, expiry at 10 rounds, save negation, snapshot exposure, no auto-cast by default_policy); golden untouched; 1705 tests green 2026-09-20 09:53:53 +02:00
ctan 4f8a131ba0 fix(rules): lich stat block aligned to the KB entry - bestiary/lich.md gives hp 111 (11d6+55 plus 15 false life), Str 10, Int 22, Init +2 and 'Aura fear (60-ft. radius, DC 18)'; the JSON carried scenario-tuned 100 hp / Str 12 / Int 20 / Init +6 and the engine's feature-keyed aura constants carried 30 ft / DC 22 (BAB/touch/AC/saves/CON already conformed); the sim's own spell list is kept (the KB's prepared list includes spells outside data/spells/) and the choice is documented in the JSON notes and the README; golden tammara-vs-lich re-pinned for the two SRD-correct line changes (initiative d20=10+2=12 from the KB +2, Magic Missile DC17 = 10+1+6 from Int 22; damage unchanged - the 75 ft deployment distance stays beyond even the widened 60 ft aura, so no save die is consumed); +2 aura range tests (75 ft outside, 60 ft inside) 2026-09-19 23:05:25 +02:00
ctan 9f0d86273a fix(rules): self-centered bursts for bless and bane - AreaEffect gains a data-driven 'centered_on_caster' flag (bless.json/bane.json set it per SRD bless.md '50-ft. burst, centered on the caster' and bane.md 'centered on you'); the burst origin is the caster's square regardless of the resolved target or a U7b target_pos, and with no chosen origin there is no primary save - every creature in the area rolls its own (mirror of the point-cast path); snapshot area key exposes centered_on_caster; golden transcript unchanged; +6 tests 2026-09-19 22:38:39 +02:00
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 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 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 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
ctan b74baf1aa9 fix(rules): Lonely and Bitter drawbacks replace bogus +1 Will bonuses (SRD: Lonely -2 Sense Motive and -2 saves vs charm with mind_affecting standing in; Bitter reduces ally-sourced healing by 1 via engine hook) 2026-09-19 10:50:50 +02:00
ctan a5ebbecad8 fix(rules): Friendly Face and Extremely Fashionable become documented no-ops (SRD: animal attitude +2 steps and +1 Bluff/Diplomacy/Intimidate in fine clothing - neither attitude nor clothing state is modeled; old +1/+4 Diplomacy bonuses were wrong) 2026-09-19 10:44:25 +02:00
ctan 6d0ee1bfeb fix(rules): Born Damned and Axe to Grind become documented no-ops (SRD: +2 saves vs curses has no curse descriptor; +1 damage vs foes threatened only by you is not expressible - old Fort-vs-poison and axe-damage bonuses were wrong) 2026-09-19 10:41:21 +02:00
ctan d0db208610 fix(rules): Exalted of the Society grants +1 channel energy/day via resource_grants; Fortified Drinker becomes documented no-op (SRD: +2 saves vs mind-affecting after drinking, alcohol state not modeled) 2026-09-19 10:37:23 +02:00
ctan 0c3f5fd907 fix(rules): condition durations scale with caster level (hold person, doom, touch of fatigue, vanish)
ConditionEffect gains duration_rounds_per_level and max_duration_rounds
(mirroring PenaltyEffect), applied as caster_level * per_level rounds capped
at max: hold person 1 rd/lvl, doom 1 min/lvl, touch of fatigue 1 rd/lvl,
vanish 1 rd/lvl (max 5). Extend Spell doubles both the per-level rate and
the cap. Tests cover scaling and the vanish cap.
2026-09-17 23:26:45 +02:00
ctan 6d96779ded fix(rules): spear of purity and shard of chaos deliver their SRD 1d8-per-two-levels ranged-touch damage (Will half); alignment gate documented as unmodeled 2026-09-17 22:45:38 +02:00
ctan bee224ab37 fix(rules): divine favor grants its SRD luck bonus on attack and weapon damage (+1 per three caster levels, max +3) for 1 minute 2026-09-17 22:41:00 +02:00
ctan 47f563e11a fix(rules): shield of faith grants its SRD deflection AC buff (+2, +1 per six caster levels, max +5) via a new BuffEffect flat scaling 2026-09-17 22:36:18 +02:00
ctan 27f962920f fix(rules): small SRD data corrections - alarm close, air bubble/endure elements touch, delay poison conjuration, 10-ft/0-ft range conventions documented, daze 4 HD or fewer 2026-09-17 21:44:19 +02:00
ctan 48c816b303 fix(rules): delay disease is a level-2 conjuration (healing) spell, not level-3 abjuration 2026-09-17 21:41:38 +02:00
ctan 79d9ff0c30 fix(rules): drunkard's breath is a level-2 conjuration cone that nauseates (Fort negates), not an invented abjuration DR-vs-alcohol buff 2026-09-17 21:40:27 +02:00
ctan 83bbcae952 fix(rules): ray of enfeeblement applies a rolled Strength penalty (Fort half) instead of fatigued
SRD ray-of-enfeeblement.md: 1d6 + 1 per 2 caster levels (max 1d6+5) Strength
penalty, Fortitude half, 1 round/level. Adds a PenaltyEffect type (rolled
formula, scaling, half on save, level-scaled duration) applied as negative
untyped modifiers on the rolls Strength feeds (attack, damage), since the
engine has no ability-score track. Formerly modeled as a fatigued condition
with Fort negates.
2026-09-17 21:28:00 +02:00
ctan 611952cbf7 fix(rules): misty - add sheet feat Plot Armor as traced no-op so data matches the Foundry sheet 2026-09-16 22:18:54 +02:00
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 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 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 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 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 9768f5ae1d feat(spells): allow spells to ignore spell resistance
SpellSpec gains spell_resistance: bool = True (JSON 'spell_resistance'). When false, _spell_delivery_blocked skips the targeted SR check and _apply_area_effect skips the per-creature SR check (threaded through the effect chain). Verified each imported spell against the local KB and set false on the 28 whose SRD block says 'Spell Resistance: no' (Acid Arrow, Flurry of Snowballs, Web, Darkness, Sanctuary, the Protection from X spells, etc.). Added no-SR targeted/area tests and a high-SR control in test_spell_resistance.py, and the README.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 12:44:35 +02:00
ctan c96b36d3f9 fix(spells): apply a shaken effect when a fear save succeeds
ConditionEffect gains save_partial (JSON 'on_success': condition_name + duration_rounds). On a successful save that would otherwise negate, _apply_condition_effect now imposes the weaker condition (SRD fear.md: panicked on a failed Will save, shaken for 1 round on a success). Extended _metamagic_transform_sub to preserve/double it; extracted _impose_condition helper. Updated fear.json, added failed/successful-save and loader tests in test_spells.py, and the README.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 12:32:45 +02:00
ctan a59ebee37a feat(rules): add ongoing spell damage
Acid Arrow (SRD spells/acid-arrow.md) deals 2d4 acid, then 2d4 again at the start of each of the target's turns for 1 round + 1 per three caster levels (max 6). DamageEffect gains a RecurringDamageSpec (JSON 'recurring'); a landed hit attaches a RecurringDamage source to CombatantState.recurring_damage, ticked in _turn_gate (after bleed), reduced by energy resistance/immunity but never DR (spell damage), cleared at expiry, and exposed in the observation snapshot. Extracted _resolve_one_projectile to keep _apply_damage_effect under the complexity ceiling. Updated acid_arrow.json, added tests/test_recurring_damage.py, and the README.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 12:25:38 +02:00
ctan 88ef9f39be feat(rules): support multi-projectile spells
SRD Magic Missile fires one missile +1 per two caster levels beyond 1st (max 5, 1d4+1 force, auto-hit); Scorching Ray fires one ray +1 per four levels beyond 3rd (max 3, 4d6 fire, ranged touch each). DamageEffect gains a ProjectileScaling spec (JSON 'projectiles'); _apply_damage_effect loops count_for(caster_level) projectiles, each rolling its own damage and (for a declared-projectile ray) its own ranged touch attack, with the target-drops-mid-sequence convention losing the rest. Updated magic_missile.json/scorching_ray.json, the golden lich transcript (CL 11 now fires 5 missiles, Tammara dies round 1: 43 damage), the scripted MM queues in test_spells/test_conformance_spellflow, added tests/test_multi_projectile.py, and the README.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 12:05:56 +02:00
ctan ee8c8f80ff fix(spells): set the correct area shapes
SRD-verified area templates: Burning Hands 15-ft cone, Color Spray 15-ft cone, Fear 30-ft cone, Flurry of Snowballs 30-ft cone, Lightning Bolt 120-ft line (all converted from single-target/burst approximations to proper AreaEffect shapes); Fireball 20-ft burst, Sound Burst 10-ft spread, Sleep 10-ft burst, Web 20-ft spread kept. Close-range spells note the 15/30-ft RAW ranges as an approximation in their descriptions. README: cone/scaling limitations moved out of the limitations list, new area-shape + caster-level scaling rules documented, areas.py added to the architecture.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 11:01:07 +02:00
ctan a6cc5aef36 feat(rules): scale spell damage with caster level
DamageEffect gains optional scaling (1dX per N levels, capped) and scaling_flat (+X per N levels, capped) specs; effective_formula resolves them against the caster's level at cast time, so scaled dice flow through crits, energy resistance and metamagic like the base formula (CRB Magic - Caster Level). Loader rejects mismatched die types. Data: Fireball/Lightning Bolt 1d6/lvl max 10d6, Burning Hands 1d4/lvl max 5d4, Shocking Grasp 1d6/lvl max 5d6, Inflict Light Wounds +1/lvl max +5 (SRD-verified). Cure Light Wounds stays flat: its golden transcript pins 1d8+1 and HealEffect scaling is 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 10:48:09 +02:00
ctan 734cfa4e55 docs(rules): document quick clear and doom description
README: the quick_clear deed is classified swift in the engine (SRD: standard action, or move with All Firearms) and is a functional no-op without a misfire model - documented rather than half-implemented. doom.json: the description mentioned a '-1 on a successful save' (a D&D 3.5 rule); PF1e Doom is Will negates with no partial success - description text corrected, effects unchanged (save_negates was already correct).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 15:11:56 +02:00
ctan 5b64fbee48 fix(spells): correct the fear condition
PF1e (SRD spells/fear.md): creatures become panicked unless they succeed on a Will save - the JSON applied frightened instead. Condition fixed to panicked. The shaken-for-1-round on a successful save needs partial-save support for conditions and the 30-ft cone needs a cone template, neither of which the engine has; both documented in README as out of scope rather than built.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:05:49 +02:00
ctan b5b1bfd395 fix(spells): correct the acid arrow range
PF1e (SRD spells/acid-arrow.md): "Range long (400 ft. + 40 ft./level)" - the JSON declared medium. Fixed to long. The spell's ongoing acid (2d4 per extra round, one per three caster levels, unless neutralized) is not modeled: the engine has no per-spell damage-over-time subsystem, documented in README rather than built.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:03:11 +02:00
ctan 5c48579768 fix(spells): correct the burning hands damage
PF1e (SRD spells/burning-hands.md): "1d4 points of fire damage per caster level (maximum 5d4)" — the JSON encoded 1d4+1, a flat bonus the spell never grants. Base is now 1d4. Per-caster-level scaling and the 15-ft. cone are not modeled by the engine (no CL-scaled dice, no cone template); documented in README rather than built.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:01:12 +02:00
ctan 910ed1dc7d fix(spells): make the bleed spell bleed
PF1e (SRD spells/bleed.md): "Saving Throw Will negates" and the target takes 1 point of damage per round. Two divergences: the JSON declared a Fortitude save, and the bleeding condition never set the engine's bleed_damage field, so the spell was a no-op. Bleed now saves Will; _apply_condition_effect sets bleed_damage = 1 when a spell applies bleeding, and _decrement_durations lands the final tick as the last bleeding condition expires before clearing the field (heal-path clearing already existed in _stop_bleeding). README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 12:46:53 +02:00
ctan 8c7e7075ad fix(spells): correct the sound burst save
PF1e (SRD spells/sound-burst.md): "Every creature in the area takes 1d8 points of sonic damage and must succeed on a Fortitude save to avoid being stunned for 1 round" — the Fortitude partial negates the stun only, never the dice. The damage component declared half_on_save: true, halving the sonic damage on a successful save. DamageEffect gains a save_negates flag (default True, existing behavior unchanged); sound_burst.json declares half_on_save: false + save_negates: false so the 1d8 is full regardless while the condition keeps save_negates. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 12:31:06 +02:00