feat(@projects/@magic-civilization): upgrade queues to btreemap for deterministic iteration

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-05 17:21:50 -04:00
parent 00a6f8a1dc
commit 7da7e71d68
2 changed files with 16 additions and 7 deletions

View file

@ -13,8 +13,8 @@ use crate::production::{
use mc_core::collectibles::CollectibleRoll;
use mc_core::ResourceId;
use mc_economy::{Stockpile, StockpileError};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{BTreeMap, HashMap, HashSet};
/// Which `CityYields` channel a collectible resource_id maps to.
/// Resources not matched here are silently skipped (they gate units/buildings
@ -265,8 +265,17 @@ pub struct City {
// ── Buildings & queues (carried from production.rs) ────────────
pub buildings: Vec<String>,
#[serde(default)]
queues: HashMap<String, BuildingQueue>,
/// Per-origin production queues. Keyed by either a producer building id
/// (e.g. `smithy`, `marketplace`) or [`CITY_CENTER_QUEUE_ID`] for
/// `ProductionOrigin::CityCenter`. Backed by a [`BTreeMap`] so iteration
/// order is deterministic — the per-turn split allocator
/// (`tick_city_production`) relies on this for reproducible runs.
///
/// `deserialize_with` accepts the legacy save schema where a flat
/// `production_queue: Vec<QueueEntry>` lived on the city; entries migrate
/// into the map keyed by their `origin` (p1-44 Phase B).
#[serde(default, deserialize_with = "deserialize_queues_with_legacy_fallback")]
queues: BTreeMap<String, BuildingQueue>,
/// Per-building flat yield bonuses keyed by building id.
/// Populated by GDScript at game load from JSON `effects` arrays.
@ -303,7 +312,7 @@ impl Default for City {
focus: CityFocus::Default,
worked_tiles: Vec::new(),
buildings: Vec::new(),
queues: HashMap::new(),
queues: BTreeMap::new(),
building_yields: HashMap::new(),
last_attacked_turn: None,
captured_turn: None,
@ -365,7 +374,7 @@ impl City {
focus: CityFocus::Default,
worked_tiles: vec![position],
buildings: Vec::new(),
queues: HashMap::new(),
queues: BTreeMap::new(),
building_yields: HashMap::new(),
last_attacked_turn: None,
captured_turn: None,

View file

@ -27,7 +27,7 @@
use serde::{Deserialize, Serialize};
use mc_core::lair::{LairCombatMode, LairId, SiegeOutcome, SiegeState};
use mc_core::lair::{LairCombatMode, LairId, RaidAftermath, RaidOutcome, SiegeOutcome, SiegeState};
use crate::bonuses::CombatBonuses;
use crate::loot::{mix_seed, roll_loot_table, LootDrop, LootEntry};