Cobnuts colony sim · delve

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.

keyGDScript signalLua callbackargumentsfires when
job_completedjob_completedon_job_completeddef_id: str, x: int, y: intA produce job completes at a tile.
building_completedbuilding_completedon_building_completeddef_id: str, x: int, y: intA building finishes construction.
colonist_diedcolonist_diedon_colonist_dieddef_id: str, x: int, y: intA colonist dies.
research_completedresearch_completedon_research_completedproject: strA research project completes.
incident_startedincident_startedon_incident_startedincident: str, size: int, origin_civ: strAn incident begins (with its size, and the originating civ when civ-sourced).
attack_landedattack_landedon_attack_landedmagnitude: int, target_name: strA 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_wipedcolony_wipedon_colony_wiped(none)The colony has been wiped.
mental_break_startedmental_break_startedon_mental_break_startedpawn_name: str, break_def: strA colonist enters a mental break.
political_decisionpolitical_decisionon_political_decisionissue: str, choice: strA colony political decision is made.
trade_executedtrade_executedon_trade_executedgiven: int, received: intA caravan trade executes (offer/request values).
raid_succeededraid_succeededon_raid_succeededincident: str, objective: str, value: intA raid achieves its objective and escapes (feature 043).
raid_failedraid_failedon_raid_failedincident: str, raiders_dead: intA raid is defeated before achieving its objective (feature 043).
aged_intoaged_intoon_aged_intodef_id: str, stage: strA colonist crosses into a new life stage (feature 147).
colonist_capturedcolonist_capturedon_colonist_capturedname: strA colonist is carried off the map by a raid (feature 045).
colonist_rescuedcolonist_rescuedon_colonist_rescuedname: strA carried/captured colonist is freed before capture completes (feature 045).
colonist_ransomedcolonist_ransomedon_colonist_ransomedname: str, silver: intA captive is ransomed home for silver (feature 045).
birthbirthon_birthchild: str, parent: strA colonist is born to a colony couple (feature 148).
partnership_formedpartnership_formedon_partnership_formeda: str, b: strTwo colonists become partners (feature 148).
visitor_joinedvisitor_joinedon_visitor_joinedname: strA wanderer/visitor joins the colony as a colonist (feature 137).
animal_killedanimal_killedon_animal_killeddef_id: str, x: int, y: intA wild animal is killed by a hunter (feature 109).
machine_broke_downmachine_broke_downon_machine_broke_downdef_id: str, x: int, y: intAn autonomous machine wears out and breaks down (feature 133).
power_outagepower_outageon_power_outagex: int, y: int, supply: int, demand: intA power network drops below demand — brownout rising edge (feature 133).
leader_electedleader_electedon_leader_electedname: str, faction: strA colony leader is elected (feature 158).
trade_completedtrade_completedon_trade_completedgood: str, price: intA colonist buys a good from another at a market stall (feature 164).
government_form_changedgovernment_form_changedon_government_form_changedform: strThe colony adopts a government form (feature 161).
civ_tier_advancedciv_tier_advancedon_civ_tier_advancedciv: str, tier: intA rival civilization advances to a new research tier (feature 151).
civ_direction_tierciv_direction_tieron_civ_direction_tierciv: str, direction: str, tier: intA rival civilization reaches a new research-direction tier (feature 151).
command_refusedcommand_refusedon_command_refusedcommand: str, reason: str, x: int, y: intA player context-menu command (move/attack/draft/assign-to-bed/ransom/prioritize) was refused (#480/#908).
animal_tamedanimal_tamedon_animal_tamedname: str, def_id: str, x: int, y: intA colonist tames a wild animal (feature 110; narration wired in 187).
animal_bornanimal_bornon_animal_borndef_id: str, x: int, y: intA tamed pair produces livestock offspring (feature 110; narration wired in 187).
threat_incomingthreat_incomingon_threat_incomingincident: str, size: int, ticks_remaining: int, origin_civ: strA raid has been telegraphed and will spawn after a warning window (feature 821).
civ_relation_changedciv_relation_changedon_civ_relation_changedciv: str, score: int, delta: intThe colony's relation score with a rival civ moved (feature 153).
envoy_arrivedenvoy_arrivedon_envoy_arrivedciv: str, demand: strAn envoy from a rival civ arrives bearing a demand (feature 153).
demand_accepteddemand_acceptedon_demand_acceptedciv: str, demand: strThe colony's vote accepted a rival civ's demand (feature 153).
demand_refuseddemand_refusedon_demand_refusedciv: str, demand: strThe colony's vote refused a rival civ's demand (feature 153).
treaty_changedtreaty_changedon_treaty_changedciv: str, treaty: strThe treaty with a rival civ transitioned to war, peace, or a trade pact (feature 153).
livestock_diedlivestock_diedon_livestock_dieddef_id: str, x: int, y: intA tamed animal dies (e.g. starvation) — a livestock death, not a hunt (feature 1339).
animal_died_of_ageanimal_died_of_ageon_animal_died_of_agedef_id: str, x: int, y: intAn animal dies of old age (feature 1344).
threat_avertedthreat_avertedon_threat_avertedincident: str, size: int, origin_civ: strA telegraphed incident could not place its force and was averted (feature 1014).
class_consciousness_onsetclass_consciousness_onseton_class_consciousness_onsetname: str, x: int, y: int, level: intA 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.
theftthefton_theftgood: str, x: int, y: intAn conscious, priced-out colonist stole food from another without paying (feature 1589).
class_consciousness_milestoneclass_consciousness_milestoneon_class_consciousness_milestonecount: intA 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_abandonedorder_abandonedon_order_abandonedname: str, x: int, y: intA 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_clearedthreat_clearedon_threat_clearedcount: int, x: int, y: intThe 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_foundedcolony_foundedon_colony_foundedcount: intThe 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_turnedyear_turnedon_year_turnedyear: int, population: intAn 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_inheritedestate_inheritedon_estate_inheritedheir: str, relation: str, item: strA 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 (partnerchildsibling), item a representative inherited asset.
civ_revoltciv_revolton_civ_revoltciv: strA 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_ownershipfounding_ownershipon_founding_ownershipstructure_name: str, owner_name: strThe 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_formedaligned_group_formedon_aligned_group_formedsize: int, mean_class_consciousness: intAn 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_issuedstrike_demands_issuedon_strike_demands_issueddemands: str, size: int, x: int, y: intA 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_beganstrike_beganon_strike_begansize: int, x: int, y: intA 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_wonstrike_wonon_strike_wondemand: str, size: int, x: int, y: intA 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_outstrike_held_outon_strike_held_outsize: int, x: int, y: intA 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_risingciv_unrest_risingon_civ_unrest_risingciv: strA 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_contrastscarcity_contraston_scarcity_contrastcount: intPriced-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_setwage_fraction_seton_wage_fraction_setpermille: intThe 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_departedcolonist_departedon_colonist_departedname: strA 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.
marriagemarriageon_marriagea: str, b: strTwo 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_beggedcolonist_beggedon_colonist_beggedname: str, x: int, y: intAn 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_beganrevolt_beganon_revolt_begansize: int, x: int, y: intA 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_suppressedrevolt_suppressedon_revolt_suppressedfallen: int, x: int, y: intA 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_seizedrevolt_seizedon_revolt_seizedowner: str, x: int, y: intA 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_bleedingcolonist_started_bleedingon_colonist_started_bleedingx: int, y: intA 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_downedcolonist_downedon_colonist_downedx: int, y: intA 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_claimedorder_claimedon_order_claimedbatch: int, order: str, x: int, y: intA 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_resolvedbatch_resolvedon_batch_resolvedbatch: int, claimed: intA 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_foundedciv_foundedon_civ_foundedciv: strA 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_declaredwar_declaredon_war_declareda: str, b: strTwo 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_endedwar_endedon_war_endeda: str, b: strA war between two rival civilizations ended (Legends L5 / #2163). a/b are the belligerent civ def ids. The resolution twin of war_declared.
boss_slainboss_slainon_boss_slainname: strA 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_brokenexpedition_link_brokenon_expedition_link_brokenname: str, x: int, y: intA 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_incave_inon_cave_inx: int, y: int, z: intA 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_fellpawn_fellon_pawn_fellx: int, y: int, z: int, levels: intA 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_trainedanimal_trainedon_animal_trainedname: str, def_id: str, level: int, x: int, y: intA 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_bondedanimal_bondedon_animal_bondedname: str, def_id: str, x: int, y: intAn 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_fightsocial_fighton_social_fightaggressor: str, victim: strTwo 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.

calldescription
Mods.api_version() -> StringThe single API version (== the Lua api_version()).
Mods.active_mods() -> PackedStringArrayEnabled mod ids in load order (the content-registration read — what the active mods registered, frozen at load).
Mods.def_ids(category: String) -> PackedStringArrayLoaded def ids for a category (e.g. "buildings", "items", "pawns", "research").
Mods.def(category: String, id: String) -> DictionaryThe 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() -> PackedStringArrayStable 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) -> DictionaryOne 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-breakbreak_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() -> PackedStringArrayStable 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) -> DictionaryOne 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.

← Modding