Compare commits

...

274 Commits

Author SHA1 Message Date
ctan f61fc5c61f docs: reorganize documentation structure
- Clean README.md with installation, usage, CLI, examples
- Move data formats to docs/data-formats/ (map.md, monster.md, pc-sheets.md, index.md)
- Move architecture to docs/architecture/ (index.md, technical-architecture.md, phase-2-llm-architecture.md)
- Add contributing guidelines in docs/contributing.md
- Clean README.md: install, usage, CLI, data-formats/rules/architecture links, contributing link
2026-09-26 20:12:05 +02:00
ctan b0bd0b95f4 docs(phase2): record the P5.3 report writer
- Mark P5.3 (rapport FR) as completed in architecture doc
- Update test count to 2238 (2211 + 27 new tests)
- Document report.py module, prompt pack, CLI flags, budget integration
2026-09-26 10:43:14 +02:00
ctan 98985ee6fc feat(phase2): llm report writer (P5.3)
- New module src/pf1e_simulator/report.py with ReportContext, build_report_context,
  LLMReportPolicy, ReportBudget, format_llm_report, build_report_prompt
- Prompt pack fixture: tests/fixtures/prompt_pack_fs/report.md (FR narrator)
- CLI integration: --llm-report + --output flags, mutually exclusive with --transcript
- Budget enforcement: ReportBudget integrates with CostBudget, allows max_retries+1 calls
- Tests: test_report_context, test_report_prompt_pack, test_llm_report_policy,
  test_format_llm_report, test_cli_llm_report, test_report_budget (27 new tests)
2026-09-26 10:40:58 +02:00
ctan c454a67b84 docs(phase2): record the P5.2 few-shot prompts and repair loop 2026-09-25 23:24:29 +02:00
ctan dc890fc8fd feat(phase2): few-shot prompts + bounded repair loop (P5.2) 2026-09-25 23:24:27 +02:00
ctan d4db4f6d33 docs(phase2): record the P5.1 provider client and plan status 2026-09-25 22:22:30 +02:00
ctan 42802f7bf5 feat(phase2): OpenAI-compatible provider client (P5.1) 2026-09-25 22:22:03 +02:00
ctan 790df17623 docs(phase2): record the P4 robustness measurements and plan status 2026-09-25 21:34:13 +02:00
ctan cd69312ff4 feat(phase2): adversary and robustness cartography (P4) 2026-09-25 21:34:11 +02:00
ctan 016d6e1459 docs(phase2): record the P3 optimizer playbook and plan status 2026-09-22 15:44:06 +02:00
ctan dd64c2ebd6 feat(phase2): optimizer and scenario compiler (P3) 2026-09-22 15:23:05 +02:00
ctan 115f54a7e5 docs(phase2): record the P2 sweep measurements and plan status 2026-09-22 13:48:01 +02:00
ctan dbd1a5822e feat(phase2): strategy compiler and sweep runner (P2) 2026-09-22 13:46:05 +02:00
ctan 15d24be638 feat(phase2): P1c 2026-09-22 12:21:23 +02:00
ctan de4645838b feat(phase2): provider-agnostic LLM client + composite cache (P1b) 2026-09-22 09:45:18 +02:00
ctan f0617a0de6 feat(phase2): external config + versioned prompt pack (P1a)
config.py centralizes phase-2 path/env reads (no hard-coded data paths,
section 15). prompt.py loads the EXTERNAL, versioned prompt pack (section
14): pack version + per-prompt content hash feed prompt_version for the
response-cache composite key (D6); missing/invalid packs raise a clear
PromptPackError, never a silent fallback. Minimal fixture pack under
tests/fixtures/prompt_pack/ pins CI to a local, versioned pack.
2026-09-22 09:21:28 +02:00
ctan 26c5b51b98 docs(phase2): record the runner entry points (P0.4) and replay (P0.5)
run_encounter gains llm_policy and per-run policy_factory (mutually
exclusive with policy/llm_policy); replay.py logs every (observation,
action) keyed by call_seq to JSONL and replay() reproduces the recorded
battle bit-identically (wins/draws/rounds/transcript) with mismatch,
exhausted, duplicate and out-of-order guards. P0 frontier complete: the
recorded run replays identically.
2026-09-22 08:45:52 +02:00
ctan 6c6de16e60 feat(phase2): replay log and replay policy (P0.5)
Add replay.py: a ReplayLog records every (observation, action) pair a
policy produced, keyed by monotonic call_seq, plus the EncounterSpec,
seed and round_cap, and serializes the whole battle to JSONL. replay()
turns a log back into an observation-only LLMPolicy that returns each
recorded action, raising ReplayMismatchError on a differing observation
and ReplayExhaustedError past the last call.

Observations are matched by their canonical JSON (observation_key), not
raw dataclass equality, because JSON collapses tuples and lists and the
snapshot's inner dict[str, object] holds both. Action and the spec are
dumped through Pydantic TypeAdapters (mode=json) so tuple fields survive
as arrays and are coerced back on load.

Move TrickCondition/ReadyTrigger out of the TYPE_CHECKING block in
combat.py: TypeAdapter(Action) must resolve them at runtime to serialize
the dataclass, so the Literal aliases now live at module level.
2026-09-21 15:08:34 +02:00
ctan 334f8f9d05 feat(phase2): runner llm_policy and policy_factory entry points 2026-09-21 14:46:25 +02:00
ctan ac7fe20054 docs(phase2): record the compact view measurements (P0.2) and candidates (P0.3)
Compact render is 15,691 bytes (~3.9k tokens) = 24.4% of the raw 64,204-byte
to_dict() snapshot on the representative 7-combatant encounter, hitting the
~2-4k token target; grids are sparse and determinism is pinned in tests.
candidates() derives plausible actions from the pairs with no second legality
engine (swift/free/full_round excluded).
2026-09-21 14:36:24 +02:00
ctan 8b5043072a feat(phase2): candidate actions 2026-09-21 13:46:05 +02:00
ctan 9da9537995 feat(phase2): compact observation view for LLM prompts
Build a decision-sufficient ObservationView from the raw snapshot and
render it as a deterministic line-based prompt string. The raw to_dict()
is ~64 KB (~16k tokens) for a 7-combatant encounter; the compact view
renders at ~15.7 KB (~3.9k tokens, 24% of raw), under the 4k-token
ceiling of the phase-2 architecture doc.

Compaction: sparse terrain/elevation/threatened grids, per-combatant
key selection (occupied_cells, abilities, skills, initiative_mod/total,
temp_hp_rounds_left, features, carried_items, weapon_damage, aware,
alignment, armor_spell_failure, arcane_caster, run_rounds dropped),
attacks compacted to formula+type damage strings, spells to 7
decision-relevant fields, pair target_index replaced by the stable
combatant id, condition_definitions dropped. Attacks and spells render
as positional tuples declared by attack_fields/spell_fields legend
lines, with '-' for missing values.

Tests pin the size bounds (compact < 0.25 x raw and < 16000 bytes), the
sparse-grid contract, the dropped-key contract and the deterministic
id/render contract on a caster-heavy 3v4 representative battle.
2026-09-21 13:21:33 +02:00
ctan 25418f164f docs(phase2): record the executed spike (measurements + first danger map)
Measures: observation 64 KB (~16k tokens) for 7 combatants / 22 KB for a 2v2;
124 policy calls per battle, i.e. 124k calls per 1000-run sweep cell -> a
per-turn LLM in a sweep is infeasible (D9 validated) and a compact view is
mandatory. Danger map (default vs focus-fire monsters, 100 runs): the default
policy understates danger (PC deaths 13%->24% and 0%->19%, TPK 0%->2%) and the
win rate alone misleads (rises to 98% while deaths rise) -> D12/D13 validated.
Notes the spike limits and the remaining P0.2-P0.5 items.
2026-09-21 12:15:16 +02:00
ctan de88543c5c feat(phase2): danger-cartography spike - per-run outcomes, objectives, focus-fire adversary
run_encounter gains an opt-in collect_runs flag returning one RunOutcome
per battle (winner, rounds, per-combatant id/side/hp/hp_max/dead) next to
the EncounterReport; default behavior is unchanged. objectives.py provides
the pure optimizer primitives: P(survives all), P(TPK), P(>=1 death),
expected hp lost, expected rounds and a worst-decile (CVaR-style) tail
metric. phase2_spike.py holds the spike-level strengthened monster policy
(focus fire on the lowest-AC enemy, ties to the wounded; party side stays
on default_policy). tests/test_phase2_danger_map.py prints the danger-map
sketch: warband and lich scenarios measured default vs focus-fire at
100 runs / seed 11, pinning the delta deterministically.
2026-09-21 12:07:23 +02:00
ctan e5c4892a57 test(phase2): measurement spike - observation size and policy calls per battle
Representative 7-combatant caster-heavy encounter on sample_arena,
measured through the real engine path (engine._build_observation at each
policy call, delegating to default_policy):
- observation: 64,204 bytes at first call, 67,712 max mid-battle (~16.9k tokens)
- policy calls/battle: 124 (7 combatants, round_cap=20), 12 for a 2v2 duel
- projected calls per sweep cell: 124 / 12,400 / 124,000 at runs 1/100/1000
Loose bounds (obs < 200 KB, calls/battle < 200) + seed-determinism pinned.
2026-09-21 11:26:24 +02:00
ctan 3c3e30634e feat(phase2): observation-only LLMPolicy entry (D1) + pinned schema_version (P0.1)
- LLMPolicy is now Callable[[Observation], tuple[Action, ...]]: the snapshot
  is the policy's sole information source; the live CombatantState is no
  longer passed to new-style policies.
- CombatEngine._llm_adapter keeps a compatibility path for pre-D1
  two-argument callables: the signature is inspected ONCE at wrap time
  (_accepts_state) and legacy policies still receive (observation, state).
- Observation.to_dict() carries schema_version (OBSERVATION_SCHEMA_VERSION=1)
  as its first pinned key; bump only on removal/meaning change.
- Pinned keyset tests updated (test_observation, test_observation_enriched);
  new tests: observation-only e2e, legacy 2-arg e2e through the adapter,
  schema_version pin + JSON-serializability.
- README: documents the new entry signature and the schema_version key.
2026-09-21 11:10:28 +02:00
ctan 3fd9860b5c docs(phase2): record the arbitrated decisions (external prompts, cartography-first)
All recommendations accepted by the user except prompt location: prompts live
outside this repo (separate pack, configurable path, versioned; prompt_version
feeds the cache key and the replay record; a pinned pack for integration, a
minimal local fixture for unit tests, no silent fallback). Also records the
repo-scope decision (code only: calculation + LLM + report) with the final
detachment of data/sheets/prompts as a backlog item and the design constraint
to avoid hardcoded data paths in the new code. Priority: cartography first.
2026-09-21 10:49:00 +02:00
ctan d794828c7e docs(phase2): add the optimizer and adversarial-cartography layers
The execution tiers answer 'how to run a tactic', not 'which tactic is best'
nor 'where is the danger'. Adds two decision layers: an optimizer over
StrategySpec (objectives incl. tail risk, coarse-to-fine search, LLM as
candidate seeder) and an adversarial cartography (AdversarySpec + alternating
min-max + scenario sweeps + danger frontier + sensitivity), giving the danger
view for BOTH sides. Reframes the LLM as hypothesis generator / red team /
explicator / reporter, never the optimizer or per-turn executor in sweeps.
Adds D12-D14 and phases P3 (optimizer) / P4 (robustness).
2026-09-21 10:27:11 +02:00
ctan 630f5321e0 docs(phase2): revise the LLM architecture after critique
Key correction: the LLM must not sit on the per-turn hot path of a Monte
Carlo sweep (runs x rounds x combatants calls, no cross-run cache reuse).
Adopts a two-tier design - a compiled, parameterised deterministic Policy for
quantitative sweeps (tier A) and a per-turn NLPolicy for a handful of showcase
battles (tier B). Also fixes: 'engine unchanged' -> rules unchanged/additive
boundary; replay keyed by a monotonic call_seq (delay re-queues, summons);
composite response-cache key incl. model id; ObservationView as a structured
object; candidates derived from pairs instead of a second legality engine;
per-run PolicyFactory; cost guardrails in P1; concurrency; D1 to settle in P0.
2026-09-21 09:55:53 +02:00
ctan be64451ba4 docs(phase2): architecture and implementation plan for the LLM decision layer
Design-only document (no code): target architecture (compact versioned
observation view, legal-actions API, LLM client + cache, NL policy with
parse/validate/repair/fallback, strategy sweep, replay/eval harness), the
concrete gaps in the current runner/CLI wiring, the decisions to settle and a
phased implementation plan (P0 foundations, P1 client+policy, P2 strategy
sweep, P3 provider+report) with exit criteria and risks.
2026-09-21 09:51:05 +02:00
ctan 7e4a9f5edf docs(rules): conformance journal round 15 - nonlethal, splash, skills and flight (out of scope -> modeled)
Brings in the last four out-of-scope mechanics: the nonlethal damage track
(unarmed strikes, minimum damage), splash weapons (direct + scatter),
combat skills (skill checks, Acrobatics Tumbling, Stealth/opposed Perception)
and 3D flight over ground terrain (vertical line of effect deferred). Updates
the out-of-scope and residual tables to drop the now-modeled items and marks
the round-2 disposition tables as historical. Gate 1981.
2026-09-20 22:35:55 +02:00
ctan 9210a54589 docs(rules): document 3D flight over ground terrain (airborne view: difficult terrain cost 1, cross non-LoS impassable cells, blocks_los still stops flight; elevation climb still applies; LoE/cover/high-ground unchanged) and the deferred 3D line-of-effect/maneuverability with SRD citations (Movement, Position and Distance; Aiming a Spell); map legend + grid.py notes, test count 1981 2026-09-20 22:29:05 +02:00
ctan 2ae86ab29f feat(rules): 3D flight over ground terrain - airborne creatures (fly >= land, V4 budget from fly speed) move on Grid.airborne_view: ground difficult terrain costs 1 and cells impassable to walkers (pits, water, rubble, low obstacles) are crossable at cost 1, EXCEPT blocks_los cells (solid barriers, assumed full height) which still stop flight; run/charge/5-foot-step gates and diagonal corner rule follow the same view (flying past a pit is legal, past a wall corner is not); elevation climb cost still applies (step heights, not wall heights - documented); line of effect, cover and high ground use the unchanged real grid so combat geometry is unaffected by flight (SRD Movement, Position and Distance: flying creatures not hampered by difficult terrain, able to avoid most obstacles), 11 tests 2026-09-20 22:21:40 +02:00
ctan 1257bb3849 docs(rules): document skill checks, Acrobatics Tumbling and Stealth in README (rule list, observation schema hidden_from, out-of-scope updates, test count 1970) 2026-09-20 21:48:02 +02:00
ctan 2c40994610 feat(rules): Stealth + opposed Perception - opt-in hide move action (Stealth d20 + trained class + ACP + size mod vs each enemy Perception with -1/10ft distance; observed creatures cannot be hidden from), per-observer hidden_from state treated as total concealment + unseen-attacker +2/deny-Dex for failed observers, attacking or running/charging breaks stealth, move/5-foot-step/withdraw re-check the opposed roll, observation exposes hidden_from (SRD Stealth/Perception/Skill Checks), 13 tests 2026-09-20 21:39:54 +02:00
ctan 61686b5f4e feat(rules): Acrobatics TUMBLING opt-in on move - half speed budget, one Acrobatics check per crossed opponent (DC = CMD, +5 fresh entry into its space trained-only, +2 per additional opponent beyond the first), success spends the opportunity without a strike, failure provokes the AoO and ends the movement (SRD Acrobatics Tumble / Moving Through a Square), transcript logs each check, 9 tests 2026-09-20 21:06:12 +02:00
ctan e0fb5beae2 feat(rules): skill-check subsystem foundation - public skill_check helper (d20 + ranks + ability + trained-class-skill +3 + armor check penalty + typed bonuses, one-shot skill track discharged, no natural 1/20 auto-success/fail per SRD Skill Checks), resolve_skill delegates to it, class-skill +3 now requires invested ranks (Table 4-2 Trained Class Skill row), 8 tests 2026-09-20 20:48:26 +02:00
ctan 678a4d8b31 feat(rules): splash weapon test suite - direct hit full damage + 1 splash to adjacent only, miss scatters d8 clockwise one range increment dealing splash alone (thrower can be caught), energy resistance/immunity apply, DR does not, splash never crit-multiplied, no precision damage, non-splash RNG stream bit-identical, schema rejects splash on melee, observation exposes splash; transcript logs splash after the damage segment 2026-09-20 19:33:12 +02:00
ctan a1b1c29ca3 feat(rules): expose splash in observation attack reference + AttackResult.splash, document splash weapon rule and JSON schema in README (SRD Throw Splash Weapon) 2026-09-20 19:23:15 +02:00
ctan 2e60bbb419 feat(rules): splash weapon resolution - direct hit splashes 1 energy to all within 5 ft (target excluded), miss scatters d8 (1=short toward thrower, 2-8 clockwise) one range increment and splashes the landing + adjacent squares; energy resistance applies, DR and crit never; no precision damage (SRD Throw Splash Weapon) 2026-09-20 19:18:46 +02:00
ctan 9cfcd67f70 feat(rules): SplashSpec data model - data-driven splash marker on thrown weapons, Alchemist's Fire splash 1 fire on oni/tammara (SRD Throw Splash Weapon, Alchemist's fire) 2026-09-20 19:11:39 +02:00
ctan 83c6e541ac feat(rules): nonlethal sources - unarmed strikes deal nonlethal by default (SRD Unarmed Attacks), Minimum Damage floor lands 1 nonlethal (CRB Damage), transcript + AttackResult expose the track 2026-09-20 18:54:22 +02:00
ctan cba7bbf3c9 feat(rules): nonlethal damage track - separate total, derived staggered/unconscious, max-hp overflow, undead/construct immunity, healing removal (SRD Nonlethal Damage) 2026-09-20 18:41:53 +02:00
ctan 3d0fd0c101 docs(rules): conformance journal round 14 - transcript/format audit (14 bugs fixed)
A read-only audit of the transcript rendering, map and loader formats found
14 confirmed bugs (4 Med, 10 Low: duplicate --side, id-disambiguation
collision, range_increment_ft division by zero, silent spell_slots/features
drops, negative round-cap, multi-char legend keys, blank rows, id parsing,
duplicate YAML keys, move_cost default) - all fixed. The transcript/format
area is audited; only the structural out-of-scope items remain. Gate 1900.
2026-09-20 18:08:29 +02:00
ctan dd0b42b73b docs(map): make omitted move_cost explicitly mean impassable 2026-09-20 18:03:46 +02:00
ctan ee22f7548a fix(map): reject duplicate YAML keys via a SafeLoader duplicate-check 2026-09-20 18:00:27 +02:00
ctan 53c6dc0b43 fix(models): reject combatant ids that misparse the transcript format 2026-09-20 17:51:40 +02:00
ctan bce0a59e60 fix(map): fail on blank rows inside a layer, keep trailing-space cells verbatim 2026-09-20 17:48:47 +02:00
ctan 205ddece0b fix(map): require single-character legend keys 2026-09-20 17:43:50 +02:00
ctan c46e636d12 fix(cli): require --round-cap >= 1 2026-09-20 17:40:29 +02:00
ctan 10f811e80e fix(models): require a positive range_increment_ft (reject 0 and negatives) 2026-09-20 17:36:45 +02:00
ctan e9c2a63122 fix(runner): disambiguation suffix skips ids already taken by explicit combatants 2026-09-20 17:33:35 +02:00
ctan de9309ca43 fix(cli): reject duplicate --side names with a clear error 2026-09-20 17:29:10 +02:00
ctan 130087a556 fix(map): reject a zones layer without a deployment section
The README documents deployment as required when zones exist, but load_map
only enforced the reverse direction (deployment without zones). A map with
a zones layer and no deployment now fails with MapValidationError instead
of loading with unreachable deployment zones.
2026-09-20 17:15:29 +02:00
ctan a8c7e37f8e fix(loaders): resource pools validated consistently before grant merge
_merge_resource_grants silently dropped non-int pool values when features
were present (a "5" pool vanished, leaving only the feat grant) while
Pydantic coerced the same value when features were absent. _validated_resources
now runs the JSON resources through TypeAdapter(dict[str, int]) -- the same
lax coercion as Combatant.resources -- so both paths coerce-or-fail
identically, and grants are added to the coerced ints.
2026-09-20 17:13:09 +02:00
ctan 9adc87c9e1 fix(loaders): coerce combatant level with Pydantic-consistent rules
Feat resolution read level with an isinstance(int) check and fell back
to 1, while Pydantic accepts numeric strings and lossless floats: a
level-4 sheet authored as "4" got Toughness for level 1. _parse_level
now runs the value through TypeAdapter(int) (same lax coercion as the
model) and raises CombatantLoadError for values the model would reject.
2026-09-20 17:05:36 +02:00
ctan b8e96294a6 fix(loaders): non-list features and blank/non-string entries fail cleanly
features was popped and only re-added when it was a list, so a bare
string (or dict/null) loaded with zero feats - silent data loss. Now a
present non-list features raises CombatantLoadError, and the list
resolution adopts an accept-string-or-error contract: non-string or
blank entries raise instead of being silently dropped.
2026-09-20 17:02:39 +02:00
ctan 9656ae820a fix(loaders): malformed spell_slots key raises CombatantLoadError, not bare ValueError
int(k) on a non-numeric JSON key escaped the loader as an unhandled
ValueError traceback (CLI exit 1) instead of the documented exit code 2.
Route the conversion through _parse_spell_slot_keys which wraps it in a
CombatantLoadError with a clear message. Audited the other loader-side
int()/conversions: foundry.py guards every int() with isinstance/isdigit
checks and combatant.py has no other unguarded conversion.
2026-09-20 16:58:57 +02:00
ctan 5911602736 docs(rules): conformance journal round 13 - objects/inventory (out of scope -> modeled)
Sunder now uses object hardness/HP with the SRD broken condition (usable at
-2), steal transfers real carried items and unarmed disarm auto-picks up the
dropped weapon. Objects/inventory leave the out-of-scope list; skills,
nonlethal, splash, 3D flight and the transcript remain. Gate 1861.
2026-09-20 16:10:00 +02:00
ctan d0f7f3a107 feat(rules): carried items + real steal transfers, unarmed-disarm auto-pickup
- Combatant.carried_items (APG Steal: non-wielded, non-closely-worn gear),
  loaded from JSON, seeded into CombatantState, exposed in the snapshot.
