DR-042 Phase C (legibility, part 1): expedition objective HUD, Aether button, cold-start seed, Biomass sink, palette declutter
Scoping/design-gated (wf_7c5a555e-136). Fixes "the base reads as inert after Phase A":
- C7b objective readout: new replicated ExpeditionObjective{[GhostField] byte State, short Remaining} on the
untagged CycleDirector ghost (cross-region safe). Sole writer ZoneEnemyDirectorSystem, written ABOVE its
early-returns (snapshot-above-early-return) so the HUD never freezes stale. Play-verified it replicates
server->client.
- C7a gate prompt + C7b HUD readout: HudSystem shows "GO TO THE EXPEDITION GATE" / "EXPEDITION IN PROGRESS - N
remaining" / "CLEARED - return to claim", below the siege/overrun overrides.
- C6a Aether upgrade button: un-gated BuildSendSystem.UpgradeAbility (was #if UNITY_EDITOR); HudSystem adds a
MenuUi.Button with live affordability tint (the only Aether sink was U-key only).
- C6c cold-start seed: CycleDirectorSpawnSystem seeds Tuning.StartingOre (50) into the ledger on a NEW game only
(born-correct, pre-Playback), killing the silent turret-before-fabricator deadlock. Play-verified seededOre=50.
- C6b Biomass sink: Wall cost Ore->Biomass (the dead currency now has a home). Play-verified WallCostRes=Biomass.
- C6d palette declutter: hide dead Pylon/Harvester/Conveyor from the build palette + trimmed their dev hotkeys
(catalog/prefabs stay baked, code-intact per DR-020).
389/389 EditMode + clean netcode Play smoke (ghost re-hash OK, no exceptions). SaveData stays v5.
C5 (walls block enemies) is the remaining Phase C item, sequenced separately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,8 @@ namespace ProjectM.Client
|
||||
// END-2: terminal win/loss banner (observes the replicated RunOutcome; latched server-side).
|
||||
VisualElement _runBanner;
|
||||
Label _runBannerText, _runBannerSub;
|
||||
// DR-042 C6a: the Aether ability-upgrade button (was U-key only) + its live affordability tint.
|
||||
Button _upgradeBtn;
|
||||
|
||||
|
||||
readonly List<VisualElement> _pips = new();
|
||||
@@ -181,6 +183,28 @@ namespace ProjectM.Client
|
||||
_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);
|
||||
// DR-042 C7 (gate prompt) + C7b (objective readout): the expedition is the win-driver, so signpost it.
|
||||
// Reads the REPLICATED ExpeditionObjective summary (cross-region safe). Lower priority than the siege /
|
||||
// cold-turret / overrun overrides below, which still win.
|
||||
if (SystemAPI.TryGetSingleton<ExpeditionObjective>(out var obj))
|
||||
{
|
||||
if (onExpedition)
|
||||
{
|
||||
if (obj.State == ExpeditionObjectiveState.Cleared)
|
||||
{ _locationText.text = "ZONE CLEARED - return to base to claim"; _locationText.style.color = new Color(0.5f, 1f, 0.6f); }
|
||||
else if (obj.State == ExpeditionObjectiveState.Active)
|
||||
{ _locationText.text = "CLEAR THE ZONE - " + obj.Remaining + " enemies remaining"; _locationText.style.color = new Color(1f, 0.8f, 0.4f); }
|
||||
}
|
||||
else if (!siege)
|
||||
{
|
||||
if (obj.State == ExpeditionObjectiveState.Cleared)
|
||||
{ _locationText.text = "EXPEDITION CLEARED - return to claim your reward"; _locationText.style.color = new Color(0.5f, 1f, 0.6f); }
|
||||
else if (obj.State == ExpeditionObjectiveState.Active)
|
||||
{ _locationText.text = "EXPEDITION IN PROGRESS - " + obj.Remaining + " enemies remaining"; _locationText.style.color = new Color(1f, 0.8f, 0.4f); }
|
||||
else
|
||||
{ _locationText.text = "GO TO THE EXPEDITION GATE - clear a sortie to advance the Engine"; _locationText.style.color = new Color(0.55f, 0.85f, 1f); }
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Goal (hex-pip meter, or a continuous bar for large targets) ----
|
||||
if (SystemAPI.TryGetSingleton<GoalProgress>(out var goal))
|
||||
@@ -230,6 +254,9 @@ namespace ProjectM.Client
|
||||
_oreNum.text = ore.ToString();
|
||||
_bioNum.text = bio.ToString();
|
||||
_chargeNum.text = charge.ToString();
|
||||
// DR-042 C6a: dim the Aether upgrade button when it isn't affordable (cost is a compile-time const).
|
||||
if (_upgradeBtn != null)
|
||||
_upgradeBtn.style.opacity = aether >= Tuning.AbilityUpgradeCostAmount ? 1f : 0.5f;
|
||||
// EB-2 quiet-turret cue (GLOBAL, not per-turret, so the deterministic Charge split never reads as one
|
||||
// broken turret): a dry base during a siege tells the player to build a Fabricator.
|
||||
if (siege && charge == 0 && !onExpedition)
|
||||
@@ -446,13 +473,19 @@ namespace ProjectM.Client
|
||||
}
|
||||
}
|
||||
|
||||
// DR-042 C6d: Harvester/Conveyor/Pylon are dead (unwired automation) -> hidden from the build palette
|
||||
// (catalog + prefabs stay baked, code-intact per DR-020). Only Turret/Wall/Fabricator are buildable in the UI.
|
||||
static bool IsPaletteType(byte type) =>
|
||||
type != StructureType.Pylon && type != StructureType.Harvester && type != StructureType.Conveyor;
|
||||
|
||||
void UpdatePalette(int aether, int ore, int bio, bool onExpedition)
|
||||
{
|
||||
if (!_paletteBuilt && SystemAPI.TryGetSingletonEntity<StructureCatalog>(out var catE))
|
||||
{
|
||||
var cat = SystemAPI.GetBuffer<StructureCatalogEntry>(catE);
|
||||
for (int i = 0; i < cat.Length; i++)
|
||||
AddPaletteItem(cat[i].Type, cat[i].CostAmount, cat[i].CostResourceId);
|
||||
if (IsPaletteType(cat[i].Type))
|
||||
AddPaletteItem(cat[i].Type, cat[i].CostAmount, cat[i].CostResourceId);
|
||||
_paletteBuilt = true;
|
||||
}
|
||||
if (!_paletteBuilt) { _paletteRow.style.display = DisplayStyle.None; return; }
|
||||
@@ -809,6 +842,11 @@ namespace ProjectM.Client
|
||||
strip.Add(ResourceChip(theme != null ? theme.OreIcon : null, OreAmber, "0", out _oreNum, 30, 22));
|
||||
strip.Add(ResourceChip(theme != null ? theme.BioIcon : null, BioGreen, "0", out _bioNum, 26, 20));
|
||||
strip.Add(ResourceChip(null, ChargeViolet, "0", out _chargeNum, 26, 20)); // EB-2 turret ammo (flat violet, no icon)
|
||||
// DR-042 C6a: the only Aether sink (ability-damage upgrade) gets a visible, clickable button (was U-key
|
||||
// only). The Button element handles its own picking even though the HUD root Ignores clicks.
|
||||
_upgradeBtn = MenuUi.Button("UPGRADE DMG (" + Tuning.AbilityUpgradeCostAmount + " AETHER)", BuildSendSystem.UpgradeAbility);
|
||||
_upgradeBtn.style.marginLeft = 18;
|
||||
strip.Add(_upgradeBtn);
|
||||
|
||||
root.Add(strip);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user