feat: cast by incantation words on the client; remove the instant Cast (2a, #36) #77

Merged
marco merged 3 commits from feat/incantation-client into main 2026-07-19 09:20:49 +02:00
Owner

Completes slice 2a: the client casts by speaking words, and the server's only casting path is the paced FSM (the instant Cast is gone).

What changed

  • Client input → incantation words: with a target selected, 1 = XEN (fire) / 2 = GHY (water) element, then 3 = VUR (bolt) / 0 = LOP (cancel) → QueueWord. The server paces + resolves.
  • The tell: each PowerWordSpoken flashes the word overhead on the caster in a cool arcane tint (deliberately distinct from warm-yellow chat). The parchment plaque + enemy ribbon are slice 4.
  • Removed the instant Cast (one casting path now): the Cast intent, World.Cast, SpellSystem.Cast, the handler case + BroadcastCastAsync, and the now-dead ManaCostPerBolt (mana is charged per word). SpellCastTests (instant path) deleted — the FSM is covered by IncantationTests; TrainingDummyTests now casts via the words.

Try it (just dev)

/spawn dummy, click it, press 1 then 3 → you speak XEN·VUR, the words flash over your head, and the fire bolt flies. 2 then 3 = water. 0 cancels a half-spoken cast.

just lint → 0 warnings. just test → full suite green (GameServer 142, Client.Core 53).

Definition of Done (delta)

  • Casting is word-by-word from the client; the server paces + shows the tell; the bolt resolves on completion.
  • The instant Cast path is gone (client + server); mana is charged per word only.
  • Whole solution builds, zero warnings, tests green.

Next: refactor the resolution into a Spell class hierarchy + SpellCatalog (owner's ask — one interpreter, no switch forest), then 2b (shield/heal + invalid-combo).

Completes slice 2a: the client casts by **speaking words**, and the server's only casting path is the paced FSM (the instant `Cast` is gone). ## What changed - **Client input → incantation words**: with a target selected, **1 = XEN** (fire) / **2 = GHY** (water) element, then **3 = VUR** (bolt) / **0 = LOP** (cancel) → `QueueWord`. The server paces + resolves. - **The tell**: each `PowerWordSpoken` flashes the word **overhead on the caster** in a cool arcane tint (deliberately distinct from warm-yellow chat). The parchment plaque + enemy ribbon are slice 4. - **Removed the instant Cast** (one casting path now): the `Cast` intent, `World.Cast`, `SpellSystem.Cast`, the handler case + `BroadcastCastAsync`, and the now-dead `ManaCostPerBolt` (mana is charged per word). `SpellCastTests` (instant path) deleted — the FSM is covered by `IncantationTests`; `TrainingDummyTests` now casts via the words. ## Try it (`just dev`) `/spawn dummy`, click it, press **1** then **3** → you speak XEN·VUR, the words flash over your head, and the fire bolt flies. **2** then **3** = water. **0** cancels a half-spoken cast. `just lint` → 0 warnings. `just test` → full suite green (GameServer 142, Client.Core 53). ## Definition of Done (delta) - [x] Casting is word-by-word from the client; the server paces + shows the tell; the bolt resolves on completion. - [x] The instant `Cast` path is gone (client + server); mana is charged per word only. - [x] Whole solution builds, zero warnings, tests green. Next: refactor the resolution into a **`Spell` class hierarchy + `SpellCatalog`** (owner's ask — one interpreter, no switch forest), then **2b** (shield/heal + invalid-combo).
feat: migrate casting to the incantation words; remove the instant Cast (2a, #36)
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m52s
a57dc151e9
The client now casts by speaking words and the server's only casting path is the
paced FSM. Selecting a target, 1=XEN / 2=GHY (element) then 3=VUR (bolt) / 0=LOP
(cancel) sends QueueWord; the server paces + resolves. Each spoken PowerWord
flashes overhead on the caster (a cool arcane tint, distinct from chat) — the
public tell; the parchment plaque + enemy ribbon come in slice 4.

Removed the instant Cast: the Cast intent, World.Cast, SpellSystem.Cast, the
handler case + BroadcastCastAsync, and the now-dead ManaCostPerBolt (mana is
charged per word). SpellCastTests (instant path) deleted — the FSM is covered by
IncantationTests; TrainingDummyTests now casts via the words.
feat(client): compose the incantation tell + outline it for readability
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m54s
2a501914b6
Power words now accumulate overhead as they are spoken ("XEN" → "XEN VUR")
instead of replacing each other, and get a dark 1px outline so the thin arcane
text reads over the world. The window (2.5s) outlasts the ~2s queue timeout so
the building incantation stays composed while the caster speaks.
feat: server-driven clear of the incantation tell (resolve/cancel/timeout/death)
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m44s
b1dc158a9c
The composed power words overhead now clear on an authoritative signal, not a
client timer:
- New PowerWordsCleared(CasterId) broadcast within AoI whenever an incantation
  ends — WorldTick sends it for every resolution (bolt landed, out-of-mana,
  timeout), the handler sends it on a Lop cancel.
- The client composes words as spoken ("XEN" → "XEN VUR") and clears them on
  PowerWordsCleared (with a short linger so the last word reads), or on PlayerDied
  (death resets any in-progress cast). A safety-net lifetime covers a caster that
  left view mid-cast.

The in-progress incantation is transient by design: it lives in SpellSystem
(in-memory), is dropped on disconnect (World.RemovePlayer → Forget) and when the
caster becomes a ghost (Step), and is never in the save blob — a relog or a death
resets it; you can't preserve a half-spoken cast.
marco merged commit c3b2ce28a6 into main 2026-07-19 09:20:49 +02:00
marco deleted branch feat/incantation-client 2026-07-19 09:20:50 +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!77
No description provided.