feat(gameserver): server-authoritative fire/water bolt (#36) #58

Merged
marco merged 1 commit from feat/spell-fire-bolt into main 2026-07-19 06:31:03 +02:00
Owner

Spell-system slice 1 (part of #36): the server-authoritative fire/water bolt. This is the authoritative core — the visible client animation is the next slice (see FX plan below).

What lands

  • Cast(Element, Form, TargetId) client intent (pre-picked target, like Attack) and a SpellBolt(CasterId, TargetId, Element, Damage) broadcast within the caster's AoI for the client to animate.
  • SpellSystem.Cast (fire/water Bolt): rejects a dead caster / unknown target / self / not-enough-mana (no spend); otherwise spends mana (no refund), trains Magery on resolution, then lands (LOS + range OK) or fizzles (out of range / no line of sight). Fire hits harder than water; damage scales with Magery; bolt range caps at the AoI radius (you can only hit whom you can see).
  • Damage flows through a shared CombatSystem.ApplyDamage so spells and melee kill identically (player → ghost + resurrection; creature → slay + corpse) — the agreed middle way, extracted from the melee resolution with no behaviour change to melee.
  • The handler broadcasts the landed bolt (+ PlayerDied) to the AoI and privately cues the caster on reject/fizzle/skill-gain. SpellOptions bound from GameOptions.

Verification

just lint → 0 warnings. just test → full suite green (GameServer 135, Shared 51, Client.Core 46, Auth 12, Assets 4, AssetExtractor 7). New SpellCastTests cover: lands with fire damage, fire > water, not-enough-mana rejects (no spend), out-of-range/no-LOS fizzle (mana still spent), self/unknown-target/unimplemented-form rejects, lethal bolt drops a corpse, casting trains Magery.

Definition of Done (delta)

  • A player casts a fire bolt at a target; it lands only if the target is in LOS + range, else fizzles.
  • Casting spends mana (no refund on fizzle) and can't cast at 0 mana; it trains Magery.
  • Fire bolt out-damages the water bolt; damage scales with Magery.
  • A lethal bolt kills through the shared damage path (creature → corpse).
  • Whole solution builds, zero warnings, tests green, multi-platform preserved.

Next slice — the VISIBLE fireball (owner's ask: reuse UO art)

Mapped and ready: UO effect art lives in art.mul (~0x36xx); the client already has SpriteAnimator with a Color? tint (per-element hue for free) and a one-shot animation pattern. Plan: a discovery pass (AssetExtractor statics --range) to find the fireball/explosion ids → add an effect/ namespace to the pack → a client ActiveEffect that flies the projectile caster→target on SpellBolt and plays the explosion at impact. Tracked on #36.

Spell-system **slice 1** (part of #36): the server-authoritative fire/water **bolt**. This is the authoritative core — the *visible* client animation is the next slice (see FX plan below). ## What lands - **`Cast(Element, Form, TargetId)`** client intent (pre-picked target, like `Attack`) and a **`SpellBolt(CasterId, TargetId, Element, Damage)`** broadcast within the caster's AoI for the client to animate. - **`SpellSystem.Cast`** (fire/water `Bolt`): rejects a dead caster / unknown target / self / not-enough-mana (no spend); otherwise **spends mana (no refund)**, **trains Magery** on resolution, then **lands** (LOS + range OK) or **fizzles** (out of range / no line of sight). **Fire hits harder than water**; damage scales with Magery; bolt range caps at the **AoI radius** (you can only hit whom you can see). - Damage flows through a shared **`CombatSystem.ApplyDamage`** so spells and melee kill identically (player → ghost + resurrection; creature → slay + corpse) — the agreed *middle way*, extracted from the melee resolution with no behaviour change to melee. - The handler broadcasts the landed bolt (+ `PlayerDied`) to the AoI and privately cues the caster on reject/fizzle/skill-gain. `SpellOptions` bound from `GameOptions`. ## Verification `just lint` → 0 warnings. `just test` → full suite green (**GameServer 135**, Shared 51, Client.Core 46, Auth 12, Assets 4, AssetExtractor 7). New `SpellCastTests` cover: lands with fire damage, fire > water, not-enough-mana rejects (no spend), out-of-range/no-LOS fizzle (mana still spent), self/unknown-target/unimplemented-form rejects, lethal bolt drops a corpse, casting trains Magery. ## Definition of Done (delta) - [x] A player casts a fire bolt at a target; it lands only if the target is in LOS + range, else fizzles. - [x] Casting spends mana (no refund on fizzle) and can't cast at 0 mana; it trains Magery. - [x] Fire bolt out-damages the water bolt; damage scales with Magery. - [x] A lethal bolt kills through the shared damage path (creature → corpse). - [x] Whole solution builds, zero warnings, tests green, multi-platform preserved. ## Next slice — the VISIBLE fireball (owner's ask: reuse UO art) Mapped and ready: UO effect art lives in `art.mul` (~`0x36xx`); the client already has `SpriteAnimator` with a `Color? tint` (per-element hue for free) and a one-shot animation pattern. Plan: a discovery pass (`AssetExtractor statics --range`) to find the fireball/explosion ids → add an `effect/` namespace to the pack → a client `ActiveEffect` that flies the projectile caster→target on `SpellBolt` and plays the explosion at impact. Tracked on #36.
feat(gameserver): server-authoritative fire/water bolt (#36)
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m36s
c30724a5a4
Spell-system slice 1: the instant bolt, resolved authoritatively.

- New Cast intent (element + form + pre-picked target) and a SpellBolt
  broadcast carrying caster/target/element/damage for the client to animate.
- SpellSystem.Cast (fire/water Bolt): rejects a dead caster / unknown target /
  self / insufficient mana; otherwise spends mana (no refund), trains Magery on
  resolution, and either lands (LOS + range) or fizzles. Fire hits harder than
  water; damage scales with Magery. Bolt range caps at the AoI radius.
- Damage flows through a shared CombatSystem.ApplyDamage so spells and melee kill
  the same way (player ghost / creature slay+corpse) — the agreed middle way,
  extracted from the melee resolution (no behaviour change to melee).
- Handler broadcasts the landed bolt (+ PlayerDied) to the caster's AoI and cues
  the caster on reject/fizzle/skill-gain. SpellOptions tuning bound from GameOptions.

Server-side + tested (cast lands/fizzles/rejects, mana spend, Magery trains,
lethal bolt drops a corpse). Client rendering of the effect is the next slice.
marco merged commit de34466280 into main 2026-07-19 06:31:03 +02:00
marco deleted branch feat/spell-fire-bolt 2026-07-19 06:31:03 +02:00
Sign in to join this conversation.
No reviewers
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!58
No description provided.