- _maneuver_steal: success transfers the first carried item victim -> thief
  (logged); empty list keeps the documented nothing-stealable behaviour.
- _maneuver_disarm: unarmed attacker's success auto-picks the dropped weapon
  into its carried_items (SRD Disarm); armed disarms unchanged. Dropped-weapon
  recovery via retrieve_weapon (move action) verified + pinned by tests.
- data/pcs: gear transcribed from Foundry sheet inventories (esha 13,
  jeanne 3, misty 9, nairda 7, oni 6, tammara 20); harvie/ierlieth/
  esha_mount and all monsters empty (no stealable inventory), noted in README.
2026-09-20 16:05:07 +02:00
ctan 74f5d64c43 feat(rules): sunder deals real weapon damage vs object hardness/hp; broken weapons usable at -2 with restricted crits
SRD Sunder + CRB 'Smashing an Object' / 'Conditions - Broken': weapons
carry Table 7-12 object stats (AttackSpec.hardness/hit_points/enhancement,
defaults by weapon class); a successful sunder deals the attacker's actual
physical weapon damage minus the target weapon's hardness, subtracted from
its hp. At <= half hp the weapon gains the broken condition - still usable
at -2 attack/damage, crits only on a natural 20 at x2. At <= 0 it is
destroyed (weapon_destroyed, unusable). The old flat str_mod+2 / durability
10 / synthetic 'disarmed' condition is gone. Firearm misfire carve-out kept
(broken firearms stay unusable until Quick Clear - misfire escalation not
modeled, documented).
2026-09-20 15:43:45 +02:00
ctan 544d870688 docs(rules): conformance journal round 12 - spell components and arcane spell failure
107/108 spells carry their SRD components; arcane_caster and
armor_spell_failure are set from the sheets; the deafened 20% verbal
failure and the arcane spell failure roll are modeled. Components/ASF
leave the out-of-scope list. Gate 1850.
2026-09-20 14:38:42 +02:00
ctan 9f72948d23 docs(rules): README - component/ASF data now encoded, dedup snapshot bullet
The components/ASF bullet no longer defers the data encoding (done in
the two preceding commits): 107/108 registry spells carry their KB
components (Create Alcool is a homebrew cantrip with no KB entry) and
all 15 combatants carry arcane_caster plus an effective
armor_spell_failure - zero everywhere because the shipped arcane casters
are unarmored or wear light armor whose ASF their magus/bard feature
annuls for their own-list spells. Also collapses an accidental
duplicated armor_spell_failure/arcane_caster description in the
observation snapshot bullet.
2026-09-20 14:34:33 +02:00
ctan f6292f8bdc feat(rules): encode arcane_caster and armor_spell_failure from the sheets
- arcane_caster true: misty (Magus/Bard sheet class), nairda (Witch/
  Sorcerer), thrune_agent (Human magus 8, KB bestiary/thrune-agent.md);
  lich already true. False: oni (Cleric), tammara (Gunslinger/
  Warpriest - divine), esha (Cavalier/Rogue, spell-like only), the
  companions harvie/ierlieth/jeanne/esha_mount and the remaining
  monsters.
- armor_spell_failure stays 0 everywhere, as the effective PF1e value:
  nairda and the lich wear no armor (sheet note "Pas d'armure", KB lich
  block); misty's mithral shirt (RAW ASF 10%, magic-items/mithral-shirt
  .md) and the thrune agent's +1 studded leather (15%) are light armor
  and every spell they cast is on their own class list - magus and bard
  cast their own spells in light armor with no arcane spell failure
  (classes/magus.md, classes/bard.md).
- tests/test_spell_failure.py: the lich/oni smoke test becomes a
  full-data table pinning all 15 combatants' flags with per-entry
  sources. No shipped combatant incurs a non-zero ASF; the failure roll
  itself stays covered by the synthetic armored-caster tests.
