feat(view): project CityView.culture_stored from the per-city CulturePool (Rail-1 Phase 0)
The live city_screen reads a per-city culture meter off the GDScript CityScript; view_json had no equivalent, so the UI-pure-view migration couldn't render it from getState(). project_cities now surfaces culture_stored from PlayerState.culture_pool.city(c_idx) (the same accumulator mc-turn::process_culture ticks for border expansion); 0.0 for a city with no pool entry. Closes the last genuine Phase-0 projection gap (UnitView equipped/experience/movement/ posture + ResourceView golden_age were already projected — design-doc table was stale). Test: projection_surfaces_city_culture_stored. mc-player-api lib 142/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0c50c04b4c
commit
04763a3870
2 changed files with 46 additions and 0 deletions
|
|
@ -286,6 +286,15 @@ fn project_cities(
|
|||
// and adapters can call view() across turns to observe progress.
|
||||
// TRACKED: surface the canonical threshold via mc-city.
|
||||
food_growth_threshold: 0.0,
|
||||
// p3-25/Phase-0: real per-city culture accumulator from the
|
||||
// player's CulturePool (the same value border expansion ticks
|
||||
// in mc-turn::process_culture). 0.0 when the city has no pool
|
||||
// entry yet (registered lazily on first culture yield).
|
||||
culture_stored: player
|
||||
.culture_pool
|
||||
.city(c_idx)
|
||||
.map(|cc| cc.culture_stored as f32)
|
||||
.unwrap_or(0.0),
|
||||
production_queue,
|
||||
buildings,
|
||||
// p3-25: real territory. CityState.owned_tiles is empty on a fresh
|
||||
|
|
@ -2142,6 +2151,38 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// p3-25 Phase-0: `CityView.culture_stored` surfaces the per-city culture
|
||||
/// accumulator from the player's `CulturePool` (the same meter the live
|
||||
/// `city_screen` reads + border expansion ticks). A city with no pool entry
|
||||
/// projects `0.0`.
|
||||
#[test]
|
||||
fn projection_surfaces_city_culture_stored() {
|
||||
let mut state = GameState::default();
|
||||
state.turn = 1;
|
||||
state.grid = Some(mc_core::grid::GridState::new(20, 20));
|
||||
let mut a = PlayerState::default();
|
||||
a.player_index = 0;
|
||||
a.city_positions = vec![(3, 4), (7, 8)];
|
||||
a.cities = vec![mc_city::CityState::starter(), mc_city::CityState::starter()];
|
||||
// City 0 accrues culture via its pool; city 1 has no pool entry.
|
||||
a.culture_pool.register_city(0, 5.0);
|
||||
a.culture_pool.tick_all();
|
||||
a.culture_pool.tick_all();
|
||||
state.players.push(a);
|
||||
|
||||
let view = project_view(&state, 0, /*omniscient=*/ true);
|
||||
assert_eq!(view.cities.len(), 2);
|
||||
assert!(
|
||||
(view.cities[0].culture_stored - 10.0).abs() < 1e-3,
|
||||
"registered city's stored culture must surface: {}",
|
||||
view.cities[0].culture_stored
|
||||
);
|
||||
assert_eq!(
|
||||
view.cities[1].culture_stored, 0.0,
|
||||
"city with no pool entry projects 0.0"
|
||||
);
|
||||
}
|
||||
|
||||
/// p3-24 rail-1: the diplomacy projection reads real OpenBorders/SharedMap
|
||||
/// agreement state from `state.trade_ledger` (replacing the former hardcoded
|
||||
/// `false`/empty stubs).
|
||||
|
|
|
|||
|
|
@ -142,6 +142,11 @@ pub struct CityView {
|
|||
pub food_stored: f32,
|
||||
/// Food needed for the next growth event.
|
||||
pub food_growth_threshold: f32,
|
||||
/// Culture accumulated toward the next border expansion (mirrors the live
|
||||
/// `CityScript` culture meter the `city_screen` reads). Sourced from the
|
||||
/// player's `CulturePool` per-city state; `0.0` for a city with no pool entry.
|
||||
#[serde(default)]
|
||||
pub culture_stored: f32,
|
||||
/// Current production queue.
|
||||
pub production_queue: Vec<ProductionQueueEntry>,
|
||||
/// Buildings completed in this city.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue