feat(@projects/@magic-civilization): 🔌 p3-26 gap 2 — load event configs in the headless harness (events now LIVE)

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) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-26 10:55:34 -04:00
parent 9bc2b2c34b
commit 64ea08b7ce
2 changed files with 28 additions and 0 deletions

View file

@ -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

View file

@ -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`