Cobnuts Modding API#
This file is generated from the live API surface by groundwork_sim::scripting::docs::render_contract(). Do not edit by hand — a headless test (cargo test -p groundwork-sim) string-compares it to the surface and fails on drift. To change it, change the surface (scripting/api.rs for events, scripting/docs.rs for the query list) and regenerate.
API version: 9.3.0 (semver — see the procedure below).
The modding API is read-only in this version: a mod can query state and react to events, but cannot issue commands that change the simulation. The same surface is reachable from GDScript (the Mods autoload — an instance of the ModApi class, named Mods so the GDScript identifier resolves to the node, not the class type) and Lua (mod scripts/*.lua); both share one event taxonomy and report the same version.
Events#
Each event has one canonical snake_case key. The GDScript signal is <key> (Mods.connect("<key>", handler)); the Lua callback is on_<key> (define a global function on_<key>(...)). Both receive identical arguments. For each event, Lua callbacks fire first, then the GDScript signal is emitted, in the post-tick dispatch window.
| key | GDScript signal | Lua callback | arguments | fires when | ||
|---|---|---|---|---|---|---|
job_completed | job_completed | on_job_completed | def_id: str, x: int, y: int | A produce job completes at a tile. | ||
building_completed | building_completed | on_building_completed | def_id: str, x: int, y: int | A building finishes construction. | ||
colonist_died | colonist_died | on_colonist_died | def_id: str, x: int, y: int | A colonist dies. | ||
research_completed | research_completed | on_research_completed | project: str | A research project completes. | ||
incident_started | incident_started | on_incident_started | incident: str, size: int, origin_civ: str | An incident begins (with its size, and the originating civ when civ-sourced). | ||
attack_landed | attack_landed | on_attack_landed | magnitude: int, target_name: str | A combat hit lands. magnitude is the impact ×100 (rounded); target_name is the struck target's display name (feature 970); no location in v1. | ||
colony_wiped | colony_wiped | on_colony_wiped | (none) | The colony has been wiped. | ||
mental_break_started | mental_break_started | on_mental_break_started | pawn_name: str, break_def: str | A colonist enters a mental break. | ||
political_decision | political_decision | on_political_decision | issue: str, choice: str | A colony political decision is made. | ||
trade_executed | trade_executed | on_trade_executed | given: int, received: int | A caravan trade executes (offer/request values). | ||
raid_succeeded | raid_succeeded | on_raid_succeeded | incident: str, objective: str, value: int | A raid achieves its objective and escapes (feature 043). | ||
raid_failed | raid_failed | on_raid_failed | incident: str, raiders_dead: int | A raid is defeated before achieving its objective (feature 043). | ||
aged_into | aged_into | on_aged_into | def_id: str, stage: str | A colonist crosses into a new life stage (feature 147). | ||
colonist_captured | colonist_captured | on_colonist_captured | name: str | A colonist is carried off the map by a raid (feature 045). | ||
colonist_rescued | colonist_rescued | on_colonist_rescued | name: str | A carried/captured colonist is freed before capture completes (feature 045). | ||
colonist_ransomed | colonist_ransomed | on_colonist_ransomed | name: str, silver: int | A captive is ransomed home for silver (feature 045). | ||
birth | birth | on_birth | child: str, parent: str | A colonist is born to a colony couple (feature 148). | ||
partnership_formed | partnership_formed | on_partnership_formed | a: str, b: str | Two colonists become partners (feature 148). | ||
visitor_joined | visitor_joined | on_visitor_joined | name: str | A wanderer/visitor joins the colony as a colonist (feature 137). | ||
animal_killed | animal_killed | on_animal_killed | def_id: str, x: int, y: int | A wild animal is killed by a hunter (feature 109). | ||
machine_broke_down | machine_broke_down | on_machine_broke_down | def_id: str, x: int, y: int | An autonomous machine wears out and breaks down (feature 133). | ||
power_outage | power_outage | on_power_outage | x: int, y: int, supply: int, demand: int | A power network drops below demand — brownout rising edge (feature 133). | ||
leader_elected | leader_elected | on_leader_elected | name: str, faction: str | A colony leader is elected (feature 158). | ||
trade_completed | trade_completed | on_trade_completed | good: str, price: int | A colonist buys a good from another at a market stall (feature 164). | ||
government_form_changed | government_form_changed | on_government_form_changed | form: str | The colony adopts a government form (feature 161). | ||
civ_tier_advanced | civ_tier_advanced | on_civ_tier_advanced | civ: str, tier: int | A rival civilization advances to a new research tier (feature 151). | ||
civ_direction_tier | civ_direction_tier | on_civ_direction_tier | civ: str, direction: str, tier: int | A rival civilization reaches a new research-direction tier (feature 151). | ||
command_refused | command_refused | on_command_refused | command: str, reason: str, x: int, y: int | A player context-menu command (move/attack/draft/assign-to-bed/ransom/prioritize) was refused (#480/#908). | ||
animal_tamed | animal_tamed | on_animal_tamed | name: str, def_id: str, x: int, y: int | A colonist tames a wild animal (feature 110; narration wired in 187). | ||
animal_born | animal_born | on_animal_born | def_id: str, x: int, y: int | A tamed pair produces livestock offspring (feature 110; narration wired in 187). | ||
threat_incoming | threat_incoming | on_threat_incoming | incident: str, size: int, ticks_remaining: int, origin_civ: str | A raid has been telegraphed and will spawn after a warning window (feature 821). | ||
civ_relation_changed | civ_relation_changed | on_civ_relation_changed | civ: str, score: int, delta: int | The colony's relation score with a rival civ moved (feature 153). | ||
envoy_arrived | envoy_arrived | on_envoy_arrived | civ: str, demand: str | An envoy from a rival civ arrives bearing a demand (feature 153). | ||
demand_accepted | demand_accepted | on_demand_accepted | civ: str, demand: str | The colony's vote accepted a rival civ's demand (feature 153). | ||
demand_refused | demand_refused | on_demand_refused | civ: str, demand: str | The colony's vote refused a rival civ's demand (feature 153). | ||
treaty_changed | treaty_changed | on_treaty_changed | civ: str, treaty: str | The treaty with a rival civ transitioned to war, peace, or a trade pact (feature 153). | ||
livestock_died | livestock_died | on_livestock_died | def_id: str, x: int, y: int | A tamed animal dies (e.g. starvation) — a livestock death, not a hunt (feature 1339). | ||
animal_died_of_age | animal_died_of_age | on_animal_died_of_age | def_id: str, x: int, y: int | An animal dies of old age (feature 1344). | ||
threat_averted | threat_averted | on_threat_averted | incident: str, size: int, origin_civ: str | A telegraphed incident could not place its force and was averted (feature 1014). | ||
class_consciousness_onset | class_consciousness_onset | on_class_consciousness_onset | name: str, x: int, y: int, level: int | A colonist's class_consciousness first crosses the narration threshold (feature 1668): the onset of class consciousness. name is the colonist, x/y their tile, level the class_consciousness level at the crossing. | ||
theft | theft | on_theft | good: str, x: int, y: int | An conscious, priced-out colonist stole food from another without paying (feature 1589). | ||
class_consciousness_milestone | class_consciousness_milestone | on_class_consciousness_milestone | count: int | A colony class_consciousness milestone: the count of conscious colonists first crosses the tuning's milestone_threshold (feature 1690) — the colony-wide tipping point. count is how many colonists now resent the divide. | ||
order_abandoned | order_abandoned | on_order_abandoned | name: str, x: int, y: int | A mental break destroyed a drafted colonist's live manual move order (feature 1695); the colonist is auto-undrafted. name is the colonist, x/y their tile. | ||
threat_cleared | threat_cleared | on_threat_cleared | count: int, x: int, y: int | The last living on-map hostile is gone and the colony survives (feature 829) — the generic win side of colony_wiped, independent of a raid party. count is how many hostiles were repelled, x/y the last cleared hostile's tile. | ||
colony_founded | colony_founded | on_colony_founded | count: int | The colony is founded at world start (feature 1565), emitted exactly once. count is the number of starting settlers. Seeds the chronicle's founding entry. | ||
year_turned | year_turned | on_year_turned | year: int, population: int | An in-game year turns (feature 1566) — the colony's yearly chronicle heartbeat. Emitted once per crossed year (one per year on a multi-year catch-up). year is the in-game year reached (1 = surviving the founding year), population the living-colonist count when the turn is chronicled. Surviving the FIRST year narrates distinctly. | ||
estate_inherited | estate_inherited | on_estate_inherited | heir: str, relation: str, item: str | A dead owner's estate (owned property and/or banked claim) passes to a living heir (feature 1594). heir is the heir's name, relation the tie to the deceased (partner | child | sibling), item a representative inherited asset. |
civ_revolt | civ_revolt | on_civ_revolt | civ: str | A rival civilization's have-nots revolt (feature 1770) — its sustained exploitation drove internal unrest across the revolt threshold, zeroing its research and briefly locking its age. The rival-side mirror of the player's class_consciousness cost. civ is the revolting civilization's CivDef id. | ||
founding_ownership | founding_ownership | on_founding_ownership | structure_name: str, owner_name: str | The colony grants founding ownership of a surplus-producing structure to a colonist (feature 1596) — emitted only when an owner is chosen (a communal vote emits nothing). structure_name is the granted building's display name, owner_name the new owner's display name. Seeds the founding chronicle line in history voice. | ||
aligned_group_formed | aligned_group_formed | on_aligned_group_formed | size: int, mean_class_consciousness: int | An aligned conscious group (proto-union) first forms (feature 1862): the largest bloc's size first crosses the tuning's solidarity_narration_min_size this slow-cadence step — 'the workers are uniting', the beat above the colony-seethe milestone. size is the bloc's member count, mean_class_consciousness its integer mean class_consciousness. | ||
strike_demands_issued | strike_demands_issued | on_strike_demands_issued | demands: str, size: int, x: int, y: int | A collective strike begins and issues its demands (feature 1591, epic #1587 C4): the aligned conscious group crossed the strike threshold. demands is the comma-joined list of demand display names (what would end the strike), size the striking group size, x/y a representative member's tile. | ||
strike_began | strike_began | on_strike_began | size: int, x: int, y: int | A strike's stoppage begins (feature 1591): the striking group lays down its tools and refuses all owner-benefiting work. size is the group size, x/y a representative member's tile. | ||
strike_won | strike_won | on_strike_won | demand: str, size: int, x: int, y: int | A strike is won (feature 1591): a demand's live-economy condition became true, so the strike ends and the strikers' class_consciousness is relieved. demand is the winning demand's display name, size the group size, x/y a representative member's tile. Chronicle-significant. | ||
strike_held_out | strike_held_out | on_strike_held_out | size: int, x: int, y: int | A strike collapses on hold-out (feature 1591): the authored duration elapsed with no demand met, so the strike ends and the strikers' class_consciousness ratchets up (the escalation toward revolt). size is the group size, x/y a representative member's tile. Chronicle-significant. | ||
civ_unrest_rising | civ_unrest_rising | on_civ_unrest_rising | civ: str | A rival civilization's internal unrest first crosses the narration threshold (feature 1774) — its people grow restless under sustained exploitation, the story beat before the revolt (civ_revolt). Edge-triggered once per upward crossing. The rival-side mirror of the player's class_consciousness_onset. civ is the affected civilization's CivDef id. | ||
scarcity_contrast | scarcity_contrast | on_scarcity_contrast | count: int | Priced-out colonists went hungry beside a withheld food surplus (feature 1925) — the inequality-thesis flagship structural-contrast line, "{count} went hungry while the granary held plenty." Colony-scope, edge-triggered, rate-limited. count is how many colonists went hungry. | ||
wage_fraction_set | wage_fraction_set | on_wage_fraction_set | permille: int | The owner set the wage fraction (feature 1593, epic #1587 class-resistance C6) — the player's single wage lever, commanded at runtime. Fires only when the effective wage actually changes; the routing split moves and (with the class-resistance layer armed) a striking group's wage-floor demand may resolve. Chronicle-significant. permille is the new worker share of routed surplus, per-mille. | ||
colonist_departed | colonist_departed | on_colonist_departed | name: str | A colonist left the colony rather than starve (feature 1924, epic #1587) — the Hirschman exit response to the class divide: sustainedly conscious, near starvation, priced out of food, and not held by a living partner or child, they walk off the map rather than starve, steal, or revolt. Colony-scope, edge-triggered once per colonist. name is the departing colonist's name. | ||
marriage | marriage | on_marriage | a: str, b: str | Two colonists wed (feature 1520, Legends L3) — the marriage-upgrade beat, the producer of the symmetric core:married relation edge in the historical record. a and b are the spouses' names. | ||
colonist_begged | colonist_begged | on_colonist_begged | name: str, x: int, y: int | An conscious, priced-out colonist begged a wealthier one for food (feature 1922) — the soft, pre-theft middle rung of the class-resistance repertoire; no item changes hands. name is the beggar, x/y their tile. | ||
revolt_began | revolt_began | on_revolt_began | size: int, x: int, y: int | A revolt begins (feature 1592, epic #1587 C5) — the aligned conscious group topped out (a collapsed strike, sustained max class_consciousness, or starvation deaths among its members) and turned hostile through the threat surface to contest ownership. size is the revolting group size, x/y a representative member's tile. Chronicle-significant. | ||
revolt_suppressed | revolt_suppressed | on_revolt_suppressed | fallen: int, x: int, y: int | A revolt is suppressed (feature 1592): the colony's defense won. Survivors' class_consciousness is relieved and the fallen seed the next uprising's accrual. fallen is how many rebels died, x/y a representative tile. Chronicle-significant. | ||
revolt_seized | revolt_seized | on_revolt_seized | owner: str, x: int, y: int | A revolt seizes an owned target (feature 1592): the rebels held it past the authored duration and ownership transferred to the uprising's leader (or the commons). owner is the new owner's display name, x/y the seized building's tile. The thesis's sharpest beat — chronicle-significant. | ||
colonist_started_bleeding | colonist_started_bleeding | on_colonist_started_bleeding | x: int, y: int | A player colonist first starts bleeding from an untended wound (feature 2107, epic::foundations) — the false→true edge of the colonist's bleed state. Feeds the #266/#267 auto-pause framework's on_bleeding category (the attention beat that completes the #2097 AlertKind::Bleeding on-ramp). Edge-triggered once per onset; colonist-only. x/y are the colonist's tile at onset. | ||
colonist_downed | colonist_downed | on_colonist_downed | x: int, y: int | A player colonist is first downed by injury (feature 2107, epic::foundations) — the frame the colonist crosses the down threshold. Feeds the #266/#267 auto-pause framework's on_downed category. Edge-triggered once per onset; colonist-only. x/y are the colonist's tile at onset. | ||
order_claimed | order_claimed | on_order_claimed | batch: int, order: str, x: int, y: int | A colonist actually CLAIMED the front of a prioritized order that carried a batch id (feature 2353) — the presentation-only readback the shell tallies per batch to show the honest acting-pawn count in one batch toast (vs the raw selection size). A pawn that loses a single-target claim race emits nothing. Non-authoritative: never serialized, absent from goldens/state_hash, no RNG. order is the prioritize verb's stable id; x/y are the claimed target's tile. | ||
batch_resolved | batch_resolved | on_batch_resolved | batch: int, claimed: int | A right-click prioritize fan-out identified by batch is FULLY resolved (feature 2419) — every dispatched colonist has either claimed the order or given it up. claimed is the EXACT acting-pawn count (colonists that took the order). The shell fires the one batch toast from THIS signal (the sim-authoritative count), replacing the old shell-side quiescence+cap settle heuristic. Emitted exactly ONCE per batch, the tick the last dispatched pawn resolves (even when claimed == 0). Non-authoritative: never serialized, absent from goldens/state_hash, no RNG. | ||
civ_founded | civ_founded | on_civ_founded | civ: str | A rival civilization is founded at world start (feature #2120), emitted once per civ. civ is the civ def's display id. Seeds the civ's earliest Legends page entry. The internal civ_id/site are NOT taxonomy args (they never reach the modding surface). | ||
war_declared | war_declared | on_war_declared | a: str, b: str | Two rival civilizations went to war (Legends L5 / #2163). a/b are the belligerent civ def ids. The first-class war-outbreak beat, replacing L5's borrowed treaty_changed render tag on the Legends war member event; emitted by the civ↔civ war producer (#2162). | ||
war_ended | war_ended | on_war_ended | a: str, b: str | A war between two rival civilizations ended (Legends L5 / #2163). a/b are the belligerent civ def ids. The resolution twin of war_declared. | ||
boss_slain | boss_slain | on_boss_slain | name: str | A themed-site boss was slain (#1546, S4, epic::themed-sites). name is the boss's generated proper name. The chronicle-significant payoff of an expedition; x/y are the death tile. | ||
expedition_link_broken | expedition_link_broken | on_expedition_link_broken | name: str, x: int, y: int | A dispatched expedition party's link home first went intact→broken (#3035, epic::chunked-world, follow-up to #2907) — the party is now cut off on a disjoint resident island (the #2906 disjoint-routing contract), a truthful "you've lost contact" signal. Colony-scope, edge-triggered ONCE per break (re-armed if the link is restored). name is the party pawn's name; x/y are its last-known tile (click-to-jump). | ||
cave_in | cave_in | on_cave_in | x: int, y: int, z: int | A cave-in resolved at a tile (#3103, epic::z-axis Z8): a cell lost its structural support and came down, damaging any structure there and wounding any colonist under it. x/y/z are the collapsing tile (click-to-jump). Fires only on a world that authors support tuning; shipped core does since #3568, so this DOES fire by default. Fires only where the collapse had a consequence (a structure damaged or a colonist wounded) — a cell that came down on nothing is not an event. APPENDED LAST so no existing event's position shifts. | ||
pawn_fell | pawn_fell | on_pawn_fell | x: int, y: int, z: int, levels: int | A pawn fell to a lower level and was hurt by the landing (#4479, epic::z-axis). Falling is an INVARIANT, not a collapse consequence (#3566), so this fires for any unsupported pawn — a generated core:open_air tile drops one with no collapse anywhere. x/y/z are the LANDING tile (click-to-jump takes you where the pawn now is, not the air it left); levels is how far it dropped. Fires only for a fall that actually moved and wounded somebody, and only on a world that authors support tuning. APPENDED LAST so no existing event's position shifts. | ||
animal_trained | animal_trained | on_animal_trained | name: str, def_id: str, level: int, x: int, y: int | A colonist advances a tamed animal's obedience training (#2375). level is the obedience just reached, so one event covers a first lesson and a mastery. APPENDED LAST (with animal_bonded below) so no existing event's position shifts — these two belong beside animal_tamed by subject, but the key order IS the published taxonomy, so position is append-only regardless of topic. | ||
animal_bonded | animal_bonded | on_animal_bonded | name: str, def_id: str, x: int, y: int | An animal bonds with the colonist training it (#2375) — the game's first persistent colonist-animal link. Fires AT MOST ONCE per animal: a bond is never replaced or re-rolled. Appended last, same rule as animal_trained above. | ||
social_fight | social_fight | on_social_fight | aggressor: str, victim: str | Two colonists who resent each other came to blows (#2931) — the escalation of a chat argument into a bounded, non-lethal scuffle, and the FIRST narrated event the chat system emits (feature 51's C-X3 declared it presentation-free; #2931 exists to close exactly that gap). The pair is directional: aggressor started it and victim took the bruise. Appended last, same append-only rule as the two above. |
Queries (GDScript Mods, read-only)#
Reachable on the global Mods autoload singleton (an instance of the ModApi class — call these on Mods, e.g. Mods.api_version(), not on the class). Returns are snapshots (copies); mutating them does not affect the simulation.
| call | description |
|---|---|
Mods.api_version() -> String | The single API version (== the Lua api_version()). |
Mods.active_mods() -> PackedStringArray | Enabled mod ids in load order (the content-registration read — what the active mods registered, frozen at load). |
Mods.def_ids(category: String) -> PackedStringArray | Loaded def ids for a category (e.g. "buildings", "items", "pawns", "research"). |
Mods.def(category: String, id: String) -> Dictionary | The full field set of one def (036 widened this from { "id", "name" } to every field the category defines; "id"/"name" remain). Categories: buildings, build_menu_categories, materials, material_categories, items, pawns, animals, research, directions, plants, recipes, skills, traits, needs, injuries, attacks, weapons, apparel, modules, policies, thoughts, incidents, sites, ages, civs, weathers. List fields are PackedStringArray; (key,value) fields (e.g. a building's build_cost) are Array[{ "key", "value" }]; ordered int-pair fields (e.g. a formation's slots) are Array[[x, y]] preserving order. Empty if absent. |
Mods.colony_stats() -> Dictionary | { "tick", "population", "researched" } — current read-only colony summary. 2046 adds "weather" (the current weather type id, e.g. "core:rain", or "" when the world has no active weather) and "weather_remaining" (int, ticks left in the current weather spell, 0 when idle) — a read-only projection of the live sim weather (#839). |
Mods.colonist_ids() -> PackedStringArray | Stable handles of the live colonists (player Colonists only — raiders/merchant excluded), sorted. Each handle is an entity id as a decimal string, stable across save/load (036). |
Mods.colonist(id: String) -> Dictionary | One colonist's read-only snapshot (036): { "id", "name", "mood": { level, low_ticks, thoughts, trait_contributions, band, enabled, break_pressure_rising, work_drag_pct, break_speed_pct, break_speed_contributions, (break_name, break_severity when broken) }, "job": { kind, progress } or {}, "needs": [{ id, reserve, capacity, action_threshold, critical_ticks }] (#4886 BREAKING: urgency was replaced by reserve, and the value INVERTED — reserve is how much is LEFT, so capacity = just satisfied and 0 = consequences begin. capacity is the percent basis for a need bar, and is 0 when the def does not resolve, so guard the division. action_threshold is stated on the RESERVE scale — the level at/below which the colonist acts. A mod that read urgency will fail loudly on the missing key; one that renamed it without inverting will read every fed colonist as starving), "skills": [{ id, level }], "traits": [...], "flags": [...], "equipped": { "weapon", "apparel", "weapon_quality", "apparel_quality" }, "class", "class_name" }. Each mood.thoughts entry is { "id", "name", "mood_offset" (int, signed), "remaining_ticks" (int, 430: ticks until the thought expires), "duration_ticks" (int, #1169: the def's authored full duration — the DENOMINATOR for a proportion; 0 when the def is unresolved, so treat 0 as "no denominator" rather than dividing by it), "stack" (int, >= 1, #1169: live instance count), "stacked_offset" (int, signed, #1192: this thought's TOTAL contribution — mood_offset * stack, the product the sim actually sums; sum THIS, not mood_offset, to reconcile the breakdown to mood.level. Equal to mood_offset at stack == 1; 0 for an unresolved def, which contributes nothing however deep its stack) }. To compute how far through its life a thought is, use (remaining_ticks + (stack - 1) * duration_ticks) / (stack * duration_ticks) — a stack shares ONE timer and drains one duration_ticks window at a time, so the naive remaining_ticks / duration_ticks jumps back to full at each window boundary. Each mood.trait_contributions entry is { "id", "name", "mood_offset" (int, signed) } (431: one per trait whose baseline mood offset is non-zero; the shown thought stacked_offsets + trait offsets reconcile to mood.level at any stack depth — #1192; the sole exception is the #2473 bound to [-100, 100], past which the items sum to the UNCLAMPED total and mood.level is that total clamped). 2643 exposes five more mood keys the inspector already read: band (str, the happiness-band word — #690), enabled (bool, whether the mood DESIGN LAYER is running — #2511; false distinguishes a content colonist from one whose mood is switched off, both level 0), break_pressure_rising (bool, #753), and — only while the colonist is mid-break — break_name (str) and break_severity (str: Minor/Major/Extreme, #520). work_drag_pct (int, signed; 2683/#2471) is the mood-driven work-output drag: 0 at/above the knee, negative below (e.g. -60 ⇒ 40% output). break_speed_pct (int; 2789/#487) is the combined mean-time-between-break factor (100 = neutral; lower breaks sooner, e.g. a Volatile colonist), and each break_speed_contributions entry is { "id", "name", "mtb_mean_pct" (int, 100 = neutral) } — one per trait whose break-speed factor is non-neutral (empty for an untraited or all-neutral colonist), the per-trait breakdown of break_speed_pct. The equipped sub-dict carries the worn weapon id ("" if unarmed) and apparel ids, plus (1074) each piece's per-instance crafted quality: weapon_quality (int, 0 = baseline) and apparel_quality (PackedInt32Array, index-aligned with apparel). class (1671: the colonist's derived social-class stable id, e.g. "core:elite") and class_name (its display name) are present only when the colonist has a derived class, and are omitted entirely for a class-less colonist. Empty if the handle does not resolve. |
Mods.colonists() -> Array[Dictionary] | All colonists' snapshots in colonist_ids() order — the one-call aggregate of colonist(id) (036). |
Mods.rival_ids() -> PackedStringArray | Stable handles of the live rival civilizations (1771), in CivId-ascending order. Each handle is a civ id as a decimal string. Empty before worldgen / when no civs are seeded. |
Mods.rival(id: String) -> Dictionary | One rival civilization's live intel by id (1771): { "id", "name", "age", "exploitation" (int, #1768 — how hard the civ exploits its people), "unrest" (int, #1770), "revolt_locked" (bool — currently locked in revolt), "opinion" (int, [-100,100] — the colony's standing with it), "treaty" ("war"/"peace"/"trade_pact"), "distance" (int — overworld straight-line distance from the colony) }. Read from the per-tick snapshot (the same projection the Rival Intel panel renders). Empty if the id does not resolve to a current rival. GDScript-only (the Lua host reads the authored posture via def("civs", id) instead). |
Mods.rivals() -> Array[Dictionary] | All rival civilizations' live intel in CivId-ascending order — the one-call aggregate of rival(id) (1771). Empty when no rival civilizations are seeded. |
Lua surface#
A Lua mod (assets/mods/<mod>/scripts/*.lua) may define any on_<key> callback above and call the globals info(message) (logs) and api_version() (returns the version string). Missing callbacks are skipped; a callback error is logged, not fatal.
Lua also has the def query globals def_ids(category) (returns an array table of id strings) and def(category, id) (returns a table with the same full field set as the GDScript Mods.def, or nil if absent), reading a content view frozen at load. List fields are array subtables; (key,value) fields are arrays of { key = …, value = … } tables; ordered int-pair fields (e.g. a formation's slots) are arrays of { x = …, y = … } tables preserving order. The live colonist queries (colonist_ids/colonist/colonists) are GDScript-only in this version — the Lua host holds no live simulation handle, so live-entity queries are deferred to a future feature. Lua and GDScript stay at version parity.
Versioning & breaking changes#
This surface is semver'd; api_version() is the single source of truth. Additive changes (a new event, a new query, a new optional argument) are a minor bump and require no migration. Breaking changes (renaming/removing an event or query, changing an argument's name/type/order, or changing firing semantics) are a major bump plus a dated migration note. See docs/modding-api-migrations.md.