feat(@projects/@magic-civilization): 🗺️ p3-29 (2) — surface CityBordersExpanded as a turn event
Second p3-29 step-1 event: the Rust turn emits border expansion (process_culture now takes &mut events, collects claimed tiles + flushes TurnEvent::CityBordersExpanded with clan/city/hex) — single-source replacement for the GDScript turn's inline `city_border_expanded` signal. Surfaced through all four TurnEvent consumers + tested (culture_expansion_claims_frontier_tiles now asserts the event). Events-only → no state change → golden/combat unaffected. 2/3 step-1 events done (CityGrew, CityBordersExpanded); FloraSuccession next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f829d87e59
commit
841f741ed5
5 changed files with 60 additions and 7 deletions
|
|
@ -210,6 +210,14 @@ fn event_to_dict(evt: &TurnEvent) -> Dictionary {
|
|||
d.set("col", hex.q as i64);
|
||||
d.set("row", hex.r as i64);
|
||||
}
|
||||
TurnEvent::CityBordersExpanded { turn, clan, city, hex } => {
|
||||
d.set("kind", GString::from("CityBordersExpanded"));
|
||||
d.set("turn", *turn as i64);
|
||||
d.set("clan", clan.0 as i64);
|
||||
d.set("city", GString::from(city.0.as_str()));
|
||||
d.set("col", hex.q as i64);
|
||||
d.set("row", hex.r as i64);
|
||||
}
|
||||
TurnEvent::GameOver { turn, winner, reason_kind, condition, resigned_clan } => {
|
||||
d.set("kind", GString::from("GameOver"));
|
||||
d.set("turn", *turn as i64);
|
||||
|
|
|
|||
|
|
@ -979,9 +979,10 @@ fn translate_processor_events(events: &[mc_replay::TurnEvent]) -> Vec<Event> {
|
|||
| mc_replay::TurnEvent::EnvelopeIntercepted { .. }
|
||||
| mc_replay::TurnEvent::LinkSevered { .. }
|
||||
| mc_replay::TurnEvent::LinkRestored { .. }
|
||||
// p3-29: surfaced for replay + the live UI (city growth), not the
|
||||
// wire protocol — drop here to keep the match exhaustive.
|
||||
| mc_replay::TurnEvent::CityGrew { .. } => {}
|
||||
// p3-29: surfaced for replay + the live UI (city growth / border
|
||||
// expansion), not the wire protocol — drop here to keep it exhaustive.
|
||||
| mc_replay::TurnEvent::CityGrew { .. }
|
||||
| mc_replay::TurnEvent::CityBordersExpanded { .. } => {}
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
|
|||
|
|
@ -121,6 +121,18 @@ pub enum TurnEvent {
|
|||
/// Hex the city sits on, for the viewer to centre on.
|
||||
hex: TileCoord,
|
||||
},
|
||||
/// p3-29: a city expanded its borders, claiming a new tile (single-source
|
||||
/// replacement for the GDScript turn's inline `city_border_expanded` signal).
|
||||
CityBordersExpanded {
|
||||
/// Turn the event fired on.
|
||||
turn: u32,
|
||||
/// Owning clan.
|
||||
clan: ClanId,
|
||||
/// Synthesised city name (`city_<pi>_<idx>`).
|
||||
city: CityName,
|
||||
/// The newly-claimed tile.
|
||||
hex: TileCoord,
|
||||
},
|
||||
/// A wonder finished construction.
|
||||
WonderBuilt {
|
||||
/// Turn the event fired on.
|
||||
|
|
@ -514,6 +526,7 @@ impl TurnEvent {
|
|||
| Self::UnitCreated { turn, .. }
|
||||
| Self::CityBuildingCompleted { turn, .. }
|
||||
| Self::CityGrew { turn, .. }
|
||||
| Self::CityBordersExpanded { turn, .. }
|
||||
| Self::WonderBuilt { turn, .. }
|
||||
| Self::WarDeclared { turn, .. }
|
||||
| Self::PeaceSigned { turn, .. }
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ impl TurnProcessor {
|
|||
self.process_city_production(state, pi, &mut result.events_emitted);
|
||||
self.try_found_city(state, pi, &mut result.events_emitted);
|
||||
self.try_spawn_unit(state, pi, &mut result);
|
||||
self.process_culture(state, pi);
|
||||
self.process_culture(state, pi, &mut result.events_emitted);
|
||||
self.process_culture_research(state, pi);
|
||||
self.process_science(state, pi, &mut result.events_emitted);
|
||||
}
|
||||
|
|
@ -1129,7 +1129,12 @@ impl TurnProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_culture(&self, state: &mut GameState, pi: usize) {
|
||||
fn process_culture(
|
||||
&self,
|
||||
state: &mut GameState,
|
||||
pi: usize,
|
||||
events: &mut Vec<mc_replay::TurnEvent>,
|
||||
) {
|
||||
// Grid dims read before the &mut player borrow (needed for in-bounds
|
||||
// candidate filtering during border expansion).
|
||||
let (gw, gh) = state
|
||||
|
|
@ -1137,6 +1142,10 @@ impl TurnProcessor {
|
|||
.as_ref()
|
||||
.map(|g| (g.width, g.height))
|
||||
.unwrap_or((0, 0));
|
||||
let turn = state.turn;
|
||||
// p3-29: tiles claimed this turn (city_idx, tile), flushed as
|
||||
// `TurnEvent::CityBordersExpanded` so the live UI + replay see expansion.
|
||||
let mut expanded: Vec<(usize, (i32, i32))> = Vec::new();
|
||||
let player = &mut state.players[pi];
|
||||
let culture_axis = *player.strategic_axes.get("culture").unwrap_or(&2);
|
||||
let per_city_yield = culture_axis as f64 * Self::BENCH_CULTURE_PER_AXIS_POINT;
|
||||
|
|
@ -1197,11 +1206,22 @@ impl TurnProcessor {
|
|||
ot.push(pick);
|
||||
claimed.insert(pick);
|
||||
player.culture_pool.consume_expansion(ci);
|
||||
expanded.push((ci, pick));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player.culture_total = player.culture_pool.culture_total;
|
||||
|
||||
// p3-29: flush border-expansion events (player no longer borrowed below).
|
||||
for (ci, tile) in expanded {
|
||||
events.push(mc_replay::TurnEvent::CityBordersExpanded {
|
||||
turn,
|
||||
clan: mc_replay::ClanId(pi as u32),
|
||||
city: mc_replay::CityName(format!("city_{}_{}", pi, ci)),
|
||||
hex: mc_replay::TileCoord::new(tile.0, tile.1),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 1b': Culture-research accumulation ────────────────────────
|
||||
|
|
@ -2677,7 +2697,7 @@ impl TurnProcessor {
|
|||
self.process_city_production(state, pi, &mut result.events_emitted);
|
||||
self.try_found_city(state, pi, &mut result.events_emitted);
|
||||
self.try_spawn_unit(state, pi, &mut result);
|
||||
self.process_culture(state, pi);
|
||||
self.process_culture(state, pi, &mut result.events_emitted);
|
||||
self.process_culture_research(state, pi);
|
||||
self.process_science(state, pi, &mut result.events_emitted);
|
||||
}
|
||||
|
|
@ -5837,10 +5857,19 @@ mod tests {
|
|||
state.players.push(p);
|
||||
|
||||
let processor = TurnProcessor::new(1);
|
||||
let mut events: Vec<mc_replay::TurnEvent> = Vec::new();
|
||||
for _ in 0..80 {
|
||||
processor.process_culture(&mut state, 0);
|
||||
processor.process_culture(&mut state, 0, &mut events);
|
||||
}
|
||||
|
||||
// p3-29: each border claim surfaces a CityBordersExpanded event.
|
||||
assert!(
|
||||
events
|
||||
.iter()
|
||||
.any(|e| matches!(e, mc_replay::TurnEvent::CityBordersExpanded { .. })),
|
||||
"CityBordersExpanded must fire when borders expand; got {events:?}"
|
||||
);
|
||||
|
||||
let owned = &state.players[0].cities[0].owned_tiles;
|
||||
assert!(
|
||||
owned.contains(&(5, 5)),
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ fn ten_turn_run_emits_each_wired_variant() {
|
|||
.map(|e| match e {
|
||||
TurnEvent::CityBuildingCompleted { .. } => "CityBuildingCompleted",
|
||||
TurnEvent::CityGrew { .. } => "CityGrew",
|
||||
TurnEvent::CityBordersExpanded { .. } => "CityBordersExpanded",
|
||||
TurnEvent::CityFounded { .. } => "CityFounded",
|
||||
TurnEvent::WonderBuilt { .. } => "WonderBuilt",
|
||||
TurnEvent::CityCaptured { .. } => "CityCaptured",
|
||||
|
|
@ -324,6 +325,7 @@ fn events_emitted_appears_on_turn_result() {
|
|||
.map(|e| match e {
|
||||
TurnEvent::CityBuildingCompleted { .. } => "CityBuildingCompleted",
|
||||
TurnEvent::CityGrew { .. } => "CityGrew",
|
||||
TurnEvent::CityBordersExpanded { .. } => "CityBordersExpanded",
|
||||
TurnEvent::CityFounded { .. } => "CityFounded",
|
||||
TurnEvent::WonderBuilt { .. } => "WonderBuilt",
|
||||
TurnEvent::CityCaptured { .. } => "CityCaptured",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue