From 04ad707e3b03b3878138d9bd6362f8a8ae552f7b Mon Sep 17 00:00:00 2001 From: Luis Gonzalez Date: Mon, 15 Jun 2026 13:13:29 -0700 Subject: [PATCH] SL-5: distinct final-siege telegraph on the HUD The client derives the climactic final siege from the replicated Charge>=Target and marks it distinctly: 'FINAL SIEGE INCOMING - Ns' during the cap-reached arming window (vs the generic 'INCURSION'), 'HOLD THE ENGINE - FINAL SIEGE' + intense red during the wave, and a last-stand location line. Pure client presentation (no sim/replication change); 342/342 EditMode green. Serves the END-2 fun-gate (the Engine telegraphs the climax + prompts deliberate prep). Co-Authored-By: Claude Opus 4.8 --- .../Scripts/Client/Presentation/HudSystem.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Assets/_Project/Scripts/Client/Presentation/HudSystem.cs b/Assets/_Project/Scripts/Client/Presentation/HudSystem.cs index 57797b3d7..5c001e733 100644 --- a/Assets/_Project/Scripts/Client/Presentation/HudSystem.cs +++ b/Assets/_Project/Scripts/Client/Presentation/HudSystem.cs @@ -139,18 +139,24 @@ namespace ProjectM.Client // ---- Macro: phase + cycle + countdown (center-top banner) ---- bool haveCycle = SystemAPI.TryGetSingleton(out var cyc); bool siege = haveCycle && cyc.Phase == CyclePhase.Siege; + bool goalFull = SystemAPI.TryGetSingleton(out var goalNow) && goalNow.Target > 0 && goalNow.Charge >= goalNow.Target; + bool finalSiege = siege && goalFull; // END-2: the climactic final siege (goal cap reached) if (haveCycle) { var endTick = new NetworkTick(cyc.PhaseEndTick); + bool arming = haveTick && cyc.PhaseEndTick != 0 && endTick.IsValid && endTick.IsNewerThan(nt.ServerTick); + bool finalArming = !siege && goalFull && arming; // the cap-reached arming window before the final wave + int secs = arming ? (endTick.TicksSince(nt.ServerTick) / 60 + 1) : 0; string detail; if (siege) - detail = "WAVE " + cyc.WaveNumber + " - " + huskCount + " HUSKS"; - else if (haveTick && cyc.PhaseEndTick != 0 && endTick.IsValid && endTick.IsNewerThan(nt.ServerTick)) - detail = "INCURSION IN " + (endTick.TicksSince(nt.ServerTick) / 60 + 1) + "s"; + detail = (finalSiege ? "FINAL SIEGE" : "WAVE " + cyc.WaveNumber) + " - " + huskCount + " HUSKS"; + else if (arming) + detail = (finalArming ? "FINAL SIEGE INCOMING" : "INCURSION") + " - " + secs + "s"; else detail = ""; - var col = PhaseColor(cyc.Phase); - _phaseText.text = PhaseLabel(cyc.Phase) + (detail.Length > 0 ? " - " + detail : ""); + // END-2: the climax reads distinct (intense red), not a normal incursion/wave. + var col = finalSiege || finalArming ? new Color(1f, 0.28f, 0.22f) : PhaseColor(cyc.Phase); + _phaseText.text = (finalSiege ? "HOLD THE ENGINE" : PhaseLabel(cyc.Phase)) + (detail.Length > 0 ? " - " + detail : ""); _phaseText.style.color = col; _cycleText.text = "CYCLE " + cyc.CycleNumber; _banner.style.borderBottomColor = col; @@ -167,10 +173,13 @@ namespace ProjectM.Client bool onExpedition = cam != null && cam.transform.position.x > ExpeditionRegionXMin; _locationText.text = onExpedition ? "ON EXPEDITION - carve the frontier, then return" - : siege - ? "DEFEND THE BASE - hold the line" - : "MINE THE CRYSTALS - any attack harvests Ore, then BUILD"; + : finalSiege + ? "FINAL SIEGE - hold the Engine, this is the last stand" + : siege + ? "DEFEND THE BASE - hold the line" + : "MINE THE CRYSTALS - any attack harvests Ore, then BUILD"; _locationText.style.color = onExpedition ? new Color(1f, 0.8f, 0.4f) + : finalSiege ? new Color(1f, 0.3f, 0.25f) : siege ? new Color(1f, 0.55f, 0.4f) : new Color(0.6f, 0.95f, 0.7f); // ---- Goal (hex-pip meter, or a continuous bar for large targets) ----