Epic: spell system — server-paced power-word duel (vertical slices) #36

Closed
opened 2026-07-17 17:51:24 +02:00 by marco · 4 comments
Owner

Epic tracking the spell system — the agreed player-facing design is docs/game-design/systems/spell-system.md (produced by the game-design interview + a 4-lens critical panel). This epic's original sketch ("Vulgare Arcano", a 118-word compositional formula language submitted atomically) is superseded: the interview converged on a different, tighter model whose core value it keeps.

The agreed design (one-liner)

You don't cast fixed spells — you speak them one power word at a time, in public, at a server-paced rhythm (~0.75s/word, tick-driven). Combat is a real-time duel of reading, predicting, and bluffing your opponent's incantation. Element first (XEN fire / GHY water), form second (attack-bolt / shield / heal); ~7 words in the MVP. Full spec + six-lens review + player-observable DoD in the design doc.

Why the pivot: atomic formula submission kills the word-by-word tell and the bluff/feint the duel is built on; and the emergent 118-word grammar was deliberately scoped down to 5 fixed valid combos. The design doc is the source of truth.

Technical direction (server-authoritative)

  • New World-owned SpellSystem component (no lock — the single sim thread serializes it, #54): current mana + regen, the in-progress incantation FSM, shield stance, cooldowns, known-words. World.cs only delegates (HARD GATE).
  • The client sends only intents (Cast with a pre-picked target, CancelCast); the server paces word emission, charges mana (no refund), broadcasts PowerWordSpoken via AoI (not the chat/ChatRateLimiter path). A modified client cannot rush the sequence → the public tell + anti-bot property always hold.
  • Reuses what exists: TargetValidator (LOS+range), the PendingSwing/_pending "queue-now-resolve-at-a-future-tick" idiom, skills-as-data (add Magery), AoI broadcast, the damage-number pipeline, GM-spawn for the training dummy.
  • Current mana is persisted with the vitals (like current HP — versioned save-format bump; old saves default to full). Correction to the doc's earlier "transient" note.
  • Combat model = the agreed middle way: resolution in a system, per-type reaction hooks (Mobile.OnHit) introduced only when a type must react differently (shield-absorb / disrupt); MVP keeps shield state in SpellSystem.
  • No strict determinism (friends-scale; simple rng).

Implementation slices (each its own green PR)

  • Slice 0 — resource & skill foundation: persist current vitals (HP + mana) in the save format; add mutable current-mana + regen in a new SpellSystem stepped from the tick; add the Magery skill. Server-only, unit-tested. (in progress)
  • Slice 1 — one instant bolt: Cast(fire, attack, target), re-validate LOS+range at a resolve tick → fizzle, elemental damage, mana spend, Magery gain, PowerWordSpoken + damage number. Proves cast→target→resolve→damage against the dummy.
  • Slice 2 — the incantation FSM: server-paced multi-word emission (queue/emit/2s-timeout/cancel/feint) + shield + heal + the 5 valid combos + the "fire cannot heal" invalid-combo cue.
  • Slice 3 — the mage/warrior triangle: per-mobile move speed → cast-walk slow; melee disrupt (cross-domain); the 3-word silence interrupt.
  • Slice 4 — client UI: self-HUD (mana + building incantation + timer), enemy ribbon, parchment-plaque overhead words, Elements/Forms palette, legend + practice mode.

Server slices (0–3) land before UI (4) so each is testable headless against World.

Definition of Done

The player-observable DoD is the one in docs/game-design/systems/spell-system.md (duel with visible word-by-word tells at a rhythm no client can rush; feint; cancel + 2s reset; targeted attack that fizzles on lost LOS/range and can't reach outside AoI; shield absorbs one hit of any element; fire bolt > water bolt; self-heal < bolt with a cooldown; "fire cannot heal" distinct cue; 3-word silence; mana spent with no refund and gates casting; casting trains Magery; legend + practice; key-or-click parity; the training-dummy demonstration). Each slice's own DoD is the delta it delivers toward that whole.

Epic tracking the **spell system** — the agreed player-facing design is `docs/game-design/systems/spell-system.md` (produced by the game-design interview + a 4-lens critical panel). This epic's original sketch ("Vulgare Arcano", a 118-word compositional formula language submitted atomically) is **superseded**: the interview converged on a different, tighter model whose core value it keeps. ## The agreed design (one-liner) You don't cast fixed spells — you **speak** them one **power word** at a time, in public, at a **server-paced rhythm** (~0.75s/word, tick-driven). Combat is a real-time duel of reading, predicting, and bluffing your opponent's incantation. Element first (XEN fire / GHY water), form second (attack-bolt / shield / heal); ~7 words in the MVP. Full spec + six-lens review + player-observable DoD in the design doc. Why the pivot: atomic formula submission kills the word-by-word **tell** and the **bluff/feint** the duel is built on; and the emergent 118-word grammar was deliberately scoped down to 5 fixed valid combos. The design doc is the source of truth. ## Technical direction (server-authoritative) - New **`World`-owned `SpellSystem` component** (no lock — the single sim thread serializes it, #54): current mana + regen, the in-progress incantation FSM, shield stance, cooldowns, known-words. `World.cs` only delegates (HARD GATE). - The client sends only intents (`Cast` with a pre-picked target, `CancelCast`); the **server paces** word emission, charges mana (no refund), broadcasts `PowerWordSpoken` via AoI (not the chat/`ChatRateLimiter` path). A modified client cannot rush the sequence → the public tell + anti-bot property always hold. - Reuses what exists: `TargetValidator` (LOS+range), the `PendingSwing`/`_pending` "queue-now-resolve-at-a-future-tick" idiom, skills-as-data (add `Magery`), AoI broadcast, the damage-number pipeline, GM-spawn for the training dummy. - **Current mana is persisted with the vitals** (like current HP — versioned save-format bump; old saves default to full). Correction to the doc's earlier "transient" note. - Combat model = the agreed **middle way**: resolution in a system, per-type reaction hooks (`Mobile.OnHit`) introduced only when a type must react differently (shield-absorb / disrupt); MVP keeps shield state in `SpellSystem`. - No strict determinism (friends-scale; simple rng). ## Implementation slices (each its own green PR) - **Slice 0 — resource & skill foundation:** persist current vitals (HP + mana) in the save format; add mutable current-mana + regen in a new `SpellSystem` stepped from the tick; add the `Magery` skill. Server-only, unit-tested. *(in progress)* - **Slice 1 — one instant bolt:** `Cast(fire, attack, target)`, re-validate LOS+range at a resolve tick → fizzle, elemental damage, mana spend, Magery gain, `PowerWordSpoken` + damage number. Proves cast→target→resolve→damage against the dummy. - **Slice 2 — the incantation FSM:** server-paced multi-word emission (queue/emit/2s-timeout/cancel/feint) + shield + heal + the 5 valid combos + the "fire cannot heal" invalid-combo cue. - **Slice 3 — the mage/warrior triangle:** per-mobile move speed → cast-walk slow; melee disrupt (cross-domain); the 3-word silence interrupt. - **Slice 4 — client UI:** self-HUD (mana + building incantation + timer), enemy ribbon, parchment-plaque overhead words, Elements/Forms palette, legend + practice mode. Server slices (0–3) land before UI (4) so each is testable headless against `World`. ## Definition of Done The **player-observable DoD is the one in `docs/game-design/systems/spell-system.md`** (duel with visible word-by-word tells at a rhythm no client can rush; feint; cancel + 2s reset; targeted attack that fizzles on lost LOS/range and can't reach outside AoI; shield absorbs one hit of any element; fire bolt > water bolt; self-heal < bolt with a cooldown; "fire cannot heal" distinct cue; 3-word silence; mana spent with no refund and gates casting; casting trains Magery; legend + practice; key-or-click parity; the training-dummy demonstration). Each slice's own DoD is the delta it delivers toward that whole.
Author
Owner

Agreed design (game-design review)

Produced with the game-design skill: a multi-round interview + a 4-lens critical panel. Full design doc: docs/game-design/systems/spell-system.md (PR #48). This comment is the player-facing summary; the technical breakdown is a separate review and should be filed as sub-issues of this epic.

One-liner: you dont cast fixed spells — you speak them, one power word at a time, in public; combat is a real-time duel of reading, predicting, and bluffing your opponents incantation.

Six-lens summary

  • Player POV — element word first (XEN fire / GHY water), form second (bolt / shield / heal); each word is server-paced (the tick emits it ~0.75s apart, appearing overhead publicly) so theres always a real tell. Feint by implying one spell and finishing another; cancel (LOP) to bait. ~2s to queue the next word or it resets.
  • Coherence / server-authority — client sends intents only; words are tick-driven (no client can rush the tell), attack range ≤ AoI, power words are their own message (not chat), target-first + LOS/range at resolution (juke behind cover to dodge), a World-owned SpellSystem component, transient mana, Magery use-based skill, cast-walk via the movement component (HARD GATE).
  • UI/UX — incantation on its own render channel (a bold parchment plaque, distinct from chat & damage), a fixed enemy ribbon, a self-HUD (mana + building words + timer), distinct failure cues, a legend + practice mode to learn, key or click.
  • Motivation — the renewable core is PvP outplay; discovery is secondary.
  • Breakage (fixed by the panel)fire bolt hits harder + shield absorbs any element (fixes water-dominates-fire); heal < bolt + cooldown (no unkillable stall); interrupt is a 3-word silence (useful 1v1, its long tell is the counter); mana-per-word-no-refund self-limits spam.
  • Scope/MVP — 2 elements × {bolt, shield, heal} (fire-heal invalid = discovery seed) + a 3-word silence interrupt + cancel; 1v1 focus; a training dummy. Architecture is N-elements/variable-arity from day one.

Definition of Done (player-observable)

  • Two players duel, each seeing the others incantation form word-by-word at a server-paced rhythm no client can rush.
  • A player can feint, cancel, and gets reset if they dont queue the next word in ~2s; casting slows them and a melee hit can disrupt.
  • An attack lands only if the target is still in LOS+range at completion (else fizzle → juke behind cover to dodge) and cant reach a foe outside your AoI.
  • Shield absorbs one hit of any element (more vs the countered one); fire bolt > water bolt; shields dont stop a melee swing.
  • Water heal (self) heals less than a bolt and has a cooldown; fire-heal shows a distinct "fire cannot heal" cue.
  • A 3-word interrupt silences a target (no new cast for a few seconds).
  • Casting spends mana (no refund), trains Magery; a legend + practice mode teach it; key or click cast identically.
  • The full pipeline is demonstrable against a training dummy that shows damage but never dies.

Deferred: 4 elements, the telegraphing sparring dummy, grimoire, reagents, word-unlock quests, group-fight legibility + interrupt anti-lock.

## Agreed design (game-design review) Produced with the `game-design` skill: a multi-round interview + a 4-lens critical panel. **Full design doc: `docs/game-design/systems/spell-system.md` (PR #48).** This comment is the player-facing summary; the **technical breakdown is a separate review** and should be filed as sub-issues of this epic. **One-liner:** you dont cast fixed spells — you *speak* them, one power word at a time, **in public**; combat is a real-time duel of reading, predicting, and bluffing your opponents incantation. ### Six-lens summary - **Player POV** — element word first (XEN fire / GHY water), form second (bolt / shield / heal); each word is **server-paced** (the tick emits it ~0.75s apart, appearing overhead publicly) so theres always a real tell. Feint by implying one spell and finishing another; **cancel** (LOP) to bait. ~2s to queue the next word or it resets. - **Coherence / server-authority** — client sends *intents* only; **words are tick-driven** (no client can rush the tell), attack **range ≤ AoI**, power words are their **own message** (not chat), target-first + **LOS/range at resolution** (juke behind cover to dodge), a `World`-owned **`SpellSystem`** component, **transient mana**, **Magery** use-based skill, cast-walk via the movement component (HARD GATE). - **UI/UX** — incantation on its **own render channel** (a bold **parchment plaque**, distinct from chat & damage), a fixed **enemy ribbon**, a **self-HUD** (mana + building words + timer), distinct failure cues, a **legend** + **practice mode** to learn, key **or** click. - **Motivation** — the renewable core is **PvP outplay**; discovery is secondary. - **Breakage (fixed by the panel)** — **fire bolt hits harder** + **shield absorbs any element** (fixes water-dominates-fire); **heal < bolt** + cooldown (no unkillable stall); interrupt is a **3-word silence** (useful 1v1, its long tell is the counter); mana-per-word-no-refund self-limits spam. - **Scope/MVP** — 2 elements × {bolt, shield, heal} (fire-heal invalid = discovery seed) + a 3-word silence interrupt + cancel; **1v1 focus**; a **training dummy**. Architecture is N-elements/variable-arity from day one. ### Definition of Done (player-observable) - Two players duel, each **seeing the others incantation form word-by-word** at a **server-paced rhythm no client can rush**. - A player can **feint**, **cancel**, and gets reset if they dont queue the next word in ~2s; **casting slows** them and a **melee hit can disrupt**. - An **attack lands only if the target is still in LOS+range** at completion (else fizzle → juke behind cover to dodge) and **cant reach a foe outside your AoI**. - **Shield absorbs one hit of any element** (more vs the countered one); **fire bolt > water bolt**; **shields dont stop a melee swing**. - **Water heal** (self) heals less than a bolt and has a cooldown; **fire-heal shows a distinct "fire cannot heal" cue**. - A **3-word interrupt silences** a target (no new cast for a few seconds). - Casting **spends mana** (no refund), **trains Magery**; a **legend + practice mode** teach it; **key or click** cast identically. - The full pipeline is demonstrable against a **training dummy** that shows damage but never dies. _Deferred: 4 elements, the telegraphing sparring dummy, grimoire, reagents, word-unlock quests, group-fight legibility + interrupt anti-lock._
marco changed title from Epic: compositional spell language (Vulgare Arcano) — vertical slice to Epic: spell system — server-paced power-word duel (vertical slices) 2026-07-19 05:31:39 +02:00
Author
Owner

Spell VFX requirement (owner): the spells must be visible — fireball (projectile + explosion), shield (aura/shimmer), heal (sparkle), etc. — not just damage numbers. Reuse as much legacy UO effect art as possible via the AssetExtractor → assets.isoa pack (new effect/ namespace; effect graphic ids need a discovery pass, as the human anims did). Reuse smartly: one projectile art hued per element (fire warm / water blue) rather than separate art; reuse the explosion, heal sparkle, and an aura for the shield. Server stays authoritative — it sends an effect descriptor (e.g. fire bolt A→B, heal self, shield up X) in the spell broadcast; the client maps it to the art and renders (never decides an effect happened). UO effect art is a temporary placeholder like the rest, to be replaced via the Asset Editor. Tracked as its own slice: extract UO effect art + client spell-FX rendering, feeding slice 1 (bolt) and slice 4 (client polish).

**Spell VFX requirement (owner):** the spells must be *visible* — fireball (projectile + explosion), shield (aura/shimmer), heal (sparkle), etc. — not just damage numbers. Reuse **as much legacy UO effect art as possible** via the AssetExtractor → `assets.isoa` pack (new `effect/` namespace; effect graphic ids need a discovery pass, as the human anims did). Reuse smartly: one projectile art **hued per element** (fire warm / water blue) rather than separate art; reuse the explosion, heal sparkle, and an aura for the shield. Server stays authoritative — it sends an effect *descriptor* (e.g. `fire bolt A→B`, `heal self`, `shield up X`) in the spell broadcast; the client maps it to the art and renders (never decides an effect happened). UO effect art is a temporary placeholder like the rest, to be replaced via the Asset Editor. Tracked as its own slice: **extract UO effect art + client spell-FX rendering**, feeding slice 1 (bolt) and slice 4 (client polish).
Author
Owner

Art slice also covers the training-dummy art. Use the real UO training dummy: it is an animated static in art.mul (the bag/dummy on a post that swings when struck), not a body animation. Pack its static frame(s) under creature/dummy/ (e.g. stand_2, and later the swing frames) reusing the extractor's static→frame path (ArtToFrame) — the same discovery-pass tooling as the spell FX (AssetExtractor statics --range) finds the id (candidate ~0x1070-range, confirm visually). The server dummy (#60) already uses kind dummy, so it renders the UO art automatically once packed. Bonus: play the UO swing frames on-hit (one-shot animator, triggered by the SpellBolt/Attacked broadcast).

**Art slice also covers the training-dummy art.** Use the real UO training dummy: it is an **animated static** in `art.mul` (the bag/dummy on a post that swings when struck), not a body animation. Pack its static frame(s) under `creature/dummy/` (e.g. `stand_2`, and later the swing frames) reusing the extractor's static→frame path (`ArtToFrame`) — the same discovery-pass tooling as the spell FX (`AssetExtractor statics --range`) finds the id (candidate ~`0x1070`-range, confirm visually). The server dummy (#60) already uses kind `dummy`, so it renders the UO art automatically once packed. Bonus: play the UO swing frames on-hit (one-shot animator, triggered by the `SpellBolt`/`Attacked` broadcast).
Author
Owner

MVP fully landed across this session's vertical slices: slice 0 persisted mana + Magery (#57), slice 1 server-authoritative fire/water bolt + UO fireball VFX (#58/#67), slice 2a the server-paced incantation FSM (#75/#77), slice 2b shield (#85) + heal (#82) with the invalid-combination cue, the Spell/SpellCatalog hierarchy (#79), and self-VFX (#92). Element-first/form-second, XEN/GHY + bolt/shield/heal, the public word-by-word tell, mana-per-word, timeout, and Lop cancel all work per the design doc. The MVP duel is playable; further spells/balance can be tracked as fresh issues. Closing the epic.

MVP fully landed across this session's vertical slices: slice 0 persisted mana + Magery (#57), slice 1 server-authoritative fire/water bolt + UO fireball VFX (#58/#67), slice 2a the server-paced incantation FSM (#75/#77), slice 2b shield (#85) + heal (#82) with the invalid-combination cue, the Spell/SpellCatalog hierarchy (#79), and self-VFX (#92). Element-first/form-second, XEN/GHY + bolt/shield/heal, the public word-by-word tell, mana-per-word, timeout, and Lop cancel all work per the design doc. The MVP duel is playable; further spells/balance can be tracked as fresh issues. Closing the epic.
marco closed this issue 2026-07-19 13:38:02 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#36
No description provided.