From 64ea08b7ce61ad9b0bae85a33ad1a29907998c6f Mon Sep 17 00:00:00 2001 From: Natalie Date: Fri, 26 Jun 2026 10:55:34 -0400 Subject: [PATCH] =?UTF-8?q?feat(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=94=8C=20p3-26=20gap=202=20=E2=80=94=20load=20event=20con?= =?UTF-8?q?figs=20in=20the=20headless=20harness=20(events=20now=20LIVE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Activates natural events in real headless games: - GdPlayerApi.set_events_config_json FFI → state.load_events_config_json. - player_api_main.gd::_apply_events_config stamps DataLoader.get_ecological_events() (the merged {category: config} map) onto GdPlayerApi after load_state_json (same #[serde(skip)] re-stamp pattern as resource_categories / catalogs). So a headless self-play game now loads the 12-category event configs → mc-turn's climate phase fires wildfire (other handlers to follow). The firing mechanism is already proven deterministically (climate_phase_fires_natural_events). FFI compiles; GDScript additions gdlint-clean. Dylib rebuild needed for the live boot to call the new FFI (next). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../engine/scenes/headless/player_api_main.gd | 17 +++++++++++++++++ src/simulator/api-gdext/src/player_api.rs | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/game/engine/scenes/headless/player_api_main.gd b/src/game/engine/scenes/headless/player_api_main.gd index a5860a96..a95e726b 100644 --- a/src/game/engine/scenes/headless/player_api_main.gd +++ b/src/game/engine/scenes/headless/player_api_main.gd @@ -229,6 +229,23 @@ func _hydrate_player_api(num_players: int) -> void: # inter-player trade ever sources. _apply_resource_categories() + # p3-26 gap 2: stamp the natural-event configs so the headless climate phase fires + # wildfire/volcanic/… events. Same `#[serde(skip)]` re-stamp pattern — load AFTER + # `load_state_json`. Without this `events_config` is empty and no events fire. + _apply_events_config() + + +## p3-26 gap 2: stamp the natural-event category configs (DataLoader's merged +## `{category: {base_frequency, severity_weights, tiers, …}}`) onto `GdPlayerApi` via +## `set_events_config_json`. Consumed by `mc-turn`'s climate-phase event dispatch. +func _apply_events_config() -> void: + var cfg: Dictionary = DataLoader.get_ecological_events() + if cfg.is_empty(): + _emit_protocol_error("ecological events config empty — headless events will not fire") + return + var n: int = int(_api.set_events_config_json(JSON.stringify(cfg))) + _emit_event("events_config_api_loaded", {"categories": n}) + ## p3-25: build the resource id→category map ("luxury" | "strategic" | "bonus") ## from DataLoader and stamp it onto `GdPlayerApi` via diff --git a/src/simulator/api-gdext/src/player_api.rs b/src/simulator/api-gdext/src/player_api.rs index e6fa752d..c3a844d7 100644 --- a/src/simulator/api-gdext/src/player_api.rs +++ b/src/simulator/api-gdext/src/player_api.rs @@ -157,6 +157,17 @@ impl GdPlayerApi { .load_resource_categories_json(json.to_string().as_str()) as i64 } + /// p3-26 gap 2: load natural-event category configs (`{"wildfire":{…},"volcanic":{…}, + /// …}`, the shape `DataLoader.get_ecological_events()` emits) so the headless climate + /// phase can fire events (wildfire/volcanic/…). Returns the number of categories + /// loaded, or 0 on parse failure. Call AFTER `load_state_json` (the field is + /// `#[serde(skip)]`, not restored by a state load). + #[func] + pub fn set_events_config_json(&mut self, json: GString) -> i64 { + self.state + .load_events_config_json(json.to_string().as_str()) as i64 + } + /// Stamp the runtime `UnitsCatalog` (id → `UnitStats`) onto the held /// `GameState`. Distinct from `set_units_catalog_json` (which loads the /// tactical `ai_unit_catalog`): this is the same `mc_units::UnitsCatalog`