From 0d501a3d722ebee95befcd310fba999b9a9538ea Mon Sep 17 00:00:00 2001 From: Natalie Date: Sat, 27 Jun 2026 08:03:07 -0400 Subject: [PATCH] =?UTF-8?q?feat(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=9B=A4=EF=B8=8F=20Rail-1=20Phase-0=20=E2=80=94=20project?= =?UTF-8?q?=20Golden=20Age=20state=20to=20the=20HUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ResourceView gains golden_age_active + golden_age_turns, projected from the PlayerState GA fields. These are the top_bar.gd:162/169 entity reads now available via view_json (HUD badge driven by Rust, not the Player entity). Omitted from the wire when inactive. Per-city culture_stored (city_screen.gd:287) is DEFERRED: it has no bench CityState backing (culture is a player-level pool in the reduced model) — a Phase-1 SOT-flip widening, not fabricated here. Additive (serde defaults). mc-player-api 140/0 incl. a GA round-trip/omission test. Co-Authored-By: Claude Opus 4.8 --- .../crates/mc-player-api/src/projection.rs | 4 +++ .../crates/mc-player-api/src/view.rs | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/simulator/crates/mc-player-api/src/projection.rs b/src/simulator/crates/mc-player-api/src/projection.rs index 6fe1ea55..2024cf87 100644 --- a/src/simulator/crates/mc-player-api/src/projection.rs +++ b/src/simulator/crates/mc-player-api/src/projection.rs @@ -162,6 +162,10 @@ fn project_resources(player: &mc_state::game_state::PlayerState) -> ResourceView // p3-26 B1: surface the happiness pool now computed by the turn's // happiness_phase (was hardcoded 0 before the phase existed). happiness_pool: player.happiness, + // Rail-1: surface the Golden Age state the HUD badge reads + // (top_bar.gd:162/169) so it comes from view_json, not the entity. + golden_age_active: player.golden_age_active, + golden_age_turns: player.golden_age_turns.max(0) as u32, stockpile, } } diff --git a/src/simulator/crates/mc-player-api/src/view.rs b/src/simulator/crates/mc-player-api/src/view.rs index f28fc97d..90629cf0 100644 --- a/src/simulator/crates/mc-player-api/src/view.rs +++ b/src/simulator/crates/mc-player-api/src/view.rs @@ -36,10 +36,21 @@ pub struct ResourceView { pub culture_per_turn: i32, /// Global happiness pool (positive = surplus). pub happiness_pool: i32, + /// Whether the empire is in a Golden Age (drives the HUD badge). + #[serde(default, skip_serializing_if = "core::ops::Not::not")] + pub golden_age_active: bool, + /// Golden-age turns remaining (`0` when not active). + #[serde(default, skip_serializing_if = "crate::view::is_zero_u32")] + pub golden_age_turns: u32, /// Strategic-resource stockpile (resource_id → amount). pub stockpile: std::collections::BTreeMap, } +/// serde helper: skip `u32` fields that are zero (wire economy). +pub(crate) fn is_zero_u32(v: &u32) -> bool { + *v == 0 +} + /// Research / tech tab state. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] pub struct ResearchView { @@ -477,6 +488,20 @@ mod tests { assert!(!json.contains("posture"), "resting posture must be omitted: {json}"); } + #[test] + fn resource_view_golden_age_round_trips_and_omits_when_inactive() { + let mut r = ResourceView::default(); + assert!(!serde_json::to_string(&r).unwrap().contains("golden_age"), + "inactive golden age omitted from wire"); + r.golden_age_active = true; + r.golden_age_turns = 8; + let json = serde_json::to_string(&r).unwrap(); + assert!(json.contains("\"golden_age_active\":true"), "json={json}"); + assert!(json.contains("\"golden_age_turns\":8"), "json={json}"); + let back: ResourceView = serde_json::from_str(&json).unwrap(); + assert_eq!(r, back); + } + #[test] fn unit_posture_serialized_only_when_active() { let mut u = UnitView {