Slice 1: combat readability + HUD declutter (DR-038)

Four playtest do-now wins:
- Enemy health bars: pooled world-space Canvas, on-damage-sticky + fade,
  always-on <25% HP (CombatFeedbackSystem; no new replication).
- Telegraph fix: new baked client-safe EnemyTelegraph sizes the danger-cone ramp
  per enemy (0->1 ending at impact, fixes the Charger plateau); windup 18->22;
  a windup scale-pulse.
- Build-mode toggle: BuildPaletteState.PaletteOpen hides the palette by default,
  Tab / gamepad-Y toggles, with a discovery chip (HudSystem/BuildSendSystem).
- Charger committed-lunge tell: [GhostEnabledBit] IsLunging derived once/tick from
  LungeState (the Dead idiom); the danger cone persists through the lunge.

345/345 EditMode (+3 IsLunging derive tests); Play-validated: ghost-hash change
did not break the handshake, bake correct (telegraph on all enemies, IsLunging
baked-disabled on the Charger, replicated to client), no runtime errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 12:48:08 -07:00
parent 5292940f9d
commit f3eccec524
12 changed files with 360 additions and 24 deletions
@@ -322,6 +322,16 @@ namespace ProjectM.Server
}
}
// Slice 1 (Feature D): derive the replicated IsLunging cue ONCE per tick from the end-of-tick LungeState
// (single point, idempotent — mirrors PlayerDeathStateSystem deriving Dead from Health). .WithPresent so a
// Charger whose bit is currently DISABLED is still visited (Entities default-excludes disabled enableables).
foreach (var (lunge, isLunging) in
SystemAPI.Query<RefRO<LungeState>, EnabledRefRW<IsLunging>>()
.WithAll<EnemyTag>().WithPresent<IsLunging>())
{
isLunging.ValueRW = lunge.ValueRO.UntilTick != 0u; // lunging iff a committed lunge is live this tick
}
if (chargerWhiffsThisTick != 0 && SystemAPI.HasSingleton<DevTelemetry>())
SystemAPI.GetSingletonRW<DevTelemetry>().ValueRW.ChargerWhiffWindowsOpened += chargerWhiffsThisTick;