2026-09-20 14:30:57 +02:00
ctan 91666d84b9 feat(rules): encode SRD spell components for all 108 registry spells
- Every data/spells/*.json now carries a "components" field with the
  V/S/M/F/DF tokens from its KB Components line (material/focus
  descriptions dropped; M/DF and F/DF alternatives kept verbatim).
- 107/108 encoded. Create Alcool (data/spells/create_alcool.json) is
  left without the field: it is a homebrew cantrip (level 0) with no KB
  entry - the closest real spell, Enhance Water (Inner Sea Gods, V, S),
  is already a separate registry entry.
- summon_monster_i/ii map to the KB's numeric files (summon-monster-1/2);
  hidden_diplomacy maps to its KB name Egorian Diplomacy (same spell,
  Spymaster's Handbook rename): V, S, M.
- tests/test_spell_failure.py: the placeholder asserting the registry was
  not yet encoded is replaced by an encoded-state smoke test (all spells
  but Create Alcool declare components; five spot-checks against the SRD
  stat blocks). Golden transcripts unchanged: the component gates still
  need deafened/ASF conditions no golden combat has.
2026-09-20 14:21:15 +02:00
ctan 1f8f068dd1 feat(rules): spell components + deafened verbal failure and arcane spell failure rolls
- SpellSpec.components (frozenset of V/S/M/F/DF, JSON key accepting the RAW
  string notation "V, S, M/DF" or a token list; slash = alternative; unknown
  token is a data error). Default empty = not encoded yet -> no failure.
- Combatant.armor_spell_failure (0-100 percent, authored on the JSON - the
  model has no equipment) and arcane_caster (divine casters never incur ASF);
  both load directly through load_combatant.
- _cast_spell: after the start-of-casting gates and before delivery, two
  conditional d100 rolls (convention mirrors the concealment roll, <=):
  deafened + V component spoils the spell on <=20 (SRD cast-a-spell.md);
  arcane_caster + S component + armor_spell_failure > 0 fails the spell on
  <= the ASF percent (SRD arcane-spells-and-armor.md). The slot stays spent
  (mirrors the concentration fizzle). No condition -> no roll: the RNG
  stream is bit-identical for every current combat (registry encodes no
  components yet - full encoding is a follow-up task).
- Observation: spell summary gains "components" (sorted tuple), combatant
  snapshot gains "armor_spell_failure"/"arcane_caster"; README documents
  the keys and the two former out-of-scope bullets now describe the modeled
  rule with its data-encoding follow-up.
- Data smoke test: lich arcane_caster true / ASF 0, oni arcane_caster false.
- tests/test_spell_failure.py: 21 tests (parsing, thresholds <=20 / <=ASF,
  divine/no-ASF/no-component never roll, roll order, slot stays spent,
  observation keys). Golden transcripts unchanged.
2026-09-20 14:02:10 +02:00
ctan a483070e13 docs(rules): conformance journal round 11 - alignment subsystem (out of scope -> modeled)
Adds the alignment field + axis flags, the AlignmentGate on spell damage
(Shard of Chaos, Spear of Purity) and the attacker-alignment-conditional
wards (Protection from Evil/Law + communal). Alignment leaves the
out-of-scope list; objects/inventory, skills, nonlethal and ASF are recorded
as the remaining out-of-scope mechanics. Gate 1829.
2026-09-20 13:02:15 +02:00
ctan 3be22b9c55 feat(rules): alignment-gated protection from evil/law wards via attacker context
Add attacker-alignment modifier conditions (attacker_is_evil/lawful/good/
chaotic) evaluated against an optional attacker context threaded through
_stat_modifiers/_modifier_condition_holds, _attack_ac_mods (resolve_attack
AC path), resolve_save and _area_save (spell save paths), plus the fear
aura. Without a source in scope the condition is False, so existing rolls
are untouched (golden transcripts bit-identical).

Give the four protection_from_evil/law(+communal) JSONs their SRD buff:
+2 deflection to AC and +2 resistance on fort/ref/will, gated on the
attacker/caster alignment (SRD: bonuses apply 'against attacks or effects
created by evil creatures'); duration 1 min/level approximated as 10
rounds (engine convention). Descriptions document the unmodeled mental-
control suppression and communal duration-split clauses. Level/range/
save/SR verified against the KB.
2026-09-20 12:57:29 +02:00
ctan 6a18d456f6 feat(rules): alignment-gated spell damage for Shard of Chaos and Spear of Purity
DamageEffect gains an optional AlignmentGate (axis law_chaos/good_evil):
the immune side (chaotic/good) is wholly unaffected (no damage dice rolled),
neutral-on-axis targets take half damage with no condition (gate halving
stacks after the save halving), and the full side (lawful/evil) takes full
damage plus the gate condition on a failed save. Shard of Chaos maps the
SRD slowed to the engine staggered condition (the -1 penalty and half-speed
of slow are not modeled); the outsider-only 1d6/level variants are not
modeled (no outsider subtype). Non-gated damage spells are bit-identical.
Loader validates axis/side pairing with clear ValueErrors.
2026-09-20 12:32:14 +02:00
ctan f9e51e377a feat(rules): add alignment field to Combatant with axis gating flags
Adds the PF1e two-axis alignment code (LG/NG/CG/LN/N/CN/LE/NE/CE,
default N) to the Combatant model with a strict validator, derived
is_lawful/is_chaotic/is_good/is_evil properties, exposure in the
observation snapshot, and alignment values for all monsters and PCs
sourced from the bestiary KB and the Foundry sheets.
2026-09-20 11:49:04 +02:00
ctan 40088e1869 docs(rules): conformance journal round 10 - utility spells (guidance, resistance, virtue, stabilize)
Guidance (new OneShotEffect), resistance (+1 resistance save buff, with the
cantrip-skip policy guard), virtue (temporary HP track) and stabilize
(auto-stabilization) are now modeled; spiritual_weapon and saving_finale
remain deferred with a precise engine reason. Gate 1734.
2026-09-20 10:52:22 +02:00
ctan f7886135bd fix(rules): stabilize is a StabilizeEffect that auto-stabilizes a dying creature 2026-09-20 10:48:08 +02:00
ctan a8a65b18cd fix(rules): virtue is 1 temporary hit point for 1 minute 2026-09-20 10:32:05 +02:00
ctan 1238c40e43 fix(rules): resistance is a +1 resistance bonus on fort/ref/will saves for 1 minute - SRD spells/resistance.md grants the subject a +1 resistance bonus on saving throws for 1 minute (Will negates, harmless); the resistance bonus type already exists in the stacking model (effects.py BonusType), so the buff is expressible as a BuffEffect and was only deferred because _find_buff_spell returns the FIRST BuffEffect in known-spell order and cantrips cost no slots - encoding it would have made default_policy cast Resistance every round (for Tammara it would preempt Divine Favor and never kill anything); resistance.json now carries a BuffEffect (+1 resistance on fort/ref/will, 10 rounds = 1 minute) and _find_buff_spell skips level-0 spells (cantrips have no spell slot in the engine's budget - spell_slots_left has no level-0 key - and the policy deliberately never auto-casts them, a cantrip buff would be cast every round degrading the policy), so Resistance cannot preempt a leveled buff (Divine Favor) in the known-spell order; leveled buffs keep the historical first-match behavior (if the first one's slots are exhausted the buff branch gives up - the golden pins that); the resistance bonus type does not stack with itself (resolve_modifiers keeps the higher value); +9 tests (test_resistance.py: buff shape, cast on a willing ally consumes no d20 and applies without a save roll, the bonus rides save rolls, no self-stacking, expiry at 10 rounds, _find_buff_spell skips the slot-less cantrip / returns None without a leveled buff, _choose_spell_action proposes Divine Favor and never Resistance); golden untouched; 1714 tests green 2026-09-20 10:10:32 +02:00
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 154c3f69a0 docs(rules): conformance journal round 9 - assumed residuals closed (10 fixes, 1 deferred)
Rounds 1-3 closed the assumed residuals: run bounded by CON endurance + Run
feat, harmless spells forgo the save on willing targets, self-centered bursts
for bless/bane, Selective Channeling, the lich stat block aligned to the KB,
summon-stats disambiguation, healing_done exposure, multi-cell difficult-terrain
move pricing, and the grazed-caster-cell cone/line fix. The burst intersection
origin is deferred with a precise rationale. Rewrites the residual table to
drop the now-fixed and stale rows, leaving only the genuinely remaining items.
2026-09-20 00:46:08 +02:00
ctan d5ad3c3aac fix(rules): a grazed caster cell no longer shortens a line spell - SRD Aiming a Spell: a line 'starts from any corner of your square and extends to the limit of its range' (Lightning Bolt: 'The bolt begins at your fingertips'), so the caster's own squares are origin space the line starts PAST and must not consume one of the length_cells squares; line_cells took a fixed ray[1:length_cells+1] window and dropped caster cells inside it, so a 2x2 caster's line came out one square short per grazed footprint cell (the old code documented this as a simplification); the ray is now extended (reach = length_cells + 1 + len(caster_cells)) and caster cells are skipped WITHOUT spending a length slot, so a multi-cell line runs its full length starting just past the footprint; 1x1 casters are bit-identical (the only skipped cell is the origin at ray[0], already outside the counted window); the barrier/LoE stop and out-of-bounds semantics are unchanged (a straight ray never re-enters a convex map); golden untouched (no line spell in it); +3 tests (grazed-cell full-length template, 1x1 bit-identical regression, engine-level reach of a foe the short line missed) 2026-09-20 00:41:02 +02:00
ctan d347dde3fc fix(rules): a multi-cell creature moving under mixed terrain pays the difficult rate for its whole footprint - SRD Movement, Position and Distance: 'If you occupy squares with different kinds of terrain, you can move only as fast as the most difficult terrain you occupy will allow'; the charge/run/5-foot-step already gated on the whole footprint (_footprint_touches_difficult) but the move/withdraw DESCENT priced every step by the anchor cell alone (grid.step_cost), so a 2x2 creature straddling rubble crossed at full speed; the descent now prices a step by the most difficult square under the footprint via _footprint_step_cost -> a new grid.step_cost_with_terrain (the 5-10-5 + elevation formula stays centralized in grid.py, step_cost delegates to it); 1x1 creatures are bit-identical (footprint == anchor cell); the direction Dijkstra field remains anchor-priced (making it creature-aware rewrites grid.reachable wholesale - the documented deferral); golden untouched (open-floor arena, no difficult terrain); +7 tests (trailing-square orthogonal/diagonal cost, 1x1 bit-identical regression, full-lane move budget 3-vs-6 squares, withdraw hampering) 2026-09-20 00:29:36 +02:00
ctan b775d46850 fix(rules): document the burst center-to-center convention as a deferred SRD approximation - the SRD (aiming-a-spell.md) measures a burst from a grid INTERSECTION with the far-edge rule ('count from intersection to intersection'; a square is inside only 'if the far edge of a square is within the spell's area'), but the engine's burst origin is a CELL (target_pos, centered_on_caster, resolved target) counted center-to-center 5-10-5 in _creatures_in_range; matching RAW exactly needs an intersection coordinate added to AreaEffect/Action.target_pos/the observation area snapshot contract plus a per-caster corner choice (the far-edge boundary result depends entirely on which of the cell's four corners is the origin), so it is DEFERRED with a precise explanation in areas.py and a pointer from _creatures_in_range; no behavior change, golden untouched 2026-09-20 00:16:18 +02:00
ctan e6021a6dda fix(rules): expose healing_done in per-combatant stats and the CLI report - the channel/spell healing deliberately removed from damage_dealt (2e2873f) was invisible; CombatantStats/_LiveStats gain a healing_done counter booked with the hp actually restored (capped at the target's hp_max, overheal uncounted) by _channel_heal and by spell HealEffect on living targets, while cure-as-damage on undead stays in damage_dealt and the damage counters keep their exact semantics; the runner aggregates and averages the new field and the French report prints it as 'soins prodigués'; +4 tests (channel heal, cure spell, undead cure guard, run_encounter aggregate) +1 CLI assertion 2026-09-19 23:23:00 +02:00
ctan 5e7436b210 fix(rules): duplicate summons get disambiguated ids - the engine now counts id occurrences (seeded from the deployed roster) and gives each new summon of the same creature a build_states-style suffix (celestial_wolf, celestial_wolf-2, ...), so two summons no longer share one _LiveStats slot (the second summon used to reset the first one's counters and their attrition merged); run_encounter aggregation verified to report each slot separately; runner 'known limitation' comment updated; +4 tests (id split, distinct slots, per-slot attrition, run_encounter aggregate) 2026-09-19 23:13:06 +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 5fe2b72339 fix(rules): selective channeling modeled as a policy-chosen exclusion - Action(kind=channel_energy) gains channel_exclude, honored only with the Selective Channeling feat and capped at the Charisma modifier (SRD feats/selective-channeling.md: 'choose a number of targets in the area up to your Charisma modifier... not affected by your channeled energy'); without the feat the list is ignored and logged (SRD Normal: all targets affected); observable in current data because heal mode also heals living ENEMIES in the burst (deny them the heal) and harm mode can spare an undead ally; default_policy never excludes (opt-in, like weapon_id/destination/metamagic); golden transcripts unchanged; +5 tests 2026-09-19 22:49:40 +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 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
ctan 418fdc826b fix(rules): a missed melee touch attack holds the charge instead of expending the spell (SRD Holding the Charge: re-attempted at the start of later turns, dissipated by casting another spell; ranged touch still expended on a miss) 2026-09-19 11:40:02 +02:00
ctan 81a09c88f0 fix(rules): metamagic no longer raises the save DC - only Heighten Spell does (SRD empower-spell: saving throws not affected; heighten-spell: DCs calculated at the heightened level, modeled at its minimum +1 slot adjustment) 2026-09-19 11:33:10 +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 1a68dfa6db docs(rules): conformance journal rounds 3-4 (22 fix commits) and residual inventory
Round 3 (attack resolution, bestiary/PC stat blocks, action economy) and
round 4 (remaining spells, PC feats) found 2 High, ~11 Med and ~70 Low
findings. Records the 22 fix commits (e0bf484..0c3f5fd), both justified
golden updates (Weapon Focus, Divine Favor), and the residual inventory:
~13 deferred spell effects, 20 feat/racial mismatches, and the missing
subsystems (alignment, items, skills). Journal marked paused at round 4.
2026-09-17 23:28:32 +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 a144423522 fix(rules): total defense and fighting defensively cannot be combined (SRD Total Defense) - no stacking, second declaration rejected 2026-09-17 23:13:22 +02:00
ctan 980d53d80b fix(rules): aid another bonuses expire at the beginning of the aider's next turn, not the beneficiary's (SRD Aid Another) 2026-09-17 23:09:44 +02:00
ctan e253b03ffe fix(rules): restricted charge - a creature limited to a standard action (staggered/disabled, surprise round) can still charge at 1x speed 2026-09-17 23:06:03 +02:00
ctan a5bd3c5a85 fix(rules): total defense denies attacks of opportunity (SRD Total Defense: allowance drops to 0 while the bonus is active) 2026-09-17 23:00:10 +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 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
ctan 3cc0502228 feat(rules): add the surprise round
SRD Surprise: when some but not all combatants are aware of their
opponents, a surprise round precedes round 1. Aware combatants take a
single standard OR move action plus free actions (swift and full-round
are dropped, full_attack collapses to one standard attack, withdraw
survives as the Restricted Withdraw standard); unaware combatants skip
their turn. New Combatant.aware flag (default true, parsed at the JSON
boundary) keeps every existing encounter bit-identical, exposed in the
observation snapshot. Unaware combatants are not flagged flat-footed
(documented simplification).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 13:20:37 +02:00
ctan 7a8351e3b2 docs(readme): update the test count after the spell feature batches 2026-09-13 12:50:54 +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 642f449b9e fix(rules): exclude the caster from a line spell's area
CRB Magic - Aiming a Spell: a line 'starts from any corner of your square', so the caster's own square is outside the template (SRD Lightning Bolt: 'The bolt begins at your fingertips' - it leaves the square, it does not fill it). line_cells now drops the origin and takes the length_cells squares beyond it; a degenerate aim affects nothing. Updated the Lightning Bolt and Line Sparks tests (caster no longer caught) 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 11:30:30 +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 4659fae9f6 feat(rules): support cone and line spell areas
AreaEffect gains shape: burst|cone|line (CRB Magic - Aiming a Spell). Cones (90 deg, length = radius_ft) and lines (5 ft wide, Bresenham ray) originate at the caster's cell and are aimed at the resolved target or the U7b point-cast center; bursts are unchanged. Shared per-creature save + SR application; the observation area dict already exposes the shape.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 10:32:37 +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 5bf2850e2f fix(rules): use the attacker's best corner for cover bonus
SRD 'Cover': 'To determine whether your target has cover from your ranged attack, choose a corner of your square.' The ranged scorer in cover_bonus_cells took the MAX over all attacker corners (worst corner); the cover gate has_cover_cells and RAW use the attacker's best corner. Ranged cells are now scored min over attacker corners of (max over target-corner lines), then min over attacker cells - the bonus can no longer exceed what the gate admits. Melee keeps the worst-corner rule ('any line from ANY corner of your square'). Updated the two pins that locked the old worst-corner scores.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 14:57:56 +02:00
ctan a989266747 fix(rules): require adjacency or pull the target when grappling
SRD 'Grapple': 'If you successfully grapple a creature that is not adjacent to you, move that creature to an adjacent open space (if no space is available, your grapple fails).' _maneuver_grapple now pulls a non-adjacent target to the nearest free anchor whose footprint is adjacent to the grappler (5-10-5 distance, (row,col) tie-break, new _nearest_anchor_adjacent_to helper) and fails outright when no adjacent open space exists. Three tests: pull, no-space failure, adjacent no-move.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 14:45:55 +02:00
ctan 25149c64ba fix(rules): allow a free weapon draw with a move at BAB +1
SRD 'Move Actions': 'If you have a base attack bonus of +1 or higher, you may draw a weapon as a free action combined with a regular move.' _filter_turn_actions computes free_draw (BAB >= 1 AND a move action in the turn sequence) and threads it through _effective_slot/_action_economy_filter/_staggered_filter: retrieve_weapon then occupies the free slot instead of the move slot. Static classification stays 'move' (repair_weapon and no-move draws unchanged). Filter-level and engine-level (BAB +2 vs BAB 0) tests added.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 14:35:42 +02:00
ctan 317c68ecdb fix(rules): allow a staggered creature to withdraw as a standard action
SRD 'Full-Round Actions - Restricted Withdraw': 'If you are limited to taking only a standard action each round you can withdraw as a standard action. In this case, you may move up to your speed.' _staggered_filter now keeps withdraw as the one move-or-standard action (spending the slot) instead of dropping it, and _flee_path halves the budget to single speed for a staggered/disabled creature (new _is_staggered_limited helper). Filter-level and engine-level tests added.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 14:21:04 +02:00
ctan a4487a85f9 fix(rules): block immediate actions while flat-footed
SRD 'Immediate Actions': 'You also cannot use an immediate action if you are flat-footed.' _check_reactions now skips states carrying the flat_footed condition before consulting the reaction policy. The engine never applies flat_footed itself (latent gate), so the unit test injects the condition via active_conditions, with a control test proving the same setup fires when not flat-footed.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 14:08:44 +02:00
ctan 7810d88488 fix(rules): expire a readied action at the combatant's next turn
SRD 'Ready - Initiative Consequences of Readying': 'If you come to your next action and have not yet performed your readied action, you don't get to take the readied action (though you can ready the same action again).' _take_turn's start-of-turn block now clears state.readied/readied_trigger (logged 'ready expired'), before the policy call; the ready still survives the round boundary and can fire in the next round as long as the readier's own turn has not arrived. Updated the two tests that pinned the old infinite persistence and added cross-round fire + expiry tests.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:59:11 +02:00
ctan ec2088ab90 fix(rules): apply cover concealment and criticals to touch attacks
CRB 'Cast a Spell - Touch Attacks': 'You can score critical hits with either type of attack as long as the spell deals damage'; CRB 'Range - Touch': a touch spell threatens on a natural 20 and deals double damage on a successful confirmation. The melee and ranged spell-touch gates now add the target's cover bonus to touch AC (new _attack_cover_bonus helper shared with resolve_attack), roll the concealment miss chance after the hit (blinded attacker = total concealment convention), and resolve crit threat/confirm with damage doubling for damage-dealing touch spells. Non-damage touch spells do not threaten (no die consumed). Golden transcript unaffected.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:47:03 +02:00
ctan 35c8d101b5 fix(rules): treat a blinded attacker as facing total concealment
CRB 'Conditions - Blinded': 'All opponents are considered to have total concealment (50% miss chance) against the blinded character.' The sneak-attack gate and the AoO suppression gate now apply the same max(target_concealment, 50) convention as _resolve_hit_damage: a blinded attacker cannot sneak attack and its AoOs are suppressed without spending the budget.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:26:52 +02:00
ctan d152d42af9 fix(rules): avoid the firing-into-melee penalty at 10 feet
SRD 'Attack - Shooting or Throwing into a Melee': a target at least 10 ft from the nearest friendly character lets you avoid the -4 penalty entirely (0, not -2). The -2 is reserved for targets two size categories larger than the engaged friendlies (0 at three+), which is not modeled (documented). Golden transcript unaffected (shot still misses).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:21:26 +02:00
ctan 84bcf004c3 docs(rules): document missile count and per-spell SR limits
Two documented limitations, no mechanical change: (a) Magic Missile fires a single 1d4+1 missile - the SRD scaling to up to five missiles per caster level and multi-target splitting is not modeled; (b) the engine applies spell resistance to every targeted spell, so the few spells whose SRD entry says "SR: no" (e.g. Acid Arrow) are not distinguished - a per-spell SR flag was deliberately not added.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 13:08:27 +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 b0fe553782 fix(rules): correct the cackle hex
PF1e (APG Cackle): "(Su) Free Action; ... extends the duration of all evil eye, fortune or misfortune hexes on all creatures within 30 feet by 1 round." The engine spent a hex use, acted only on the resolved target, and extended every hex-sourced condition (including slumber, which RAW cackle does not touch). Cackle is now dispatched before the resource/target gate (no cost), occupies the FREE budget slot via _effective_slot (the hex kind stays standard for evil_eye/slumber), and rebuilds the evil_eye buff list of every creature within 30 ft in a single bounded pass. Fortune/misfortune are not implemented, so evil_eye is the only extendable family member — documented in README. test_cackle.py rewritten for the new semantics (extension, range, free slot, no cost).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 12:58:32 +02:00
ctan 68699ad6c7 fix(rules): set the slumber hex duration to the witch level
PF1e (APG Slumber): the target "remains asleep for a number of rounds equal to the witch's level" (Will negates). _hex_slumber hardcoded 1 round; it now uses the caster's level. The RAW clause that the hex ends when the target takes damage is not modeled (no wake-on-damage hook in the engine) — documented in README. Two behavioral tests pin the failed-save duration (= level) and the saved case.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 12:52:52 +02:00
ctan f9c369380d fix(rules): correct the evil eye hex
PF1e (APG Evil Eye): the target takes -2 on ONE chosen option (AC, ability checks, attack rolls, saving throws or skill checks) for 3 rounds, and "A Will save reduces this to just 1 round" — it never negates. _hex_evil_eye applied shaken (-2 attack AND saves) for 1 round with the save negating entirely. It now adds a targeted -2 attack ActiveBuff for 3 rounds, or 1 round on a successful Will save. Simplification documented in README: the option choice is not modeled (attack rolls are the engine's target). The hex-DC behavioral test now reads the -2 attack (3r) transcript line.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 12:49:48 +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
ctan e3818fa6c9 fix(rules): make the run reachability check avoid difficult terrain
PF1e (CRB Movement): "You can't run or charge across difficult
terrain". _execute_run checked the explicit destination against the full
cost field (which routes through difficult terrain at 2x) but descended
with avoid_difficult=True, so a destination beyond rubble was accepted
(ok=True) yet silently truncated at the obstacle. _destination_reachable
now takes the same avoid_difficult constraint (difficult cells removed
from the field; a destination whose footprint touches difficult terrain is
not a legal run endpoint either), so verdict and path agree. The test that
pinned the old silent-stop-short convention now expects
unreachable_destination; README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:53:27 +02:00
ctan 8043dd393c fix(rules): scale reposition and drag distance with the margin
PF1e (APG Reposition / Drag): "For every 5 by which your attack exceeds
your opponent's CMD, you can move/drag the target an additional 5 feet."
Both handlers now walk 1 + margin//5 cells along the chosen (or auto-picked)
direction, stopping at the first obstacle or occupied square, mirroring the
bull rush scaling. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:43:12 +02:00
ctan f9420c7b09 fix(rules): scale dirty trick duration and drop nauseated
PF1e (APG Dirty Trick): "This condition lasts for 1 round. For every 5
by which your attack exceeds your opponent's CMD, the penalty lasts 1
additional round" — rounds_left = 1 + margin//5. The RAW condition list
(blinded, dazzled, deafened, entangled, shaken, sickened) does not include
nauseated, so it is removed from TrickCondition. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:34:20 +02:00
ctan 34be75c492 fix(rules): apply AC penalties to CMD
PF1e (SRD Combat Maneuvers): "Any penalties to a creature's AC also
apply to its CMD." _maneuver_cmd now folds negative AC modifiers into the
sum regardless of bonus type (grappled -2, other untyped penalties), and —
a maneuver being a melee attack roll — the prone -4 melee AC penalty.
Positive modifiers keep the eight SRD-listed types. The test that pinned
the old ignore-penalties convention is updated; README documents the rule.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:26:05 +02:00
ctan a4f7f4e5de fix(rules): exclude weapon special ability dice from critical multiplication
PF1e (CRB Critical Hits): "additional damage dice from weapon special
abilities (such as flaming) are not multiplied when you score a critical
hit". _resolve_hit_damage now multiplies only the physical dice plus flat
bonuses by crit_mult; energy-typed component dice are added at 1x (the
energy share on the DR path and the per-component resistance step drop
their crit factor). Sneak precision dice were already excluded. README
and the module convention list updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:21:35 +02:00
ctan a9b33e4055 fix(rules): apply the spell combat penalty to the spell attack
PF1e (CRB Magus Spell Combat): "any attack roll made as part of this
spell also takes this penalty". _execute_spell_combat now threads the -2
through _cast_spell into the melee/ranged touch attack helpers, so e.g.
Shocking Grasp delivered via Spell Combat attacks at -2 too. No global
state is touched: the penalty is a keyword threaded per cast.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:14:30 +02:00
ctan 068b49794e fix(rules): stop steal from taking held weapons
PF1e (APG Steal): "Items held in the hands (such as wielded weapons or
wands) also cannot be taken with the steal maneuver—you must use the disarm
combat maneuver instead." The model carries no non-held items, so a
successful steal now takes nothing and logs
"nothing stealable — wielded weapons require disarm". README updated; the
retrieve_weapon tests keep locking the restoration mechanism directly.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 11:06:08 +02:00
ctan 57a597babd fix(rules): cap reposition and drag by size category
PF1e (APG Reposition / Drag): "You can only reposition/drag an opponent
that is no more than one size category larger than you" — the same cap the
SRD puts on trip/bull rush/overrun. reposition and drag join
_SIZE_RESTRICTED_MANEUVERS, so a two-categories-larger target is rejected
outright (no roll, no AoO). README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:58:13 +02:00
ctan 5a848fd07c fix(rules): compute CMD from Strength and Dexterity directly
PF1e (SRD Combat Maneuvers): CMD = 10 + BAB + Strength modifier +
Dexterity modifier + special size modifier. The Tiny-or-smaller
Dex-for-Str substitution is CMB-only, but _maneuver_cmd reused
_maneuver_cmb for the base, so Tiny/Diminutive/Fine creatures got
10 + BAB + 2xDex + size and dropped Strength (live bug on the Tiny PC
ierlieth). The CMD base is now computed with str_mod directly; CMB
keeps its substitution.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:54:21 +02:00
ctan e1c9e78b4e fix(rules): stop auto-directed movement cutting corners
PF1e (CRB, Measuring Distance): "You can't move diagonally past a corner
(even by taking a 5-foot step)". _best_move_step (the greedy descent behind
the auto-directed move/run/5-foot-step) now enforces the same
diagonal_allowed corner check _step5_to and _best_flee_step already apply.

tests/test_combat.py::test_ranged_no_line_of_effect_moves_around_wall
pinned the old corner-cutting step (0,1)->(1,2) past the wall at (0,2);
re-scripted for the legal (0,1)->(1,1) path (wall cover now applies).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:48:12 +02:00
ctan 4b4e6f2726 fix(rules): stabilize a dying creature when healed
PF1e (CRB, Stable Characters and Recovery): any healing that cures a
dying creature of even 1 point makes it stable. _apply_heal_effect and
_channel_heal now set stabilized_hp when the healed hp stays negative,
so the creature stops losing 1 hp per round.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:35:44 +02:00
ctan e8cca954d0 docs(rules): document out-of-scope limitations
Record the three limitations deliberately left out of the L1-L10 conformance pass in the README: no surprise round (the combatant model has no awareness field), one_round casting clamped to a full-round action (no pending-cast state across turns), and Tiny/Diminutive/Fine reach and co-occupancy abstracted to the 5-ft grid.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:02:45 +02:00
ctan 1363bb7f06 fix(rules): consume the current turn's swift for an in-turn immediate
PF1e (SRD Immediate Actions): "Using an immediate action on your turn is the same as using a swift action and counts as your swift action for that turn." The engine now tracks the acting combatant (_turn_actor); an immediate fired during its OWN turn sets swift_used_this_turn instead of immediate_used, so swift actions later in the turn's sequence are dropped (downgraded:immediate_swift) and no second immediate fires that round, while the next turn's swift stays free. Off-turn immediates keep consuming the next swift. The off-turn consumption test was retargeted to a true off-turn event and an in-turn test added; README updated (Actions immediates + budget classification).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 10:00:25 +02:00
ctan dde6bc1f5d fix(rules): restrict nauseated creatures to a single move action
PF1e (SRD Conditions, nauseated): "The only action such a character can take is a single move actions per turn." _NAUSEA_MOVE_KINDS now holds only genuine move actions (move, stand_up, retrieve_weapon, repair_weapon): the 5-foot step and dropping prone are free actions and are dropped by the nauseated filter. The pinned filter test was retargeted; README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 09:44:53 +02:00
ctan 49aa79d612 fix(rules): restrict maneuvers to targets within one size category
PF1e (SRD Trip / Bull Rush / Overrun): "You can only [affect] an opponent who is no more than one size category larger than you." _execute_maneuver now rejects trip, bull_rush and overrun against a target two or more size categories larger: no roll, no provoked AoO, logged rejection. A _SIZE_ORDER ranking (Fine..Colossal, unknown -> Medium) backs the check; other maneuvers keep no size cap. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 09:40:50 +02:00
ctan 1b60cd049b fix(rules): apply maneuver failure consequences
PF1e (SRD Trip): "If your attack fails by 10 or more, you are knocked prone instead" - a trip whose total lands at CMD-10 or below now knocks the attacker prone (1 round). PF1e (SRD Overrun): "If your overrun attempt fails, you stop in the space directly in front of the opponent, or the nearest open space in front of the creature" - a failed overrun from beyond adjacency walks the attacker to the free approach cell nearest the target; an already-adjacent attacker stays put. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 09:34:57 +02:00
ctan 346e300eab fix(rules): scale the bull rush push distance with the margin
PF1e (SRD Bull Rush): "your target is pushed back 5 feet. For every 5 by which your attack exceeds your opponent's CMD you can push the target back an additional 5 feet." _bull_rush_push (and the explicit-direction branch) now walk 1 + margin//5 cells, stopping at the first obstacle; a natural-20 auto-success below CMD floors the margin at 0 (base push only). README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 09:27:03 +02:00
ctan 2f00d76ecc fix(rules): allow movement through friendly squares
PF1e (SRD Movement, Position and Distance): "You can move through a square occupied by a friendly character, unless you are charging." The move and run descents now route their Dijkstra cost field and greedy steps over hostile-only blocking (_blocked_hostile), so allies' squares are passable; a friendly square may only be entered when a free cell descends from it and the path tail is trimmed, so movement never ENDS on an occupied cell. Charge, withdraw, the auto 5-foot step and the Observation cost fields keep full blocking (documented). Corridor tests added; the Combat Reflexes multi-AoO transcript is unchanged. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 09:18:29 +02:00
ctan faa8a72a86 fix(rules): let a natural 20 auto-succeed on escape
PF1e (SRD Combat Maneuvers): "Rolling a natural 20 while attempting a combat maneuver is always a success (except when attempting to escape from bonds)" - escaping a grapple is a combat maneuver, not bonds. _maneuver_escape now auto-succeeds on a natural 20 below CMD (the natural 1 auto-failure is unchanged). README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 08:58:11 +02:00
ctan c96579a485 fix(rules): forbid a diagonal 5-foot step past a corner
PF1e (SRD Measuring Distance): "You can't move diagonally past a corner (even by taking a 5-foot step)." _step5_to now rejects a diagonal destination whose two flanking squares are not both passable, reusing Grid.diagonal_allowed - the same corner rule the regular movement already applies. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 08:54:36 +02:00
ctan 98b61c6cf8 fix(rules): add AC bonuses and flat-footed Dexterity to CMD
PF1e (SRD Combat Maneuver Defense): "A creature can also add any circumstance, deflection, dodge, insight, luck, morale, profane, and sacred bonuses to AC to its CMD [...] A flat-footed creature does not add its Dexterity bonus to its CMD." _maneuver_cmd now sums the target's eligible AC bonus modifiers (plus the dodge/circumstance runtime bonuses from total defense, fight defensively and aid another) and drops the Dexterity bonus (keeping any penalty) while the target denies its Dexterity to AC. README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 08:49:46 +02:00
ctan 47010c6e00 fix(rules): use Dexterity for the CMB of Tiny and smaller creatures
PF1e (SRD Combat Maneuvers): "Creatures that are size Tiny or smaller use their Dexterity modifier in place of their Strength modifier to determine their CMB." _maneuver_cmb now swaps Str for Dex when the size is Tiny, Diminutive or Fine. True 0-reach and co-occupancy for Tiny-or-smaller stay unmodelled (5-ft grid abstraction), documented in the README.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-12 08:44:44 +02:00
ctan 9d583a04c0 fix(rules): only total concealment blocks sneak attack
PF1e (CRB rogue Sneak Attack): "A rogue cannot sneak attack while
striking a creature with total concealment." _sneak_attack_dice now gates
on miss_chance >= 50 instead of any concealment > 0, so a blurred (20%)
target can still be sneak attacked while a displaced (50%) one cannot. The
test that pinned the old any-concealment denial was retargeted to total
concealment and a partial-concealment case added; README updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:41:06 +02:00
ctan a2825e73cc fix(rules): apply the power attack penalty to melee touch attacks
PF1e (CRB Power Attack): "take a -1 penalty on all melee attack rolls ...
The bonus damage does not apply to touch attacks." A melee touch (kind
"touch", no range increment) is a melee attack, so _power_attack_amt now
rides its attack and confirmation rolls; the +2X/+3X damage bonus stays
gated on kind == "melee". Ranged attacks and rays are unaffected. The
test that pinned the old melee-weapon-only simplification was rewritten to
lock the corrected behavior; README and abilities.py updated.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:35:07 +02:00
ctan 8a8b813889 fix(rules): pick the best target square for melee cover
PF1e (CRB Cover, Big Creatures and Cover): when making a melee attack
against a multi-square creature you can pick any of the squares it
occupies to determine cover. has_cover_cells/cover_bonus_cells now
aggregate the target side by the MINIMUM over target cells for melee and
keep the maximum (any line to any corner) for ranged; the attacker-side
minimum is unchanged. 1x1 geometry is bit-identical; no data case changes
today, locked by a unit test (Large target: melee 0, ranged +4).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:29:59 +02:00
ctan 843cbf881c fix(rules): suppress attacks of opportunity through cover or total concealment
PF1e (CRB Cover): "You can't execute an attack of opportunity against an
opponent with cover relative to you"; (CRB Concealment): the same holds
against total concealment. _resolve_aoo now checks terrain cover
(cover_bonus_cells, melee line) and miss_chance >= 50 before striking,
logs the suppression and leaves the per-round budget unspent. Partial
concealment (blurred 20%) and normal attacks are unaffected.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:23:11 +02:00
ctan be2a755d17 fix(rules): stop damage reduction from eating energy damage
PF1e (CRB Damage Reduction): "Damage reduction does not negate ...
energy damage dealt along with an attack." _resolve_hit_damage now
splits the post-crit total and applies DR only to the non-energy share
(physical dice + flat bonuses + precision dice); energy components are
reduced solely by the per-component energy resistance/immunity step.
DR-bypass semantics (multi-component "and" requires every component)
are unchanged. Pure-physical weapons keep bit-identical arithmetic and
the golden transcripts are untouched.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:17:34 +02:00
ctan 1862c22b7b fix(rules): apply maneuver AoO damage as a penalty to the maneuver
PF1e (CRB Combat Maneuvers): "If you are hit by the target, you take the
damage normally and apply that amount as a penalty to the attack roll to
perform the maneuver." Capture the provoked AoO's damage in
_execute_maneuver and subtract it from the check total (floored at 0;
natural 20 still succeeds, natural 1 still fails; dropping below 0 hp
still forfeits). Escape and the Improved-feat exemption are untouched.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:11:57 +02:00
ctan 74bfeb5414 fix(rules): allow one movement opportunity per opponent per round
PF1e (CRB Attacks of Opportunity): moving out of more than one square
threatened by the same opponent in the same round counts as one
opportunity for that opponent. Track per-round movement provocation in
_move_aoo_provoked (cleared with _aoo_used at round start); the stand-up
trigger passes movement=False and still provokes. Ranged, casting and
maneuver triggers are untouched.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 23:08:11 +02:00
ctan 5568e0f8af fix(rules): knock prone only on a 5-point overrun margin
PF1e CRB 'Overrun': 'If your maneuver is successful, you move through
the target's space. If your attack exceeds your opponent's CMD by 5 or
more, ... the target is knocked prone.' The engine knocked the target
prone on ANY success. A plain success (margin 0-4) now only moves the
attacker through; prone requires total - CMD >= 5 (_OVERRUN_PRONE_MARGIN).

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 22:22:57 +02:00
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
298 changed files with 36838 additions and 3261 deletions
+41 -1120
View File
File diff suppressed because it is too large Load Diff
+66 -49
View File
@@ -1,53 +1,70 @@
{
"name": "Celestial Wolf",
"level": 2,
"size": "Medium",
"abilities": {
"str_score": 13,
"dex_score": 15,
"con_score": 12,
"int_score": 2,
"wis_score": 12,
"cha_score": 6
},
"hp_max": 15,
"ac": {
"total": 14,
"touch": 13,
"flat_footed": 12
},
"bab": 1,
"initiative_mod": 2,
"speed_land_ft": 50,
"saves": {
"fort": 4,
"ref": 5,
"will": 2
},
"attacks": [
"name": "Celestial Wolf",
"alignment": "NG",
"level": 2,
"size": "Medium",
"abilities": {
"str_score": 13,
"dex_score": 15,
"con_score": 15,
"int_score": 2,
"wis_score": 12,
"cha_score": 6
},
"hp_max": 13,
"ac": {
"total": 14,
"touch": 12,
"flat_footed": 12
},
"bab": 1,
"initiative_mod": 2,
"speed_land_ft": 50,
"saves": {
"fort": 5,
"ref": 5,
"will": 1
},
"arcane_caster": false,
"armor_spell_failure": 0,
"attacks": [
{
"id": "celestial_wolf:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 2,
"damage": [
{
"id": "celestial_wolf:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 3,
"damage": [
{
"formula": "1d6+1",
"types": ["piercing"]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
"formula": "1d6+1",
"types": [
"piercing"
]
}
],
"source": "summon monster II",
"features": [],
"spells": [],
"casting_ability": "wis",
"resources": {},
"metamagic_feats": [],
"skills": {}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true
}
],
"dr": {
"amount": 5,
"bypass": ["magic"]
},
"resistances": {
"acid": 5,
"cold": 5,
"electricity": 5
},
"spell_resistance": 6,
"notes": "SRD Bestiary wolf (2d8+4 hp 13, AC 14/touch 12/flat 12, Fort +5, Will +1, bite +2 1d6+1 plus trip, Str 13 Dex 15 Con 15) + celestial template. Encoded from the template: DR 5/magic, energy resistance acid/cold/electricity 5, SR 6 (CR 1 + 5). Not encodable in this model: smite evil 2/day (+2 damage vs evil), the bite's free trip on hit (the engine has trip only as a standard-action maneuver, no auto-trip attack qualifier), and the weapon model has no magic property so no equipment bypasses the DR 5/magic (correct for mundane weapons). Summoning scaling (energy resistance +2 and SR +5 per summon spell level above 2nd) is not modeled; this sheet is the summon monster II baseline. Alignment inferred: SRD celestial template = any good; NG chosen for the good axis with neutral law axis",
"source": "summon monster II",
"features": [],
"spells": [],
"casting_ability": "wis",
"resources": {},
"metamagic_feats": [],
"skills": {}
}
+70 -43
View File
@@ -1,46 +1,73 @@
{
"name": "Giant Belostomatid",
"level": 3,
"size": "Large",
"cr": "3",
"xp": 800,
"source": "Bestiary > Giant Belostomatid",
"notes": "Digestive enzyme (DC 15 Fort or paralyzed 1d4 rounds + 1d2 Con damage/round) and grab on claws not modeled in Phase 0; Int - (mindless) modeled as 0; immune to mind-affecting not modeled; swim 40 ft / fly 20 ft (poor) not modeled, land speed only.",
"abilities": {
"str_score": 15,
"dex_score": 13,
"con_score": 16,
"int_score": 0,
"wis_score": 11,
"cha_score": 2
},
"hp_max": 30,
"ac": { "total": 15, "touch": 10, "flat_footed": 14 },
"bab": 3,
"initiative_mod": 1,
"speed_land_ft": 20,
"saves": { "fort": 7, "ref": 2, "will": 1 },
"attacks": [
"name": "Giant Belostomatid",
"alignment": "N",
"level": 3,
"size": "Large",
"cr": "3",
"xp": 800,
"source": "Bestiary > Giant Belostomatid",
"notes": "Digestive enzyme (DC 15 Fort or paralyzed 1d4 rounds + 1d2 Con damage/round) and grab on claws not modeled in Phase 0; Int - (mindless) modeled as 0; immune to mind-affecting not modeled; swim 40 ft / fly 20 ft (poor) not modeled, land speed only.",
"abilities": {
"str_score": 15,
"dex_score": 13,
"con_score": 16,
"int_score": 0,
"wis_score": 11,
"cha_score": 2
},
"hp_max": 30,
"ac": {
"total": 15,
"touch": 10,
"flat_footed": 14
},
"bab": 3,
"initiative_mod": 1,
"speed_land_ft": 20,
"saves": {
"fort": 7,
"ref": 2,
"will": 1
},
"arcane_caster": false,
"armor_spell_failure": 0,
"attacks": [
{
"name": "bite",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"name": "bite",
"kind": "melee",
"attack_bonus": 4,
"damage": [{ "formula": "1d6", "types": ["piercing"] }],
"damage_bonus": 2,
"crit_mult": 2,
"reach_ft": 10
},
{
"name": "claw",
"kind": "melee",
"attack_bonus": 4,
"damage": [{ "formula": "1d4", "types": ["slashing"] }],
"damage_bonus": 2,
"crit_mult": 2,
"count": 2,
"reach_ft": 10
"formula": "1d6",
"types": [
"piercing"
]
}
],
"cmb": 6,
"cmd": 17
}
],
"damage_bonus": 2,
"crit_mult": 2,
"reach_ft": 10,
"natural": true
},
{
"name": "claw",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": [
"slashing"
]
}
],
"damage_bonus": 2,
"crit_mult": 2,
"count": 2,
"reach_ft": 10,
"natural": true
}
],
"cmb": 6,
"cmd": 17
}
+3
View File
@@ -1,5 +1,6 @@
{
"name": "Goblin",
"alignment": "NE",
"level": 1,
"size": "Small",
"cr": "1/3",
@@ -19,6 +20,8 @@
"initiative_mod": 6,
"speed_land_ft": 30,
"saves": { "fort": 3, "ref": 2, "will": -1 },
"arcane_caster": false,
"armor_spell_failure": 0,
"attacks": [
{
"name": "short sword",
+18 -13
View File
@@ -1,31 +1,34 @@
{
"name": "Lich",
"alignment": "NE",
"level": 11,
"size": "Medium",
"creature_type": "undead",
"abilities": {
"str_score": 12,
"str_score": 10,
"dex_score": 14,
"con_score": 14,
"int_score": 20,
"con_score": 10,
"int_score": 22,
"wis_score": 14,
"cha_score": 16
},
"hp_max": 100,
"hp_max": 111,
"ac": {
"total": 25,
"total": 23,
"touch": 14,
"flat_footed": 23
"flat_footed": 21
},
"bab": 8,
"initiative_mod": 6,
"bab": 5,
"initiative_mod": 2,
"speed_land_ft": 30,
"saves": {
"fort": 10,
"ref": 10,
"will": 14
"fort": 6,
"ref": 7,
"will": 11
},
"casting_ability": "int",
"arcane_caster": true,
"armor_spell_failure": 0,
"spell_slots": {
"1": 4,
"2": 4,
@@ -39,14 +42,15 @@
"id": "lich:touch",
"name": "Paralyzing Touch",
"kind": "touch",
"attack_bonus": 10,
"attack_bonus": 5,
"natural": true,
"damage": [
{
"formula": "1d8+5",
"types": ["negative"]
}
],
"damage_bonus": 5,
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
@@ -58,6 +62,7 @@
"bypass": ["bludgeoning", "magic"]
},
"source": "bestiary",
"notes": "Stat block aligned to the KB lich entry (bestiary/lich.md): hp 111 (11d6+55 plus 15 false life), Str 10, Int 22, Init +2, fear aura 60 ft / DC 18. The spell list is the sim's own encounter-authored list (the KB's prepared list includes spells outside the data/spells registry); only the stat block was aligned.",
"features": [
"Fear Aura",
"Paralyzing Touch",
+3
View File
@@ -1,5 +1,6 @@
{
"name": "Orc",
"alignment": "CE",
"level": 1,
"size": "Medium",
"cr": "1/3",
@@ -20,6 +21,8 @@
"initiative_mod": 0,
"speed_land_ft": 30,
"saves": { "fort": 3, "ref": 0, "will": -1 },
"arcane_caster": false,
"armor_spell_failure": 0,
"attacks": [
{
"name": "falchion",
+3
View File
@@ -1,5 +1,6 @@
{
"name": "Thrune Agent",
"alignment": "LE",
"level": 8,
"size": "Medium",
"creature_type": "living",
@@ -30,6 +31,8 @@
"will": 6
},
"casting_ability": "int",
"arcane_caster": true,
"armor_spell_failure": 0,
"spell_slots": {
"1": 5,
"2": 5,
+20
View File
@@ -1,5 +1,6 @@
{
"name": "Esha Randu",
"alignment": "LG",
"level": 4,
"size": "Medium",
"abilities": {
@@ -25,6 +26,8 @@
"will": 5
},
"casting_ability": "int",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {},
"resources": {},
"attacks": [
@@ -44,6 +47,7 @@
"crit_range": 20,
"crit_mult": 3,
"reach_ft": 10,
"reach_weapon": true,
"count": 1
},
{
@@ -100,5 +104,21 @@
],
"spells": [
"Unseen Servant"
],
"carried_items": [
"Arrow",
"Blunt Arrow",
"Dye Arrow",
"Smoke Arrow",
"Tangleshot Arrow",
"Whistling Arrow",
"Thieves' Tools, common",
"Clefs de la loge",
"Badge d'éclaireur",
"Rope",
"Pomme",
"La pomme de Notch",
"Pierre de communication"
]
}
+9 -3
View File
@@ -1,5 +1,6 @@
{
"name": "Esha's Mount",
"alignment": "N",
"level": 4,
"size": "Large",
"abilities": {
@@ -32,7 +33,7 @@
"attack_bonus": 8,
"damage": [
{
"formula": "1d6",
"formula": "1d4",
"types": ["bludgeoning"]
}
],
@@ -40,9 +41,11 @@
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 10,
"count": 2
"count": 2,
"natural": true
}
],
"notes": "Light-horse mount shared with Esha's sheet: hooves are 1d4 bludgeoning (SRD Bestiary horse, light - 2 hooves 1d4), natural attacks. No bite encoded: the SRD light horse has no bite attack and the Foundry sheet lists no mount bite (unlike Jeanne's sheet, which does). Alignment inferred: SRD animal (horse) is typically neutral; no alignment on the sheet",
"source": "fiches_personnages/esha_sheet.json (mount)",
"features": [
"Combat Reflexes",
@@ -54,7 +57,10 @@
],
"spells": [],
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"resources": {},
"metamagic_feats": [],
"skills": {}
"skills": {},
"carried_items": []
}
+84 -72
View File
@@ -1,76 +1,88 @@
{
"name": "Harvie",
"level": 2,
"size": "Small",
"abilities": {
"str_score": 10,
"dex_score": 15,
"con_score": 12,
"int_score": 2,
"wis_score": 12,
"cha_score": 6
},
"hp_max": 15,
"ac": {
"total": 14,
"touch": 13,
"flat_footed": 12
},
"bab": 1,
"initiative_mod": 2,
"speed_land_ft": 10,
"speed_fly_ft": 80,
"saves": {
"fort": 4,
"ref": 5,
"will": 2
},
"name": "Harvie",
"alignment": "N",
"level": 2,
"size": "Small",
"abilities": {
"str_score": 10,
"dex_score": 15,
"con_score": 12,
"int_score": 2,
"wis_score": 14,
"cha_score": 6
},
"hp_max": 15,
"ac": {
"total": 14,
"touch": 13,
"flat_footed": 12
},
"bab": 1,
"initiative_mod": 2,
"speed_land_ft": 10,
"speed_fly_ft": 80,
"saves": {
"fort": 4,
"ref": 5,
"will": 2
},
"attacks": [
{
"id": "harvie:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 3,
"damage": [
{
"formula": "1d4",
"types": ["piercing"]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
},
{
"id": "harvie:claw",
"name": "Claw",
"kind": "melee",
"attack_bonus": 3,
"damage": [
{
"formula": "1d3",
"types": ["slashing"]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
}
{
"id": "harvie:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 2,
"damage": [
{
"formula": "1d4",
"types": [
"piercing"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true,
"secondary": true
},
{
"id": "harvie:talons",
"name": "Talons",
"kind": "melee",
"attack_bonus": 2,
"damage": [
{
"formula": "1d4",
"types": [
"slashing"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 2,
"natural": true
}
],
"source": "fiches_personnages/harvie_sheet.json",
"features": [
"Friendly Face",
"Fey-touched",
"Animal (race)"
],
"spells": [],
"casting_ability": "wis",
"spell_slots": {},
"resources": {},
"metamagic_feats": [],
"skills": {}
"source": "fiches_personnages/harvie_sheet.json",
"notes": "Feat : 'Friendly Face' est un no-op documenté (attitude initiale des animaux +2 pas, système d'attitude non modélisé).",
"features": [
"Friendly Face",
"Fey-touched",
"Animal (race)"
],
"spells": [],
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {},
"resources": {},
"metamagic_feats": [],
"skills": {},
"carried_items": []
}
+82 -54
View File
@@ -1,59 +1,87 @@
{
"name": "Ierlieth",
"level": 4,
"size": "Tiny",
"abilities": {
"str_score": 8,
"dex_score": 17,
"con_score": 11,
"int_score": 10,
"wis_score": 10,
"cha_score": 9
},
"hp_max": 29,
"ac": {
"total": 17,
"touch": 15,
"flat_footed": 14
},
"bab": 3,
"initiative_mod": 3,
"speed_land_ft": 15,
"speed_fly_ft": 40,
"saves": {
"fort": 3,
"ref": 6,
"will": 3
},
"attacks": [
"name": "Ierlieth",
"alignment": "CN",
"level": 4,
"size": "Tiny",
"abilities": {
"str_score": 8,
"dex_score": 17,
"con_score": 11,
"int_score": 10,
"wis_score": 10,
"cha_score": 9
},
"hp_max": 29,
"ac": {
"total": 17,
"touch": 15,
"flat_footed": 14
},
"bab": 3,
"initiative_mod": 3,
"speed_land_ft": 15,
"speed_fly_ft": 40,
"saves": {
"fort": 3,
"ref": 6,
"will": 3
},
"attacks": [
{
"id": "ierlieth:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"id": "ierlieth:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": ["piercing"]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
"formula": "1d4",
"types": [
"piercing"
]
}
],
"damage_bonus": -1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true
},
{
"id": "ierlieth:tail",
"name": "Tail",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": [
"bludgeoning"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true,
"secondary": true
}
],
"source": "fiches_personnages/ierlieth_sheet.json",
"features": [
"Drake (race)",
"Toughness",
"Power Attack"
],
"spells": [],
"casting_ability": "wis",
"spell_slots": {},
"resources": {},
"metamagic_feats": [],
"skills": {}
"source": "fiches_personnages/ierlieth_sheet.json",
"features": [
"Drake (race)",
"Toughness",
"Power Attack"
],
"spells": [],
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {},
"resources": {},
"metamagic_feats": [],
"skills": {},
"carried_items": []
}
+27 -3
View File
@@ -1,5 +1,6 @@
{
"name": "Jeanne",
"alignment": "N",
"level": 4,
"size": "Large",
"abilities": {
@@ -32,7 +33,7 @@
"attack_bonus": 8,
"damage": [
{
"formula": "1d6",
"formula": "1d4",
"types": ["bludgeoning"]
}
],
@@ -40,7 +41,27 @@
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 10,
"count": 2
"count": 2,
"natural": true
},
{
"id": "jeanne:bite",
"name": "Bite",
"kind": "melee",
"attack_bonus": 8,
"damage": [
{
"formula": "1d3",
"types": ["piercing"]
}
],
"damage_bonus": 2,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true,
"secondary": true
}
],
"source": "fiches_personnages/jeanne_sheet.json",
@@ -54,8 +75,11 @@
],
"spells": [],
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {},
"resources": {},
"metamagic_feats": [],
"skills": {}
"skills": {},
"carried_items": ["Saddle (riding)", "Feed", "Trail Rations"]
}
+183 -161
View File
@@ -1,166 +1,188 @@
{
"name": "Misty",
"level": 4,
"size": "Medium",
"abilities": {
"str_score": 12,
"dex_score": 19,
"con_score": 14,
"int_score": 16,
"wis_score": 7,
"cha_score": 18
},
"hp_max": 31,
"ac": {
"total": 20,
"touch": 14,
"flat_footed": 16
},
"bab": 3,
"initiative_mod": 4,
"speed_land_ft": 30,
"saves": {
"fort": 7,
"ref": 9,
"will": 3
},
"casting_ability": "cha",
"spell_slots": {
"1": 4,
"2": 3
},
"resources": {
"bardic_rounds": 12
},
"attacks": [
"name": "Misty",
"alignment": "NE",
"level": 4,
"size": "Medium",
"abilities": {
"str_score": 12,
"dex_score": 19,
"con_score": 14,
"int_score": 16,
"wis_score": 7,
"cha_score": 18
},
"hp_max": 31,
"ac": {
"total": 20,
"touch": 14,
"flat_footed": 16
},
"bab": 3,
"initiative_mod": 4,
"speed_land_ft": 30,
"saves": {
"fort": 7,
"ref": 9,
"will": 3
},
"casting_ability": "cha",
"arcane_caster": true,
"armor_spell_failure": 0,
"spell_slots": {
"1": 4,
"2": 3
},
"resources": {
"bardic_rounds": 12
},
"attacks": [
{
"name": "MWK Rapier",
"kind": "melee",
"attack_bonus": 8,
"damage": [
{
"name": "MWK Rapier",
"kind": "melee",
"attack_bonus": 8,
"damage": [
{
"formula": "1d6",
"types": [
"piercing"
]
}
],
"damage_bonus": 1,
"crit_range": 18,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
},
{
"name": "Dagger",
"kind": "melee",
"attack_bonus": 7,
"damage": [
{
"formula": "1d4",
"types": [
"piercing"
]
}
],
"damage_bonus": 1,
"crit_range": 19,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 10
},
{
"name": "Talons (attaque naturelle)",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": [
"slashing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
},
{
"name": "Bite (attaque naturelle)",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": [
"piercing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
},
{
"name": "Unarmed Strike",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d3",
"types": [
"bludgeoning"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
"formula": "1d6",
"types": [
"piercing"
]
}
],
"source": "fiches_personnages/misty_sheet.json",
"features": [
"Harmonic Spell",
"Weapon Finesse",
"Axe to Grind (combat)",
"Spell Vulnerability (divination; drawback)",
"Born Damned (race: tiefling)",
"Gifted Adept (dispel magic; magic)",
"Prehensile Tail (tiefling daemon-spawn)",
"Scaled Skin (electricity) (tiefling daemon-spawn)",
"Maw or Claw (Bite) (tiefling daemon-spawn)"
],
"spells": [
"Prestidigitation",
"Oath of Anonymity",
"Detect Magic",
"Read Magic",
"Disrupt Undead",
"Ray of Frost",
"Summon Instrument",
"Mage Hand",
"Open/Close",
"Arcane Mark",
"Acid Splash",
"Grasp",
"Shield",
"Cure Light Wounds",
"Color Spray",
"Saving Finale",
"Vanish",
"Shocking Grasp",
"Burning Hands",
"Alarm",
"Scorching Ray",
"Bladed Dash",
"Mirror Image",
"Heroism"
],
"damage_bonus": 1,
"crit_range": 18,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
},
{
"name": "Dagger",
"kind": "melee",
"attack_bonus": 7,
"damage": [
{
"formula": "1d4",
"types": [
"piercing"
]
}
],
"damage_bonus": 1,
"crit_range": 19,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 10
},
{
"name": "Talons (attaque naturelle)",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d4",
"types": [
"slashing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true
},
{
"name": "Bite (attaque naturelle)",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d6",
"types": [
"piercing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true
},
{
"name": "Unarmed Strike",
"kind": "melee",
"attack_bonus": 4,
"damage": [
{
"formula": "1d3",
"types": [
"bludgeoning"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
}
],
"source": "fiches_personnages/misty_sheet.json",
"notes": "Traits : 'Born Damned' est un no-op documenté (+2 sauvegardes vs malédictions, descripteur 'curse' absent du moteur) ; 'Axe to Grind' est un no-op documenté (+1 dégâts vs ennemi menacé seulement par soi, condition non exprimable) ; 'Scaled Skin (electricity)' est encodé dans resistances (électricité 5, SRD : remplace Fiendish Resistance, donc pas de résistance froid/feu). 'Maw or Claw' : la fiche Foundry note la morsure « 1d4 piercing estimé » (item sans stats, estimation de l'auteur, non délibérée) ; le SRD (ARG, trait tieffelin daemon-spawn) accorde une morsure 1d6 à une créature Medium - l'encodage suit le SRD (1d6) et consigne l'écart avec la fiche.",
"features": [
"Harmonic Spell",
"Weapon Finesse",
"Axe to Grind (combat)",
"Spell Vulnerability (divination; drawback)",
"Born Damned (race: tiefling)",
"Gifted Adept (dispel magic; magic)",
"Prehensile Tail (tiefling daemon-spawn)",
"Scaled Skin (electricity) (tiefling daemon-spawn)",
"Maw or Claw (Bite) (tiefling daemon-spawn)",
"Plot Armor"
],
"spells": [
"Prestidigitation",
"Oath of Anonymity",
"Detect Magic",
"Read Magic",
"Disrupt Undead",
"Ray of Frost",
"Summon Instrument",
"Mage Hand",
"Open/Close",
"Arcane Mark",
"Acid Splash",
"Grasp",
"Shield",
"Cure Light Wounds",
"Color Spray",
"Saving Finale",
"Vanish",
"Shocking Grasp",
"Burning Hands",
"Alarm",
"Scorching Ray",
"Bladed Dash",
"Mirror Image",
"Heroism"
],
"resistances": {
"electricity": 5
},
"carried_items": [
"Pouch, Spell Component",
"Tools, Thieves'",
"Thieves' Tools, masterwork",
"Clef de la Loge de Sauerton",
"+1 Mithral Arrow",
"Bouteille de ??? à moitié vide",
"Scroll of Detect Secret Doors",
"Scroll of Cure Light Wounds",
"Wand of Infernal Healing"
]
}
+13
View File
@@ -1,5 +1,6 @@
{
"name": "Nairda Guisenda",
"alignment": "CN",
"level": 4,
"size": "Medium",
"abilities": {
@@ -25,6 +26,8 @@
"will": 7
},
"casting_ability": "int",
"arcane_caster": true,
"armor_spell_failure": 0,
"spell_slots": {
"1": 4,
"2": 3
@@ -114,5 +117,15 @@
"Ice Slick",
"Darkness",
"Flurry of Snowballs"
],
"carried_items": [
"Spell Component Pouch",
"Holy symbol (Wood)",
"Arrow",
"Lettres dans une chambre de bandits",
"Clefs de la loge",
"Badge des éclaireurs",
"Potion de soin de Nairda"
]
}
+138 -121
View File
@@ -1,126 +1,143 @@
{
"name": "Oni Triumvir",
"level": 4,
"size": "Medium",
"abilities": {
"str_score": 12,
"dex_score": 14,
"con_score": 14,
"int_score": 12,
"wis_score": 18,
"cha_score": 16
},
"hp_max": 36,
"ac": {
"total": 19,
"touch": 18,
"flat_footed": 17
},
"bab": 4,
"initiative_mod": 2,
"speed_land_ft": 30,
"saves": {
"fort": 7,
"ref": 7,
"will": 9
},
"casting_ability": "wis",
"spell_slots": {
"1": 4,
"2": 3
},
"resources": {
"channel_energy": 6
},
"attacks": [
"name": "Oni Triumvir",
"alignment": "NG",
"level": 4,
"size": "Medium",
"abilities": {
"str_score": 12,
"dex_score": 14,
"con_score": 14,
"int_score": 12,
"wis_score": 18,
"cha_score": 16
},
"hp_max": 36,
"ac": {
"total": 19,
"touch": 18,
"flat_footed": 17
},
"bab": 4,
"initiative_mod": 2,
"speed_land_ft": 30,
"saves": {
"fort": 7,
"ref": 7,
"will": 9
},
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {
"1": 4,
"2": 3
},
"resources": {
"channel_energy": 6
},
"attacks": [
{
"name": "Longbow",
"kind": "ranged",
"attack_bonus": 6,
"damage": [
{
"name": "Longbow",
"kind": "ranged",
"attack_bonus": 6,
"damage": [
{
"formula": "1d8",
"types": [
"piercing"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 3,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 100
},
{
"name": "Alchemist's Fire",
"kind": "touch",
"attack_bonus": 6,
"damage": [
{
"formula": "1d6",
"types": [
"fire"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 10
},
{
"name": "Attaques naturelles (Shifter Claws / formes fées)",
"kind": "melee",
"attack_bonus": 5,
"damage": [
{
"formula": "1d6",
"types": [
"slashing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1
"formula": "1d8",
"types": [
"piercing"
]
}
],
"source": "fiches_personnages/oni_sheet.json",
"notes": "Armes placeholder Foundry sans dégâts encodés omises : 'une Pioche' (non maîtrisée) et 'Weapon' (à remplacer par la vraie arme).",
"features": [
"Summon Good Monster",
"Sacred Summons",
"Selective Channeling",
"Extremely Fashionable",
"Exalted of the Society (Cleric, Pathfinder Society)",
"Bitter",
"Ambitious"
],
"spells": [
"Guidance",
"Detect Magic",
"Stabilize",
"Read Magic",
"Detect Poison",
"Purify Food and Drink",
"Summon Monster I",
"Shield of Faith",
"Forbid Action",
"Cure Light Wounds",
"Hidden Diplomacy",
"Protection from Evil",
"Bless",
"Summon Monster II",
"Cure Moderate Wounds",
"Sound Burst",
"Suppress Charms and Compulsions",
"Delay Poison",
"Ironskin",
"Restoration, Lesser"
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 3,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 100
},
{
"name": "Alchemist's Fire",
"kind": "touch",
"attack_bonus": 6,
"damage": [
{
"formula": "1d6",
"types": [
"fire"
]
}
],
"damage_bonus": 0,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 10,
"splash": {
"damage": 1,
"type": "fire"
}
},
{
"name": "Attaques naturelles (Shifter Claws / formes fées)",
"kind": "melee",
"attack_bonus": 5,
"damage": [
{
"formula": "1d6",
"types": [
"slashing"
]
}
],
"damage_bonus": 1,
"crit_range": 20,
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"natural": true
}
],
"source": "fiches_personnages/oni_sheet.json",
"notes": "Armes placeholder Foundry sans dégâts encodés omises : 'une Pioche' (non maîtrisée) et 'Weapon' (à remplacer par la vraie arme). Traits : 'Exalted of the Society' accorde +1 canalisation d'énergie/jour (channel_energy 6 -> 7, fusionné par le chargeur) ; 'Extremely Fashionable' est un no-op documenté (+1 Bluff/Diplomacy/Intimidate en habits fins, état vestimentaire non modélisé) ; 'Bitter' (drawback) réduit de 1 les soins reçus d'un allié (comportement moteur).",
"features": [
"Summon Good Monster",
"Sacred Summons",
"Selective Channeling",
"Extremely Fashionable",
"Exalted of the Society (Cleric, Pathfinder Society)",
"Bitter",
"Ambitious"
],
"spells": [
"Guidance",
"Detect Magic",
"Stabilize",
"Read Magic",
"Detect Poison",
"Purify Food and Drink",
"Summon Monster I",
"Shield of Faith",
"Forbid Action",
"Cure Light Wounds",
"Hidden Diplomacy",
"Protection from Evil",
"Bless",
"Summon Monster II",
"Cure Moderate Wounds",
"Sound Burst",
"Suppress Charms and Compulsions",
"Delay Poison",
"Ironskin",
"Restoration, Lesser"
],
"carried_items": [
"Arrow",
"Holy symbol (Wood)",
"Clefs de la loge",
"Eh je suis Éclaireuse maintenant",
"Kit, Fishing",
"Alchemist's Fire"
]
}
+32 -1
View File
@@ -1,5 +1,6 @@
{
"name": "Tammara Cailean",
"alignment": "CG",
"level": 4,
"size": "Medium",
"abilities": {
@@ -25,6 +26,8 @@
"will": 8
},
"casting_ability": "wis",
"arcane_caster": false,
"armor_spell_failure": 0,
"spell_slots": {
"1": 4,
"2": 3
@@ -108,7 +111,11 @@
"crit_mult": 2,
"reach_ft": 5,
"count": 1,
"range_increment_ft": 10
"range_increment_ft": 10,
"splash": {
"damage": 1,
"type": "fire"
}
},
{
"name": "Unarmed Strike",
@@ -130,6 +137,7 @@
}
],
"source": "fiches_personnages/tammara_sheet.json",
"notes": "Traits : 'Fortified Drinker' est un no-op documenté (+2 sauvegardes vs effets mentaux après alcool, état d'ébriété non modélisé) ; 'Lonely' (drawback) applique -2 Sense Motive et -2 sauvegardes vs charme (mind_affecting remplace charme).",
"features": [
"Weapon Focus (Pistol)",
"Rapid Reload (Pistol)",
@@ -202,5 +210,28 @@
"Delay Poison",
"Aid",
"Drunkard's Breath"
],
"carried_items": [
"Mess Kit",
"Gunsmith's Kit",
"Holy symbol (Wood)",
"Spell component pouch",
"Powder horn",
"Firearm Bullet",
"Alchemical Cartridge (Paper, Bullet Or Pellet)",
"Black Powder (Dose)",
"Matériaux pour munitions",
"Rope",
"Bouteille d'alcool (poste des bandits)",
"Clefs de la loge (4)",
"Black Powder (Dose) en stock",
"Firearm Bullet en stock",
"Alchemical Cartridge (Paper, Bullet Or Pellet) en stock",
"Alcool de poison d'araignée ogre",
"Badge des éclaireurs",
"Bouteille d'alcool fort (genre plus que du désinfectant)",
"Une médaille de Caiden Cailean",
"Potion of Cure Light Wounds"
]
}
+6 -3
View File
@@ -1,10 +1,13 @@
{
"name": "Abundant Ammunition",
"level": 0,
"school": "transmutation",
"level": 1,
"school": "conjuration",
"components": "V, S, M/DF",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip that conjures duplicate ammunition: attacks with the affected ammunition do not expend it. No combat-mechanical effect in the engine (ammunition is not tracked)."
"description": "A 1st-level spell that conjures duplicate ammunition: attacks with the affected ammunition do not expend it. No combat-mechanical effect in the engine (ammunition is not tracked)."
}
+14 -3
View File
@@ -2,16 +2,27 @@
"name": "Acid Arrow",
"level": 2,
"school": "evocation",
"range": "medium",
"components": "V, S, M, F",
"range": "long",
"save": "none",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "damage",
"formula": "2d4",
"types": ["acid"],
"half_on_save": false
"half_on_save": false,
"recurring": {
"formula": "2d4",
"types": ["acid"],
"base_rounds": 1,
"per_levels": 3,
"max": 7
}
}
],
"description": "An arrow of acid springs from your hand and speeds to its target, dealing 2d4 points of acid damage. No save."
"description": "SRD spells/acid-arrow.md: ranged touch attack dealing 2d4 acid damage, plus another 2d4 acid damage at the start of each of the target's turns for 1 round + 1 round per three caster levels (maximum 6 ADDITIONAL rounds at 18th level, i.e. 7 recurring rounds total). Recurring damage is reduced by energy resistance/immunity, not by DR. No save."
}
+4
View File
@@ -2,9 +2,13 @@
"name": "Acid Splash",
"level": 0,
"school": "conjuration",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "damage",
+8 -4
View File
@@ -1,19 +1,23 @@
{
"name": "Aid",
"level": 1,
"level": 2,
"school": "enchantment",
"range": "long",
"components": "V, S, DF",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "attack", "value": 1, "bonus_type": "morale" },
{ "target": "will", "value": 1, "bonus_type": "morale" }
{ "target": "fort", "value": 1, "bonus_type": "morale", "condition": "fear" },
{ "target": "ref", "value": 1, "bonus_type": "morale", "condition": "fear" },
{ "target": "will", "value": 1, "bonus_type": "morale", "condition": "fear" }
],
"duration_rounds": 10
}
],
"description": "The subject gains a +1 morale bonus on attack rolls and on saving throws against fear effects (modeled as Will). Duration 10 min/level approximated as 10 rounds."
"description": "SRD spells/aid.md: the subject gains a +1 morale bonus on attack rolls and on saves against fear effects, plus temporary hit points equal to 1d8 + caster level (maximum 1d8+10). The save bonus is scoped to fear effects via the descriptor-conditioned modifiers (all three saves, replacing the previous unconditional Will-only approximation). The temporary hit points are deferred: CombatantState has no temp-HP track (same gap as the virtue cantrip). Duration 1 min/level approximated as 10 rounds."
}
+4 -2
View File
@@ -2,9 +2,11 @@
"name": "Air Bubble",
"level": 1,
"school": "conjuration",
"range": "personal",
"components": "S, M/DF",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "You create a sphere of air around your head (or a touched creature's) that lasts 10 min/level, allowing breathing underwater. No combat-mechanical effect in the engine."
"description": "SRD spells/air-bubble.md: range touch - a small pocket of breathable air surrounds the touched creature's head (or a touched object) for 1 min./level. No combat-mechanical effect in the engine."
}
+4 -1
View File
@@ -2,9 +2,12 @@
"name": "Alarm",
"level": 1,
"school": "abjuration",
"range": "medium",
"components": "V, S, F/DF",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "Wards a area (20-ft radius, 20 ft high) against intrusion for 2 hours/level, alerting you mentally. No combat-mechanical effect in the engine."
}
+4 -1
View File
@@ -1,10 +1,13 @@
{
"name": "Arcane Mark",
"level": 0,
"school": "divination",
"school": "universal",
"components": "V, S",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "Creates a permanent visible inscribed mark of your choosing on an object or surface, optionally invisible and keyed to you. Utility cantrip, no combat effect."
}
+17 -6
View File
@@ -2,16 +2,27 @@
"name": "Bane",
"level": 1,
"school": "enchantment",
"components": "V, S, DF",
"range": "medium",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "shaken",
"duration_rounds": 10,
"save_negates": true
"type": "area",
"radius_ft": 50,
"targets": "enemies",
"centered_on_caster": true,
"save_for_half": false,
"effects": [
{
"type": "condition",
"condition_name": "bane",
"duration_rounds": 10,
"save_negates": true
}
]
}
],
"description": "Enemies within range take a -1 penalty on attack rolls and saving throws (approximated with the shaken condition, -2). Will negates. Concentration 1 min/level approximated as 10 rounds."
}
"description": "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. Modeled with the dedicated bane condition (untyped -1 on attacks, -1 on saves conditioned on the fear descriptor) - the previous shaken (-2) approximation overcounted the penalty. Duration 1 min/level approximated as 10 rounds."
}
+14 -4
View File
@@ -2,18 +2,28 @@
"name": "Bear's Endurance",
"level": 2,
"school": "transmutation",
"components": "V, S, M/DF",
"range": "touch",
"save": "fort",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "fort", "value": 2, "bonus_type": "enhancement" },
{ "target": "hp", "value": 2, "bonus_type": "enhancement" }
{ "target": "fort", "value": 2, "bonus_type": "enhancement" }
],
"duration_rounds": 60
},
{
"type": "buff",
"modifiers": [
{ "target": "hp", "value": 0, "bonus_type": "enhancement" }
],
"duration_rounds": 60,
"scaling_flat": { "amount": 2, "per_levels": 1, "max": 40 }
}
],
"description": "The subject gains a +4 enhancement bonus to Constitution, granting +2 Fortitude and +2 hit points (approximated as +2/+2). Fortitude negates (harmless). Duration 10 min/level approximated as 60 rounds."
"description": "SRD spells/bear-s-endurance.md: the subject gains a +4 enhancement bonus to Constitution - +2 to Fortitude saves and +2 hit points per Hit Die - for 10 min./level. The engine has no ability-score track, so the +4 Con is encoded as its combat consequences: an unscaled +2 Fortitude enhancement, and a separate hp modifier carrying the per-HD bonus via flat scaling (+2 per level, capped at +40 for level 20; caster level stands in for the target's Hit Dice, and the hp target is informational - the engine has no per-HD hp pool). Will negates (harmless). Duration 10 min/level approximated as 60 rounds."
}
+5 -3
View File
@@ -1,10 +1,12 @@
{
"name": "Bladed Dash",
"level": 1,
"level": 2,
"school": "transmutation",
"range": "close",
"components": "V",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
@@ -14,5 +16,5 @@
"duration_rounds": 1
}
],
"description": "You move up your speed as a move action and make one melee attack at the end. Simplified as a +30 ft speed buff for 1 round; the extra attack is not modeled."
"description": "Upon casting, you immediately move up to 30 feet in a straight line in any direction; this movement does not provoke attacks of opportunity, and you may make a single melee attack at your highest base attack bonus against any creature you are adjacent to at any point along this 30 feet (SRD bladed dash). Simplified as a +30 ft speed buff for 1 round; the free melee attack, the no-AoO movement, the straight-line constraint and the circumstance attack bonus are not modeled."
}
+6 -3
View File
@@ -2,16 +2,19 @@
"name": "Bleed",
"level": 0,
"school": "necromancy",
"components": "V, S",
"range": "close",
"save": "fort",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "bleeding",
"duration_rounds": 1,
"save_negates": true
"save_negates": true,
"requires_dying": true
}
],
"description": "A cantrip that causes a wound to bleed: the target takes 1 point of damage per round until healed. Fortitude negates. Modeled as the bleeding condition for 1 round."
"description": "SRD spells/bleed.md: you target one living creature that has -1 or fewer hit points; it begins dying, taking 1 point of damage per round; Will negates. Gated on the target's hit points (requires_dying): a creature at 0 hp or above is not a valid target and the spell has no effect. Modeled as the bleeding condition for 1 round with bleed_damage = 1 (the engine's bleed tick deals it at the start of the target's turn)."
}
+6 -2
View File
@@ -2,13 +2,17 @@
"name": "Bless",
"level": 1,
"school": "enchantment",
"components": "V, S, DF",
"range": "medium",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "area",
"radius_ft": 20,
"radius_ft": 50,
"targets": "allies",
"centered_on_caster": true,
"save_for_half": false,
"effects": [
{
@@ -22,5 +26,5 @@
]
}
],
"description": "All allies within 20 ft/level (modeled as a 20-ft burst applied to all creatures in area) gain a +1 morale bonus on attack rolls and on saving throws against fear effects (modeled as Will). Duration 1 min/level approximated as 10 rounds."
"description": "The caster and all allies within a 50-ft burst centered on the caster gain a +1 morale bonus on attack rolls and on saving throws against fear effects (modeled as Will). Duration 1 min/level approximated as 10 rounds."
}
+4 -2
View File
@@ -2,9 +2,11 @@
"name": "Blindness/Deafness",
"level": 2,
"school": "necromancy",
"components": "V",
"range": "medium",
"save": "will",
"save": "fort",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
@@ -13,5 +15,5 @@
"save_negates": true
}
],
"description": "The target becomes blind or deaf (modeled as blinded) until removed by a caster of higher level. Permanent duration approximated as 100 rounds. Will negates."
"description": "The target becomes blind or deaf (modeled as blinded) until removed by a caster of higher level. Permanent duration approximated as 100 rounds. Fortitude negates."
}
+8 -4
View File
@@ -2,18 +2,22 @@
"name": "Bull's Strength",
"level": 2,
"school": "transmutation",
"components": "V, S, M/DF",
"range": "touch",
"save": "fort",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "attack", "value": 1, "bonus_type": "enhancement" },
{ "target": "damage", "value": 1, "bonus_type": "enhancement" }
{ "target": "attack", "value": 2, "bonus_type": "enhancement" },
{ "target": "damage", "value": 2, "bonus_type": "enhancement" },
{ "target": "cmb", "value": 2, "bonus_type": "enhancement" }
],
"duration_rounds": 60
}
],
"description": "The subject gains a +4 enhancement bonus to Strength, granting +2 attack and +2 damage (approximated as +1/+1). Fortitude negates (harmless)."
"description": "The subject gains a +4 enhancement bonus to Strength, granting +2 to melee attack rolls, melee damage rolls, and combat maneuver checks (SRD bull's strength). Will negates (harmless)."
}
+17 -6
View File
@@ -2,16 +2,27 @@
"name": "Burning Hands",
"level": 1,
"school": "evocation",
"range": "touch",
"components": "V, S",
"range": "close",
"save": "ref",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "1d4+1",
"types": ["fire"],
"half_on_save": true
"type": "area",
"shape": "cone",
"radius_ft": 15,
"save_for_half": true,
"effects": [
{
"type": "damage",
"formula": "0",
"types": ["fire"],
"half_on_save": true,
"scaling": { "dice": "1d4", "per_levels": 1, "max_dice": 5 }
}
]
}
],
"description": "A cone of searing flame shoots from your fingertips, dealing 1d4+1 points of fire damage per caster level (max 5d4+5). Reflex save for half."
"description": "SRD spells/burning-hands.md: a 15-ft. cone of searing flame dealing 1d4 points of fire damage per caster level (maximum 5d4); Reflex half. The SRD range is 15 ft. - modeled as the closest category (close, 25 ft. at low levels)."
}
+4 -2
View File
@@ -2,9 +2,11 @@
"name": "Charm Person",
"level": 1,
"school": "enchantment",
"range": "medium",
"components": "V, S",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
@@ -13,5 +15,5 @@
"save_negates": true
}
],
"description": "The charmed person regards the caster as a trusted friend (modeled as the fascinated condition; the charmed condition is not in the engine). Duration 1 hour/level approximated as 100 rounds. Will negates."
"description": "SRD spells/charm-person.md: range close (25 ft. + 5 ft./2 levels) - corrected from the previous medium approximation. The charmed person regards the caster as a trusted friend; the engine has no charmed condition, so it is modeled as fascinated (the subject takes no actions). Duration 1 hour/level approximated as 100 rounds. Will negates."
}
+5 -2
View File
@@ -1,13 +1,16 @@
{
"name": "Color Spray",
"level": 1,
"school": "enchantment",
"school": "illusion",
"components": "V, S, M",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "area",
"shape": "cone",
"radius_ft": 15,
"save_for_half": false,
"effects": [
@@ -20,5 +23,5 @@
]
}
],
"description": "A cone of chaotic colors (modeled as a 15-ft burst) blinds and stuns creatures with fewer HD than your level for 1d4 rounds (approximated as 1 round of stunned). Will negates."
"description": "SRD spells/color-spray.md: a 15-ft. cone of chaotic colors blinds and stuns creatures with fewer HD than your level for 1d4 rounds (approximated as 1 round of stunned; the HD tiers are not modeled). Will negates."
}
+4 -2
View File
@@ -1,10 +1,12 @@
{
"name": "Comprehend Languages",
"level": 0,
"level": 1,
"school": "divination",
"components": "V, S, M/DF",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip that lets you understand the meaning of any spoken or written language you encounter for 1 min/level. Utility spell, no combat effect."
"description": "A 1st-level spell that lets you understand the meaning of any spoken or written language you encounter for 1 min/level. Utility spell, no combat effect."
}
+1
View File
@@ -5,6 +5,7 @@
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip (Create Alcohol, spelled as on the PC sheet) that transforms one liquid per level into an alcoholic drink of equal volume. Utility spell, no combat effect."
}
+5 -2
View File
@@ -1,10 +1,13 @@
{
"name": "Create Water",
"level": 1,
"level": 0,
"school": "conjuration",
"range": "medium",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "Conjures 4 gallons of clean water per level in an open space or container. Utility spell, no combat effect."
}
+6 -2
View File
@@ -2,14 +2,18 @@
"name": "Cure Light Wounds",
"level": 1,
"school": "conjuration",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "heal",
"formula": "1d8+1"
"formula": "1d8",
"scaling_flat": { "amount": 1, "per_levels": 1, "max": 5 }
}
],
"description": "Cures 1d8+1 points of damage. Will save negates (harmless)."
"description": "Cures 1d8 points of damage +1 point per caster level (maximum +5). Will half (harmless); since undead are powered by negative energy, the spell deals damage to them instead (Will half, SRD cure light wounds)."
}
+6 -2
View File
@@ -2,14 +2,18 @@
"name": "Cure Moderate Wounds",
"level": 2,
"school": "conjuration",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "heal",
"formula": "2d8+3"
"formula": "2d8",
"scaling_flat": { "amount": 1, "per_levels": 1, "max": 10 }
}
],
"description": "Cures 2d8+3 points of damage. Will save negates (harmless)."
"description": "Cures 2d8 points of damage +1 point per caster level (maximum +10). Will half (harmless); since undead are powered by negative energy, the spell deals damage to them instead (Will half, SRD cure moderate wounds)."
}
+5 -2
View File
@@ -1,10 +1,13 @@
{
"name": "Dancing Lights",
"level": 0,
"school": "conjuration",
"range": "close",
"school": "evocation",
"components": "V, S",
"range": "medium",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip that creates up to four flickering torch-like lights you move as desired (concentration, 1 min/level). Utility spell, no combat effect."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Darkness",
"level": 2,
"school": "evocation",
"components": "V, M/DF",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "area",
+3 -1
View File
@@ -2,9 +2,11 @@
"name": "Daze",
"level": 0,
"school": "enchantment",
"components": "V, S, M",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
@@ -13,5 +15,5 @@
"save_negates": true
}
],
"description": "A cantrip that clouds the mind of a humanoid creature with 5 or fewer HD, making it unable to act for 1 round. Will negates."
"description": "SRD spells/daze.md: a cantrip that clouds the mind of a humanoid creature with 4 HD or fewer, making it unable to act for 1 round. Humanoids of 5 or more HD are not affected. Will negates."
}
+6 -4
View File
@@ -1,10 +1,12 @@
{
"name": "Deja Vu",
"level": 0,
"school": "divination",
"range": "personal",
"level": 1,
"school": "enchantment",
"components": "V",
"range": "medium",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip that lets you reroll one attack roll, saving throw, or skill check made in the past round (once per casting). Not modeled by the engine's action flow, so it carries no mechanical effect."
"description": "A 1st-level spell that lets you reroll one attack roll, saving throw, or skill check made in the past round (once per casting). Not modeled by the engine's action flow, so it carries no mechanical effect."
}
+6 -3
View File
@@ -1,10 +1,13 @@
{
"name": "Delay Disease",
"level": 3,
"school": "abjuration",
"level": 2,
"school": "conjuration",
"components": "V, S, DF",
"range": "touch",
"save": "fort",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "You suppress the progression of one disease the subject carries for 1 hour/level, keeping it dormant. Utility spell, no combat-mechanical effect in the engine. Fortitude negates (harmless)."
"description": "SRD spells/delay-disease.md: conjuration (healing), level 2 for nearly every divine/prime class (witch 1 for ratfolk); the engine pins the common level 2. The target becomes temporarily immune to disease for 1 day. Utility spell, no combat-mechanical effect in the engine. Fortitude negates (harmless)."
}
+4 -1
View File
@@ -1,10 +1,13 @@
{
"name": "Delay Poison",
"level": 2,
"school": "abjuration",
"school": "conjuration",
"components": "V, S, DF",
"range": "touch",
"save": "fort",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "You temporarily stave off incoming poison or eliminate one already in the subject's system for 1 hour/level. Utility spell, no combat-mechanical effect in the engine. Fortitude negates (harmless)."
}
@@ -2,9 +2,12 @@
"name": "Detect Fiendish Presence",
"level": 0,
"school": "divination",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip that reveals whether a creature of the outsider (evil) subtype has been in the area within 10 minutes/HD, and hints at its nature. Utility spell, no combat effect."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Detect Magic",
"level": 0,
"school": "divination",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip that detects magical auras in a 60-ft cone (2 rounds to identify strength and school). Utility spell, no combat effect."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Detect Poison",
"level": 0,
"school": "divination",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip that determines whether a creature, object, or area is poisoned, venomous, or afflicted by acid. Utility spell, no combat effect."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Disrupt Undead",
"level": 0,
"school": "necromancy",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"effects": [
{
"type": "damage",
+14 -2
View File
@@ -2,9 +2,21 @@
"name": "Divine Favor",
"level": 1,
"school": "evocation",
"components": "V, S, DF",
"range": "personal",
"save": "none",
"base_dc": 10,
"effects": [],
"description": "You draw upon divine power for 1 round/level: a +1 luck bonus on attack rolls and weapon damage per 4 caster levels. Simplified: the engine has no luck-bonus self-buff that preserves the pinned default-policy casting order, so no mechanical effect is applied."
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "attack", "value": 1, "bonus_type": "luck" },
{ "target": "damage", "value": 1, "bonus_type": "luck" }
],
"duration_rounds": 10,
"scaling_flat": { "amount": 1, "per_levels": 3, "max": 2 }
}
],
"description": "SRD spells/divine-favor.md: evocation, level 1 (cleric/inquisitor/oracle/paladin/warpriest), range personal, duration 1 minute. You gain a +1 luck bonus on attack and weapon damage rolls, +1 for every three caster levels (at least +1, maximum +3). The bonus does not apply to spell damage - the engine's 'damage' modifier target only rides weapon damage rolls, so that clause holds by construction. Duration 1 minute = 10 rounds."
}
+5 -2
View File
@@ -2,16 +2,19 @@
"name": "Doom",
"level": 1,
"school": "necromancy",
"range": "close",
"components": "V, S, DF",
"range": "medium",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "shaken",
"duration_rounds": 10,
"duration_rounds_per_level": 10,
"save_negates": true
}
],
"description": "The target is paralyzed with fear and becomes shaken (-2 on attacks and saves; on a successful save it takes -1 instead, not modeled). Will negates. Duration 1 min/level approximated as 10 rounds."
"description": "SRD spells/doom.md: the subject is filled with horrible dread and becomes shaken (-2 on attacks and saves). Saving Throw Will negates - there is no partial success (the earlier '-1 on a successful save' wording was a D&D 3.5 rule that does not exist in PF1e). Duration 1 min/level (10 rounds per level)."
}
+24 -6
View File
@@ -1,10 +1,28 @@
{
"name": "Drunkard's Breath",
"level": 1,
"school": "abjuration",
"range": "personal",
"save": "none",
"level": 2,
"school": "conjuration",
"components": "V, S, M",
"range": "close",
"save": "fort",
"base_dc": 10,
"effects": [],
"description": "For 1 hour/level you gain DR 5/- against alcohol effects and cannot become drunk. The engine has no DR buff target, so no mechanical effect is applied."
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "area",
"shape": "cone",
"radius_ft": 30,
"save_for_half": false,
"effects": [
{
"type": "condition",
"condition_name": "nauseated",
"duration_rounds": 4,
"save_negates": true
}
]
}
],
"description": "SRD spells/drunkards-breath.md: bard 2, cleric 2, oracle 2, skald 2, warpriest 2; conjuration (creation). A cone-shaped burst emanating from your mouth functions like stinking cloud: living creatures in the area become nauseated (Fortitude negates; this is a poison effect). Spell Resistance: no. The SRD range is 30 ft. - modeled as the closest category (close). Duration 1 round/level approximated as 4 rounds (the SRD nausea lasts 1d4+1 rounds after leaving the cloud; fixed duration is the engine convention, cf. fear)."
}
+4 -2
View File
@@ -2,9 +2,11 @@
"name": "Endure Elements",
"level": 1,
"school": "abjuration",
"range": "personal",
"components": "V, S",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "For 24 hours you tolerate comfortable extremes of heat and cold (from -50 to 140 degrees F) without ill effect. Environmental protection, no combat-mechanical effect in the engine."
"description": "SRD spells/endure-elements.md: range touch - the creature touched suffers no harm from hot or cold environments (from -50 to 140 degrees F) for 24 hours. Environmental protection, no combat-mechanical effect in the engine."
}
+7 -4
View File
@@ -1,10 +1,13 @@
{
"name": "Enhance Water",
"level": 2,
"school": "abjuration",
"range": "personal",
"save": "none",
"level": 1,
"school": "transmutation",
"components": "V, S",
"range": "touch",
"save": "fort",
"harmless": true,
"base_dc": 10,
"cast_time": "one_round",
"effects": [],
"description": "You purify and bless 1 gallon/level of water (or one vial of holy/unholy water). Utility spell, no combat-mechanical effect in the engine."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Enhanced Diplomacy",
"level": 0,
"school": "divination",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: the subject gains a +2 competence bonus on a single Diplomacy or Intimidate check, declared before the roll (duration 1 minute or until discharged). Will negates (harmless). Social skill effect only: the engine does not model skill-check modifiers, so the spell has no mechanical effects here."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Face of the Devourer",
"level": 1,
"school": "transmutation",
"components": "V, S",
"range": "touch",
"save": "fort",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "The target's face becomes hideous: it gains a +4 circumstance bonus on Intimidate checks and, if it lacks one, a bite attack (1d6 for Medium) for 1 min/level. Fortitude negates (harmless). The skill bonus and the granted natural attack are not modeled by the engine, so the spell has no mechanical effects here."
}
+17 -6
View File
@@ -2,16 +2,27 @@
"name": "Fear",
"level": 4,
"school": "necromancy",
"range": "medium",
"components": "V, S, M",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "frightened",
"duration_rounds": 4,
"save_negates": true
"type": "area",
"shape": "cone",
"radius_ft": 30,
"save_for_half": false,
"effects": [
{
"type": "condition",
"condition_name": "panicked",
"duration_rounds": 4,
"save_negates": true,
"on_success": { "condition_name": "shaken", "duration_rounds": 1 }
}
]
}
],
"description": "The affected creature becomes frightened and flees from the caster. Will negates."
"description": "SRD spells/fear.md: each living creature in the 30-ft. cone becomes panicked unless it succeeds on a Will save; a creature that succeeds on the save is instead shaken for 1 round (partial save). Modeled as panicked for 4 rounds on a failed save, shaken for 1 round on a successful save. The SRD range is 30 ft. - modeled as the closest category (close)."
}
+6 -3
View File
@@ -2,9 +2,11 @@
"name": "Fireball",
"level": 3,
"school": "evocation",
"components": "V, S, M",
"range": "long",
"save": "ref",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "area",
@@ -13,12 +15,13 @@
"effects": [
{
"type": "damage",
"formula": "6d6",
"formula": "0",
"types": ["fire"],
"half_on_save": true
"half_on_save": true,
"scaling": { "dice": "1d6", "per_levels": 1, "max_dice": 10 }
}
]
}
],
"description": "A glowing bead streaks from you and bursts into a 20-foot radius explosion of fire, dealing 6d6 points of fire damage. Reflex save for half."
"description": "A glowing bead streaks from you and bursts into a 20-foot radius explosion of fire, dealing 1d6 points of fire damage per caster level (maximum 10d6). Reflex save for half."
}
+6 -2
View File
@@ -2,13 +2,17 @@
"name": "Flurry of Snowballs",
"level": 2,
"school": "evocation",
"components": "V, S",
"range": "close",
"save": "ref",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "area",
"radius_ft": 20,
"shape": "cone",
"radius_ft": 30,
"save_for_half": true,
"effects": [
{
@@ -20,5 +24,5 @@
]
}
],
"description": "A flurry of snowballs hurtles at your foes: any creature in the area takes 4d6 points of cold damage. Reflex half. The RAW 30-ft cone-shaped burst is approximated as a 20-ft burst centered on the target point."
"description": "SRD spells/flurry-of-snowballs.md: any creature in the 30-ft. cone-shaped burst takes 4d6 points of cold damage. Reflex half. The SRD range is 30 ft. - modeled as the closest category (close)."
}
+2
View File
@@ -2,9 +2,11 @@
"name": "Forbid Action",
"level": 1,
"school": "enchantment",
"components": "V",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
+3
View File
@@ -2,9 +2,12 @@
"name": "Grasp",
"level": 0,
"school": "divination",
"components": "V",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "immediate",
"spell_resistance": false,
"effects": [],
"description": "A cantrip cast as an immediate action when you fail a Climb check and would fall: you immediately retry the Climb check at a -2 penalty (increasing by 2 per successive use in the same situation); on success you don't fall. Climbing is not modeled by the engine, so the spell has no mechanical effects here."
}
+13 -2
View File
@@ -2,9 +2,20 @@
"name": "Guidance",
"level": 0,
"school": "divination",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "A cantrip: the subject gains a +1 competence bonus on a single attack roll, saving throw, or skill check, declared before the roll (duration 1 minute or until discharged). Will negates (harmless). Single-roll discharge is not modeled by the engine, so the spell has no mechanical effects here."
"cast_time": "standard",
"effects": [
{
"type": "one_shot",
"track": "attack",
"value": 1,
"bonus_type": "competence",
"duration_rounds": 10
}
],
"description": "SRD spells/guidance.md: a cantrip - the subject gets a +1 competence bonus on a SINGLE attack roll, saving throw, or skill check, declared before the roll (duration 1 minute or until discharged). Modeled as a one-shot +1 competence modifier on the ATTACK track: the subject's next weapon attack roll (resolve_attack) discharges it and the bonus rides that roll only, not the crit-confirm roll; spell touch-delivery attacks roll on a separate path and do not consume it. The SRD 'attack OR save OR skill' choice has no declaration channel in the engine, so the attack-roll variant is encoded (the mechanism also carries save and skill tracks). 1 minute = 10 rounds, expiring at the start of the bearer's turns like any duration-tracked effect. The bonus folds into the resolve_modifiers pool, so it stacks with other types but not with a higher competence bonus on the same roll. default_policy never casts it (cantrips have no spell slot, and the one-shot type is not a buff/heal/offensive pick)."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Heroism",
"level": 2,
"school": "enchantment",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
+2
View File
@@ -2,9 +2,11 @@
"name": "Hex Vulnerability",
"level": 1,
"school": "necromancy",
"components": "V, S, M",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "The target becomes susceptible to a repeat use of your harmful hexes for 1 round/level, bypassing the once-per-day targeting limit. Will negates. No combat effect: the hex-reuse permission is outside the engine's model, so the spell has no mechanical effects here."
}
+2
View File
@@ -2,9 +2,11 @@
"name": "Hidden Diplomacy",
"level": 1,
"school": "enchantment",
"components": "V, S, M",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "As part of casting, you attempt a single Intimidate check to make the target act friendly; its memory of the threats is clouded, so its attitude does not worsen afterward. Will negates. Social effect only: no combat mechanics are modeled."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Hide from Undead",
"level": 1,
"school": "abjuration",
"components": "V, S, DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "Undead cannot perceive warded creatures (nonintelligent undead ignore them automatically; intelligent undead get a single Will save) until the subject attacks, channels energy, or touches undead. Duration 10 min/level. Saving throw entries here cover the harmless-target save; the undead-perception effect is not modeled by the engine."
}
+5 -2
View File
@@ -2,16 +2,19 @@
"name": "Hold Person",
"level": 3,
"school": "enchantment",
"components": "V, S, F/DF",
"range": "medium",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "paralyzed",
"duration_rounds": 3,
"duration_rounds": 1,
"duration_rounds_per_level": 1,
"save_negates": true
}
],
"description": "The subject becomes paralyzed and freezes in place. Will negates."
"description": "The subject becomes paralyzed and freezes in place. Will negates. Duration 1 round/level."
}
+5 -2
View File
@@ -2,9 +2,11 @@
"name": "Ice Slick",
"level": 2,
"school": "evocation",
"components": "V, S",
"range": "close",
"save": "ref",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "area",
@@ -15,7 +17,8 @@
"type": "damage",
"formula": "1d6",
"types": ["cold"],
"half_on_save": true
"half_on_save": true,
"scaling_flat": { "amount": 1, "per_levels": 1, "max": 10 }
},
{
"type": "condition",
@@ -26,5 +29,5 @@
]
}
],
"description": "A blast of intense cold coats a 5-ft-radius burst in ice: creatures in the area take 1d6 points of cold damage (Reflex half) and fall prone (no prone on a successful save). The lingering slippery surface (half speed, Acrobatics checks) is not modeled."
"description": "SRD spells/ice-slick.md: a blast of intense cold coats a 5-ft-radius burst in ice: creatures in the area take 1d6 points of cold damage + 1 point per caster level (maximum +10) and fall prone; a successful Reflex save halves the damage and avoids falling prone. The lingering slippery surface (half speed, Acrobatics checks) is not modeled."
}
+2
View File
@@ -2,9 +2,11 @@
"name": "Ill Omen",
"level": 1,
"school": "enchantment",
"components": "V, S, M",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "The target is afflicted with bad luck: on its next d20 roll (one per 5 caster levels, up to five) it must roll twice and take the less favorable result; duration 1 round/level. No saving throw; spell resistance applies. The forced worst-of-two reroll mechanic is not modeled by the engine, so the spell has no mechanical effects here."
}
+6 -3
View File
@@ -2,16 +2,19 @@
"name": "Inflict Light Wounds",
"level": 1,
"school": "necromancy",
"components": "V, S",
"range": "touch",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "1d8+1",
"formula": "1d8",
"types": ["negative"],
"half_on_save": true
"half_on_save": true,
"scaling_flat": { "amount": 1, "per_levels": 1, "max": 5 }
}
],
"description": "Touching a creature channels negative energy, dealing 1d8 points of damage + 1 per caster level (maximum +5); at caster level 1 this is modeled as 1d8+1. Will half. Undead are instead cured of a like amount of damage - the engine only models the damaging side against living targets."
"description": "SRD spells/inflict-light-wounds.md: touching a creature channels negative energy, dealing 1d8 points of damage + 1 point per caster level (maximum +5). Will half. Undead are instead cured of a like amount of damage - the engine only models the damaging side against living targets."
}
+2
View File
@@ -2,9 +2,11 @@
"name": "Ironskin",
"level": 2,
"school": "transmutation",
"components": "V, S, DF/M",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "buff",
+3
View File
@@ -2,9 +2,12 @@
"name": "Light",
"level": 0,
"school": "evocation",
"components": "V, M/DF",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip: a touched object glows like a torch, shedding normal light in a 20-foot radius (plus dim light for 20 more feet) for 10 min/level. No combat effect; illumination is not modeled by the engine."
}
+16 -5
View File
@@ -2,16 +2,27 @@
"name": "Lightning Bolt",
"level": 3,
"school": "evocation",
"components": "V, S, M",
"range": "medium",
"save": "ref",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "6d6",
"types": ["electricity"],
"half_on_save": true
"type": "area",
"shape": "line",
"radius_ft": 120,
"save_for_half": true,
"effects": [
{
"type": "damage",
"formula": "0",
"types": ["electricity"],
"half_on_save": true,
"scaling": { "dice": "1d6", "per_levels": 1, "max_dice": 10 }
}
]
}
],
"description": "You release a powerful stroke of electrical energy, dealing 6d6 points of electricity damage. Reflex save for half."
"description": "SRD spells/lightning-bolt.md: a 120-ft. line dealing 1d6 points of electricity damage per caster level (maximum 10d6). Reflex half. The line starts at the caster's cell (the bolt begins at your fingertips), so the caster's own square is in the area."
}
+21
View File
@@ -0,0 +1,21 @@
{
"name": "Mage Armor",
"level": 1,
"school": "conjuration",
"components": "V, S, F",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "ac", "value": 4, "bonus_type": "armor" }
],
"duration_rounds": 600
}
],
"description": "An invisible but tangible field of force surrounds the subject, providing a +4 armor bonus to AC (SRD mage armor: school conjuration (creation) [force], range touch, duration 1 hour/level (modeled as a fixed 600 rounds), spell resistance: no; Will negates (harmless) is not modeled - the engine only self/target-buffs willing recipients). Known simplification: the engine bakes worn armor into the AC total of the JSON and cannot see it, so this armor bonus stacks additively with that baked-in armor value instead of replacing it; two mage armors still do not stack with each other (armor is a non-stacking bonus type)."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Mage Hand",
"level": 0,
"school": "transmutation",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip: a spectral hand lifts and moves one unattended nonmagical object weighing up to 5 lb. from up to 25 ft. away, as far as 15 feet per move action while you concentrate. No combat effect; object manipulation is not modeled by the engine."
}
+5 -2
View File
@@ -2,16 +2,19 @@
"name": "Magic Missile",
"level": 1,
"school": "evocation",
"components": "V, S",
"range": "medium",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "1d4+1",
"types": ["force"],
"half_on_save": false
"half_on_save": false,
"projectiles": { "base": 1, "per_levels": 2, "at_level": 1, "max": 5 }
}
],
"description": "A missile of magical energy darts forth from your fingertip and strikes its target, dealing 1d4+1 points of force damage."
"description": "SRD spells/magic-missile.md: unerring force missiles, 1d4+1 points of force damage each. One missile, plus one additional missile per two caster levels beyond 1st (two at 3rd, three at 5th, four at 7th, maximum five at 9th or higher). Auto-hit (no attack roll); spell resistance applies to the target."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Make Whole",
"level": 2,
"school": "transmutation",
"components": "V, S",
"range": "close",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "long",
"effects": [],
"description": "Repairs 1d6 points of damage per caster level (maximum 5d6) to one object or construct creature (10-minute casting). Will negates (harmless, object). Object/construct repair is outside the engine's combat model, so the spell has no mechanical effects here."
}
@@ -2,9 +2,12 @@
"name": "Masterwork Transformation",
"level": 2,
"school": "transmutation",
"components": "V, S, M",
"range": "touch",
"save": "none",
"base_dc": 10,
"cast_time": "long",
"spell_resistance": false,
"effects": [],
"description": "After 1 hour of casting (plus masterwork-priced reagents), a non-masterwork weapon, armor, shield, tool, or skill kit becomes its masterwork equivalent. Item crafting is outside the engine's combat model, so the spell has no mechanical effects here."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Mending",
"level": 0,
"school": "transmutation",
"components": "V, S",
"range": "close",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "long",
"effects": [],
"description": "A cantrip: after 10 minutes, the spell repairs 1d4 hit points of damage to one object (up to 1 lb./level), removing the broken condition if restored to at least half HP. Will negates (harmless, object). Object repair is outside the engine's combat model, so the spell has no mechanical effects here."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Message",
"level": 0,
"school": "transmutation",
"components": "V, S, F",
"range": "medium",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip: you whisper messages and receive replies with targeted creatures within range for 10 min/level; bystanders hear nothing without a DC 25 Perception check. Communication is not modeled by the engine, so the spell has no mechanical effects here."
}
+10 -2
View File
@@ -2,16 +2,24 @@
"name": "Mirror Image",
"level": 2,
"school": "illusion",
"components": "V, S",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "blurred",
"duration_rounds": 10,
"save_negates": false
"duration_rounds_per_level": 10,
"save_negates": false,
"images": {
"dice": "1d4",
"per_levels": 3,
"max": 8
}
}
],
"description": "Illusory doubles (1d4 + 1 per 3 caster levels, max 8) occupy your space, making attacks risk striking a figment instead. Duration 1 min/level approximated as 10 rounds. Simplification: the image-destruction mechanic is approximated as the blurred condition (20% miss chance); the real spell can deflect attacks entirely until the images are used up."
"description": "SRD spells/mirror-image.md: 1d4 illusory doubles plus one per three caster levels (maximum eight total) inhabit your space. The image count is rolled at cast time and folded into the engine's concealment machinery as a dynamic miss chance of images/(images+1) on the blurred carrier condition (an attack that would hit strikes a figment instead). Duration 1 min./level scaled as 10 rounds per caster level. Simplification: the figment-destruction track (a hit or a near miss by 5 or less destroys one image; area and no-attack-roll effects ignore them) is not modeled - the miss chance persists for the whole duration."
}
+2
View File
@@ -2,9 +2,11 @@
"name": "Oath of Anonymity",
"level": 0,
"school": "illusion",
"components": "V",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: you swear an oath of secrecy - your voice, body language and gear are magically distorted, granting a +10 bonus on Disguise checks to conceal your identity (not to impersonate a specific person) for 10 min/level. Social skill effect only: the engine does not model skill-check modifiers, so the spell has no mechanical effects here."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Obscuring Mist",
"level": 1,
"school": "conjuration",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "area",
+2
View File
@@ -2,9 +2,11 @@
"name": "Open/Close",
"level": 0,
"school": "transmutation",
"components": "V, S, F",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: you open or close one unsecured object or portal weighing up to 30 lb. at up to 25 ft. away (fails if a bar or lock resists). Will negates (object). Object manipulation is not modeled by the engine, so the spell has no mechanical effects here."
}
+13 -2
View File
@@ -2,9 +2,20 @@
"name": "Owl's Wisdom",
"level": 2,
"school": "transmutation",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "The subject gains a +4 enhancement bonus to Wisdom for 1 min/level, adding the usual benefit to Wisdom-based skills, Will saves and spell DCs. Will negates (harmless). Ability-score increases are not modeled by the engine, so the spell has no mechanical effects here."
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "will", "value": 2, "bonus_type": "enhancement" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/owl-s-wisdom.md: the subject gains a +4 enhancement bonus to Wisdom for 1 min./level. The engine has no ability-score track, so the +4 Wis is encoded as its combat-visible consequence: a +2 enhancement bonus on Will saves (half of +4, as a score modifier feeds saves at half). Wisdom-based skills and spell DCs are not modeled. Will negates (harmless). Duration 1 min/level approximated as 10 rounds."
}
+4 -1
View File
@@ -2,9 +2,12 @@
"name": "Prestidigitation",
"level": 0,
"school": "universal",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "A cantrip: minor magical tricks for 1 hour - lift 1 lb. of material, color/clean/soil items in a 1-ft cube, chill/warm/flavor 1 lb. of nonliving material, or create crude trinkets. It can deal no damage and duplicates no other spell. No combat effect; the trivial manipulations are not modeled by the engine."
"description": "SRD spells/prestidigitation.md: a cantrip - minor magical tricks for 1 hour; lift 1 lb. of material, color/clean/soil items in a 1-ft cube, chill/warm/flavor 1 lb. of nonliving material, or create crude trinkets. It deals no damage and duplicates no other spell. The SRD range is 10 ft. - the engine has no 10-ft category, so the closest usable category (close) is kept. No combat effect; the trivial manipulations are not modeled by the engine."
}
+17 -2
View File
@@ -2,9 +2,24 @@
"name": "Protection from Evil",
"level": 1,
"school": "abjuration",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "A barrier wards the subject from evil for 1 min/level: +2 deflection bonus to AC and +2 resistance bonus on saves against attacks and effects created by evil creatures, renewed saves and suppression of mental control, and lawful summoned evil creatures cannot touch it. Will negates (harmless). The engine has no alignment system, so alignment-restricted bonuses cannot be modeled and the spell has no mechanical effects here."
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "ac", "value": 2, "bonus_type": "deflection", "condition": "attacker_is_evil" },
{ "target": "fort", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" },
{ "target": "ref", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" },
{ "target": "will", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/protection-from-evil.md: a barrier wards the subject from evil for 1 min./level. The subject gains a +2 deflection bonus to AC and a +2 resistance bonus on saves - the exact SRD wording: \"+2 deflection bonus to AC and a +2 resistance bonus on saves against attacks or effects created by evil creatures\" - so both bonuses are gated on the attacker/caster alignment (condition attacker_is_evil) and never apply against a creature that is not evil. Will negates (harmless), spell resistance no. Duration 1 min/level approximated as 10 rounds (engine convention for 1-minute durations). Not modeled: the SRD's second effect (immediate renewed saving throw with a +2 morale bonus against mental-control spells and effects, suppression for the duration, immunity to new attempts) - the engine has no mental-control tracking to suppress; and the third effect (evil summoned creatures cannot touch the warded creature) - the engine has no summoned-subtype contact restriction."
}
+17 -2
View File
@@ -2,9 +2,24 @@
"name": "Protection from Evil, Communal",
"level": 2,
"school": "abjuration",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "Functions like protection from evil (+2 deflection to AC and +2 resistance on saves vs evil creatures, protection from mental control), except the 1 min/level duration is divided in 1-minute intervals among the touched creatures. Will negates (harmless). Alignment-restricted bonuses and duration-sharing are not modeled, so the spell has no mechanical effects here."
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "ac", "value": 2, "bonus_type": "deflection", "condition": "attacker_is_evil" },
{ "target": "fort", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" },
{ "target": "ref", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" },
{ "target": "will", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_evil" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/protection-from-evil-communal.md: functions like protection from evil - each touched creature gains the +2 deflection bonus to AC and +2 resistance bonus on saves against attacks or effects created by evil creatures (gated on the attacker/caster alignment via condition attacker_is_evil, never applying against a non-evil source). Will negates (harmless), spell resistance no. Not modeled: the SRD duration split - \"you divide the duration in 1-minute intervals among the creatures touched\" - the engine gives every touched creature the same fixed duration as protection from evil (1 min/level approximated as 10 rounds each) instead of draining one shared pool; and the mental-control suppression clause of the base spell (see protection_from_evil.json)."
}
+17 -2
View File
@@ -2,9 +2,24 @@
"name": "Protection from Law",
"level": 1,
"school": "abjuration",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "A barrier wards the subject from lawful creatures for 1 min/level: +2 deflection bonus to AC and +2 resistance bonus on saves against attacks and effects created by lawful creatures, plus renewed saves against mental control by lawful creatures. Will negates (harmless). The engine has no alignment system, so alignment-restricted bonuses cannot be modeled and the spell has no mechanical effects here."
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "ac", "value": 2, "bonus_type": "deflection", "condition": "attacker_is_lawful" },
{ "target": "fort", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" },
{ "target": "ref", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" },
{ "target": "will", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/protection-from-law.md: functions like protection from evil, except the deflection and resistance bonuses apply against lawful creatures - \"+2 deflection bonus to AC and a +2 resistance bonus on saves against attacks or effects created by lawful creatures\" - so both bonuses are gated on the attacker/caster alignment (condition attacker_is_lawful) and never apply against a creature that is not lawful. Will negates (harmless), spell resistance no. Duration 1 min/level approximated as 10 rounds (engine convention for 1-minute durations). Not modeled: the renewed saving throw against mental control by lawful creatures and the lawful summoned creature contact restriction (same gaps as protection from evil)."
}
+17 -2
View File
@@ -2,9 +2,24 @@
"name": "Protection from Law, Communal",
"level": 2,
"school": "abjuration",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "Functions like protection from law (+2 deflection to AC and +2 resistance on saves vs lawful creatures, protection from mental control), except the 1 min/level duration is divided in 1-minute intervals among the touched creatures. Will negates (harmless). Alignment-restricted bonuses and duration-sharing are not modeled, so the spell has no mechanical effects here."
"cast_time": "standard",
"spell_resistance": false,
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "ac", "value": 2, "bonus_type": "deflection", "condition": "attacker_is_lawful" },
{ "target": "fort", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" },
{ "target": "ref", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" },
{ "target": "will", "value": 2, "bonus_type": "resistance", "condition": "attacker_is_lawful" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/protection-from-law-communal.md: functions like protection from law - each touched creature gains the +2 deflection bonus to AC and +2 resistance bonus on saves against attacks or effects created by lawful creatures (gated on the attacker/caster alignment via condition attacker_is_lawful, never applying against a non-lawful source). Will negates (harmless), spell resistance no. Not modeled: the SRD duration split - \"you divide the duration in 1-minute intervals among the creatures touched\" - the engine gives every touched creature the same fixed duration as protection from law (1 min/level approximated as 10 rounds each) instead of draining one shared pool; and the mental-control suppression clause of the base spell (see protection_from_law.json)."
}
+3 -1
View File
@@ -2,9 +2,11 @@
"name": "Purify Food and Drink",
"level": 0,
"school": "transmutation",
"components": "V, S",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: cleanses 1 cu. ft./level of spoiled, rotten, diseased or poisoned food and water, making it pure and drinkable (no effect on creatures or magic potions). Will negates (object). Non-combat utility: the engine does not model objects, so the spell has no mechanical effects here."
"description": "SRD spells/purify-food-and-drink.md: a cantrip - cleanses 1 cu. ft./level of spoiled, rotten, diseased or poisoned food and water (no effect on creatures or magic potions). Will negates (object). The SRD range is 10 ft. - the engine has no 10-ft category, so the closest usable category (close) is kept. Non-combat utility: the engine does not model objects, so the spell has no mechanical effects here."
}
+3 -1
View File
@@ -2,9 +2,11 @@
"name": "Putrefy Food and Drink",
"level": 0,
"school": "transmutation",
"components": "V, S",
"range": "close",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: instantly rots 1 cu. ft./level of edible food and makes water and liquids brackish and undrinkable (can also spoil a single potion; no effect on creatures or unholy water). Will negates (object). Non-combat utility: the engine does not model objects, so the spell has no mechanical effects here."
"description": "SRD spells/putrefy-food-and-drink.md: a cantrip - instantly rots 1 cu. ft./level of edible food and makes water and liquids brackish and undrinkable (can also spoil a single potion; no effect on creatures or unholy water). Will negates (object). The SRD range is 10 ft. - the engine has no 10-ft category, so the closest usable category (close) is kept. Non-combat utility: the engine does not model objects, so the spell has no mechanical effects here."
}
+11 -6
View File
@@ -2,16 +2,21 @@
"name": "Ray of Enfeeblement",
"level": 1,
"school": "necromancy",
"components": "V, S",
"range": "close",
"save": "fort",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"effects": [
{
"type": "condition",
"condition_name": "fatigued",
"duration_rounds": 4,
"save_negates": true
"type": "penalty",
"formula": "1d6",
"scaling_flat": { "amount": 1, "per_levels": 2, "max": 5 },
"targets": ["attack", "damage"],
"duration_rounds_per_level": 1,
"half_on_save": true
}
],
"description": "A coruscating ray strikes the target (ranged touch attack), dealing 1d6 points of Strength damage +1 per two caster levels (maximum 1d6+5); a successful Fortitude save halves the damage. Simplification: the engine has no ability damage, so the Strength penalty is modeled as the fatigued condition (attack/damage penalties, no charge) for 4 rounds (1 round/level at caster level 4); Fortitude halves is approximated as Fortitude negates."
}
"description": "A coruscating ray strikes the target (ranged touch attack), dealing 1d6 points of Strength damage +1 per two caster levels (maximum 1d6+5); a successful Fortitude save halves the damage. Simplification: the engine has no ability-score track, so the Strength penalty is modeled as negative untyped modifiers on the attack and damage rolls (the two combat rolls Strength feeds) for 1 round per caster level; the SRD 'cannot drop below 1' floor and the non-stacking clause are not modeled."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Ray of Frost",
"level": 0,
"school": "evocation",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"effects": [
{
"type": "damage",
+2
View File
@@ -2,9 +2,11 @@
"name": "Read Magic",
"level": 0,
"school": "divination",
"components": "V, S, F",
"range": "personal",
"save": "none",
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "A cantrip: you can decipher magical inscriptions on objects that would otherwise be unintelligible, at one page per minute, and you can thereafter read that particular writing without the spell. Duration 10 min./level. No combat effect; reading magical writing is not modeled by the engine."
}
+15 -2
View File
@@ -2,9 +2,22 @@
"name": "Resistance",
"level": 0,
"school": "abjuration",
"components": "V, S, M/DF",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"effects": [],
"description": "A cantrip: the subject gains a +1 resistance bonus on saving throws for 1 minute. Will negates (harmless). Simplified: the engine has no resistance bonus type and no self-buff encoding that preserves the pinned default-policy casting order, so no mechanical effect is applied."
"cast_time": "standard",
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "fort", "value": 1, "bonus_type": "resistance" },
{ "target": "ref", "value": 1, "bonus_type": "resistance" },
{ "target": "will", "value": 1, "bonus_type": "resistance" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/resistance.md: a cantrip - the subject gains a +1 resistance bonus on saving throws for 1 minute. Will negates (harmless). Modeled as a BuffEffect granting +1 resistance to fort/ref/will for 10 rounds (1 minute); the resistance bonus type does not stack with itself, so a second Resistance (or a cloak of resistance) keeps the higher value. default_policy never casts it: cantrips have no spell slot, and _find_buff_spell skips spells the caster has no slot for, so Resistance cannot preempt a leveled buff (Divine Favor) in the known-spell order."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Restoration, Lesser",
"level": 2,
"school": "conjuration",
"components": "V, S",
"range": "touch",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "long",
"effects": [],
"description": "Casting time 3 rounds. Dispels any magical effects reducing one of the subject's ability scores or cures 1d4 points of temporary ability damage to one ability score, and eliminates any fatigue (improving exhausted to fatigued). Will negates (harmless). No combat effect encoded: the engine has no ability damage, fatigue-from-spell, or magical-effect-reduction state to restore."
}
+3
View File
@@ -2,9 +2,12 @@
"name": "Sanctuary",
"level": 1,
"school": "abjuration",
"components": "V, S, DF",
"range": "touch",
"save": "will",
"base_dc": 10,
"cast_time": "standard",
"spell_resistance": false,
"effects": [],
"description": "You ward a creature against attacks for 1 round/level: any opponent attempting to directly attack the warded creature must succeed on a Will save or be unable to follow through with that attack and cannot directly attack the subject for the duration. The subject cannot attack without ending the spell. Will negates. No combat effect encoded: the engine has no attack-refusal-by-save mechanic, so the protection cannot be represented."
}
+4 -1
View File
@@ -2,9 +2,12 @@
"name": "Saving Finale",
"level": 1,
"school": "evocation",
"components": "V, S",
"range": "close",
"save": "will",
"harmless": true,
"base_dc": 10,
"cast_time": "immediate",
"effects": [],
"description": "Immediate action, requires an active bardic performance: when a creature affected by your performance fails a saving throw, end the performance to let it immediately reroll the failed save and take the new result. Will negates (harmless). No combat effect encoded: conditional save rerolls tied to bardic performance are not modeled by the engine."
"description": "SRD spells/saving-finale.md: immediate action, requires an active bardic performance - when a creature affected by your bardic performance fails a saving throw, end the performance to let it immediately reroll the failed save and take the new result. Will negates (harmless). Deferred: rerolling an already-resolved save needs a post-save reaction hook plus a bardic-performance resource gate (the engine's saves are one-shot resolutions with no reroll window), a cross-cutting redesign. No combat effect encoded; the immediate casting time is kept for data fidelity."
}
+6 -2
View File
@@ -2,16 +2,20 @@
"name": "Scorching Ray",
"level": 2,
"school": "evocation",
"components": "V, S",
"range": "close",
"save": "none",
"base_dc": 10,
"requires_ranged_touch": true,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "4d6",
"types": ["fire"],
"half_on_save": false
"half_on_save": false,
"projectiles": { "base": 1, "per_levels": 4, "at_level": 3, "max": 3 }
}
],
"description": "You blast your enemy with a searing beam of fire: each ray requires a ranged touch attack and deals 4d6 points of fire damage. You fire one ray, plus one additional ray per four levels beyond 3rd (max three at 11th) — the engine models a single ray. No save."
"description": "SRD spells/scorching-ray.md: a searing beam of fire. One ray, plus one additional ray for every four caster levels beyond 3rd (to a maximum of three rays at 11th level). Each ray requires a ranged touch attack to hit and deals 4d6 points of fire damage. No save."
}
+20 -2
View File
@@ -2,9 +2,27 @@
"name": "Shard of Chaos",
"level": 2,
"school": "evocation",
"components": "V, S, DF",
"range": "close",
"save": "will",
"base_dc": 10,
"effects": [],
"description": "A dart-shaped projectile of chaotic energy (ranged touch attack): lawful creatures take 1d8 per two caster levels (max 5d8); lawful outsiders take 1d6 per caster level (max 10d6) and are slowed 1 round. A successful Will save halves the damage and negates the slow. Half damage to neutral creatures, no effect on chaotic ones. No combat effect encoded: the engine has no alignment, so the alignment-gated damage cannot be represented."
"requires_ranged_touch": true,
"cast_time": "standard",
"effects": [
{
"type": "damage",
"formula": "0",
"types": ["force"],
"half_on_save": true,
"scaling": { "dice": "1d8", "per_levels": 2, "max_dice": 5 },
"alignment_gate": {
"axis": "law_chaos",
"immune": "chaotic",
"full": "lawful",
"full_condition": "staggered",
"full_condition_rounds": 1
}
}
],
"description": "SRD spells/shard-of-chaos.md: evocation [chaotic], level 2 (cleric/oracle/warpriest), range close, dart-shaped projectile of chaotic energy delivered as a ranged touch attack, Will partial, spell resistance yes. A lawful creature struck takes 1d8 points of damage per two caster levels (maximum 5d8) and, on a failed Will save, is slowed for 1 round; a successful Will save halves the damage and negates the slow. The bolt has no effect on chaotic creatures (alignment_gate immune) and deals only half damage to creatures neither lawful nor chaotic, without the slow (alignment_gate neutral halving). Simplifications: the SRD's slowed condition is mapped to the engine's staggered condition (the SRD slow entry states a slowed creature is staggered; the -1 penalty and half-speed parts of slow are not modeled); the lawful-outsider variant (1d6 per caster level, maximum 10d6) is not modeled (the engine has no outsider subtype); force damage stands in for chaotic energy (no energy type models it)."
}

Some files were not shown because too many files have changed in this diff Show More