Vitals HUD: show hits / mana / stamina (they are invisible today) #181
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo#181
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Surfaced by the stamina design review (#178): the player cannot see any vital. There is no wire message carrying Hits/Mana to the client and no HUD element — they exist only server-side. Today you learn your HP from floating damage numbers and your mana from a "not enough mana" notice.
That is tolerable for HP/mana but becomes a real problem with stamina (#178), which gates running: without a bar the player just mysteriously stops sprinting.
Scope (proposed - needs its own design review)
GameScreen(Screen HARD GATE).ProtocolVersionbump in the same change.Definition of Done (delta)
GameScreenonly wires it.Prereq/related: #178 (stamina), #177/#180 (run gait).
Agreed design (reviewed with critical-design-review)
Owner decisions: build on
mainnow (STAM shows theStamina => Dexstub until #185 lands, then one line points it atStamTenths); procedural HUD first, the gothic frame (from the owner's mockup) as a follow-up skin; no enemy HP bars (self-only).Design
VitalsState(Hits, MaxHits, Stam, MaxStam, Mana, MaxMana, Str, Dex, Int)ServerMessage; bumpProtocolVersion. Sent only to the owning connection, likeInventoryState— never broadcast, so other players' vitals stay server-side.PlayerRegistry.DrainVitalsChanges()compares each player's current vitals to the last sent (stored onPlayerMobile), emitting only on change. Robust across every mutation site (damage, cast, regen,/set) with no dirty-flag plumbing; the last-sent snapshot lives on the entity (not a side dict).WorldTicksends each to its owner viaconnections.SendToAsync(id, …), exactly likeSkillGains.ClientWorld.Vitalsset by the dispatcher; aVitalsHudcomponent drawn byWorldRenderer(Screen HARD GATE: nothing inGameScreen); the pure fill fraction (current/max) in Client.Core, unit-tested. MVP draws three bars (HP red / STAM green / MANA blue) + value text + STR/DEX/INT plates, laid out like the mockup.Invariants Check
Scope ✓ (only the self vitals HUD). Server-authoritative ✓ (client displays, never computes/sends). GM auth N/A. Identity N/A. Protocol versioned ✓ (new
VitalsState→ bump). String catalog N/A (static client UI labels, not server copy). Single-threaded sim ✓ (drain in the tick, sim thread).World.csHARD GATE ✓ (drain delegated toPlayerRegistry).ScreenHARD GATE ✓ (VitalsHudcomponent, notGameScreen). Client engine-independence ✓ (fill fraction pure in Client.Core). Gameplay/Networking separation ✓. Act on the instance ✓ (last-sent snapshot onPlayerMobile, no side dict). Extend by type not switch ✓ (a DTO, not a type switch). Server-paced N/A. Persistence (GameServer) ✓ (nothing new persisted; HP/mana already are). Persistence (Auth) N/A. Process separation N/A. Typed options N/A. Broadcasts/AoI ✓ —VitalsStateis self-only, never broadcast (other players' vitals stay server-side). Multi-platform ✓ (.NET/MonoGame). Assets ✓ (procedural MVP needs no art; the frame skin will be realgump/*art). Asset naming ✓ (gump/vitals_framewhen skinned). ModernUO ✓ (self-only status bar; enemy target-bar deferred). Docs & DoD ✓.No blocking verdict.
Verification plan
VitalsBar.Fill(current, max)clamps 0–1.VitalsStatefor the owner only (trust boundary: an observer does not receive it) — asserted via a WebSocket flow (CreateWebSocketClient) that a second client gets noVitalsStatefor the first./set hits 20(GM) → the HP bar drops.Definition of Done (delta)
/set).GameScreenonly wires it.ProtocolVersionbumped in the same change;VitalsStateround-trips.Implemented in #205 (branch
feat/vitals-hud, on top of currentmain).What landed (the "1 poi 2" first pass):
VitalsStatewire message (hits/maxHits, stam/maxStam, mana/maxMana + str/dex/int);ProtocolVersion.Current→ 14.PlayerMobile.LastSentVitals;SendToAsyncby id) — scoped exactly likeInventoryState/SkillGains, never broadcast, never to observers.VitalsHud(bottom-left): STR/DEX/INT plates + HP/STAM/MANA bars with current/max, laid out to the mockup. Thin view owned byWorldRenderer; fill math inClient.Core(VitalsBar, NaN-safe).DoD status:
VitalsState, verified by the WebSocket trust-boundary test (self receives it, a nearby observer never does)./set hits 18→ HP bar drains to 18/50).dotnet testacross all projects, whole-solution build).Part 2 (deferred, as agreed): the carved gothic frame skin over this procedural layout, once the frame art exists — tracked as a follow-up, not this PR.
Risolta da PR #205 (
33fa395): Vitals HUD self-only —VitalsState(hits/mana/stam + str/dex/int) inviato solo alla connessione proprietaria,ProtocolVersion→14, HUD in componente dedicato (VitalsHud.cs/VitalsBar.cs) senza accrescereGameScreen. DoD soddisfatta. Chiudo — completata.