Phase 0 stabilize: lifecycle-aware banner, RoomExplore location line, grounded crate + beacon, tamed beacon HDR

- The center-top phase banner read the CyclePhase label ("AT BASE") inside
  expedition rooms - now shows ON EXPEDITION whenever a run is past Staging.
- The location-line switch had no RoomExplore case, so "choose your boon"
  stuck through the whole loot window - added the portal-steer line.
- Storage crate floated 1u: GridOrigin.y=1 is the CC capsule-CENTER plane;
  CellToWorld returns it, and the crate's mesh pivot is at its base (Turret/
  Wall pivots are centered, which is why structures look grounded). The
  container now sits on the terrain; same grounding for the portal beacon,
  whose pos.y was the capsule plane too.
- Beacon HDR 2.6/3.4 bloomed into a white egg that swallowed the prompt -
  tamed to 1.2/1.55 + slimmed the pillar.

456/456 EditMode before and after; Play smoke: crate at y=0, banner correct
at Staging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 12:26:19 -07:00
parent 274cf3b791
commit 7c1fee097e
3 changed files with 17 additions and 7 deletions
@@ -180,10 +180,12 @@ namespace ProjectM.Client
int huskCount = _huskQuery.CalculateEntityCount();
// ---- Macro: phase + cycle + countdown (center-top banner) ----
bool haveRun = SystemAPI.TryGetSingleton<RunInfo>(out var runInfo); // hoisted: the phase banner is lifecycle-aware (Phase 0 fix — it read "AT BASE" inside expedition rooms)
bool haveCycle = SystemAPI.TryGetSingleton<CycleState>(out var cyc);
bool siege = haveCycle && cyc.Phase == CyclePhase.Siege;
bool goalFull = SystemAPI.TryGetSingleton<GoalProgress>(out var goalNow) && goalNow.Target > 0 && goalNow.Charge >= goalNow.Target;
bool finalSiege = siege && goalFull; // END-2: the climactic final siege (goal cap reached)
bool onRun = haveRun && !siege && runInfo.Lifecycle != RunLifecycle.Staging; // mid-run: the base's Calm label is wrong
if (haveCycle)
{
var endTick = new NetworkTick(cyc.PhaseEndTick);
@@ -199,7 +201,7 @@ namespace ProjectM.Client
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.text = (finalSiege ? "HOLD THE ENGINE" : onRun ? "ON EXPEDITION" : PhaseLabel(cyc.Phase)) + (detail.Length > 0 ? " - " + detail : "");
_phaseText.style.color = col;
_cycleText.text = "CYCLE " + cyc.CycleNumber;
_banner.style.borderBottomColor = col;
@@ -215,7 +217,6 @@ namespace ProjectM.Client
// (the old camera-X + walk-in-gate copy died with the gate; siege/final overrides below still win).
var cam = Camera.main; // camera-X region signal still feeds downstream panels (atmosphere/threat)
bool onExpedition = cam != null && cam.transform.position.x > ExpeditionRegionXMin;
bool haveRun = SystemAPI.TryGetSingleton<RunInfo>(out var runInfo);
SystemAPI.TryGetSingleton<ExpeditionObjective>(out var obj);
if (haveRun && !siege && !finalSiege)
{
@@ -255,6 +256,10 @@ namespace ProjectM.Client
_locationText.text = "ROOM CLEARED - choose your boon";
_locationText.style.color = new Color(0.5f, 1f, 0.6f);
break;
case RunLifecycle.RoomExplore: // Phase 0: without this case the RoomReward text stuck through the loot window
_locationText.text = "ROOM CLEAR - grab the loot, take the portal to move on";
_locationText.style.color = new Color(0.5f, 1f, 0.6f);
break;
case RunLifecycle.RouteSelect:
_locationText.text = "CHOOSE YOUR PATH";
_locationText.style.color = new Color(0.55f, 0.85f, 1f);