Extend the type-variation pipeline to emit StyleBox variations, then dedup the repeated "modal panel" stylebox (bg=background.panel, border=border.panel, bw=2, corner=6) into shared Theme variations: - PanelModal (no content margins) ← hotkey_sheet, tutorial_overlay, turn_notification - PanelModalPadded (12/10 margins) ← statistics 4 inline StyleBoxFlat builds → theme_type_variation inheritance. Value-preserving (variation stylebox == the inline geometry/colours). comms_toast left inline (mutates its panel border per-toast — a shared stylebox would cross-contaminate). Verified: PanelModal/Padded baked into ui_theme.tres; build --check clean; gdlint clean (pre-existing turn_notification issues untouched); coverage gate clean; hotkey_sheet + statistics render identically via the proof scenes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
171 lines
9.3 KiB
JSON
171 lines
9.3 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "start_script",
|
|
"title": "Game-start script",
|
|
"description": "Declarative opening sequence for a game. The bundled default (default_dwarf_tribe) reproduces the freepeople tribe-founding prologue; scenario-makers author their own and point setup.json:start_script at the file id. The runner lives in Rust (mc-turn::start_script); this script is content. Every op maps to an existing simulation primitive — the runner sequences, it does not add new behaviour.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["id", "schema_version", "actors", "phases"],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"description": "Unique script id; matches the filename stem and the value of setup.json:start_script.",
|
|
"pattern": "^[a-z0-9_]+$"
|
|
},
|
|
"schema_version": {
|
|
"type": "integer",
|
|
"description": "Schema version this script targets. Current: 1.",
|
|
"const": 1
|
|
},
|
|
"label": {
|
|
"type": "string",
|
|
"description": "Human-readable name shown in scenario/new-game UI."
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"description": "What this opening does, for the scenario picker."
|
|
},
|
|
"rng_domain": {
|
|
"type": "string",
|
|
"description": "SeedDomain tag for this script's PRNG substream — keeps it disjoint from worldgen/other streams. Default: 'start_script'.",
|
|
"default": "start_script"
|
|
},
|
|
"actors": {
|
|
"type": "array",
|
|
"description": "Actor archetypes the ops reference by tag (e.g. the wanderers that converge, the tribe unit they become).",
|
|
"items": { "$ref": "#/$defs/actor" }
|
|
},
|
|
"params": {
|
|
"type": "object",
|
|
"description": "Named tunables that ops reference via *_ref. Each resolves a value (today: from a setup.json key via from_setup) so a scenario retunes counts/mode/radius without editing ops.",
|
|
"additionalProperties": { "$ref": "#/$defs/param" }
|
|
},
|
|
"phases": {
|
|
"type": "array",
|
|
"description": "Ordered scripted turns. The runner is at one phase at a time; End Turn resolves the current phase and enters the next. Must contain at least one phase of kind 'normal' (the hand-off to normal play).",
|
|
"minItems": 1,
|
|
"items": { "$ref": "#/$defs/phase" }
|
|
}
|
|
},
|
|
"$defs": {
|
|
"actor": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["tag", "unit_id"],
|
|
"properties": {
|
|
"tag": { "type": "string", "description": "Reference name used by ops (e.g. 'wanderer', 'tribe')." },
|
|
"unit_id": { "type": "string", "description": "Unit definition id this actor instantiates (must exist in the unit catalog)." },
|
|
"controlled": { "type": "boolean", "default": false, "description": "True if the player commands this actor (e.g. the tribe on the hand-off turn)." }
|
|
}
|
|
},
|
|
"param": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["from_setup"],
|
|
"properties": {
|
|
"from_setup": { "type": "string", "description": "setup.json key whose value this param resolves to." }
|
|
}
|
|
},
|
|
"phase": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["id", "display_turn", "kind"],
|
|
"properties": {
|
|
"id": { "type": "string", "description": "Phase id; used in validator error messages." },
|
|
"display_turn": { "type": "integer", "description": "Turn counter the HUD shows during this phase (need not be contiguous or monotonic)." },
|
|
"kind": {
|
|
"type": "string",
|
|
"enum": ["prologue", "normal"],
|
|
"description": "'prologue' = input-gated scripted turn; 'normal' = hand-off marker (its on_enter runs, then normal play takes over)."
|
|
},
|
|
"banner_key": { "type": "string", "description": "ThemeVocabulary key for the centered banner shown this phase." },
|
|
"banner_persist": { "type": "boolean", "description": "True = banner stays until the next phase; false = auto-dismiss. Default: true for 'prologue', false for 'normal'." },
|
|
"allowed_actions": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "Player input gate for this phase. Default: ['end_turn'] for 'prologue'."
|
|
},
|
|
"on_enter": {
|
|
"type": "array",
|
|
"description": "Ops run when the phase becomes active.",
|
|
"items": { "$ref": "#/$defs/op" }
|
|
},
|
|
"on_resolve": {
|
|
"type": "array",
|
|
"description": "Ops run on End Turn, before entering the next phase.",
|
|
"items": { "$ref": "#/$defs/op" }
|
|
}
|
|
}
|
|
},
|
|
"op": {
|
|
"type": "object",
|
|
"required": ["op"],
|
|
"description": "A single declarative operation. Each maps to an existing simulation primitive. The 'op' discriminator selects the variant; see each variant's required args.",
|
|
"properties": {
|
|
"op": {
|
|
"type": "string",
|
|
"enum": [
|
|
"place_spawn_box",
|
|
"roll_actor_directions",
|
|
"step_actors",
|
|
"converge_actors",
|
|
"spawn_unit",
|
|
"set_unit_actions",
|
|
"set_banner",
|
|
"lift_fog",
|
|
"emit_chronicle",
|
|
"found_city"
|
|
]
|
|
}
|
|
},
|
|
"allOf": [
|
|
{ "if": { "properties": { "op": { "const": "place_spawn_box" } } },
|
|
"then": { "required": ["actor", "count_ref", "mode_ref", "radius_ref"], "properties": {
|
|
"actor": { "type": "string", "description": "Actor tag to seed." },
|
|
"count_ref": { "type": "string", "description": "params key giving the per-mode wanderer count." },
|
|
"mode_ref": { "type": "string", "description": "params key for the start mode (tournament|custom)." },
|
|
"radius_ref": { "type": "string", "description": "params key for the spawn-box radius." } } } },
|
|
{ "if": { "properties": { "op": { "const": "roll_actor_directions" } } },
|
|
"then": { "required": ["actor", "mode_ref", "bias_ref"], "properties": {
|
|
"actor": { "type": "string" },
|
|
"mode_ref": { "type": "string", "description": "params key for the start mode (tournament|custom)." },
|
|
"bias_ref": { "type": "string", "description": "params key for the inward-bias probability." } } } },
|
|
{ "if": { "properties": { "op": { "const": "step_actors" } } },
|
|
"then": { "required": ["actor"], "properties": { "actor": { "type": "string" } } } },
|
|
{ "if": { "properties": { "op": { "const": "converge_actors" } } },
|
|
"then": { "required": ["actor", "radius_ref", "min_count_ref", "produce", "mode_ref", "cap_ref"], "properties": {
|
|
"actor": { "type": "string", "description": "Actor tag to merge." },
|
|
"radius_ref": { "type": "string", "description": "params key for the convergence radius." },
|
|
"min_count_ref": { "type": "string", "description": "params key for the minimum to form the group." },
|
|
"produce": { "type": "string", "description": "Actor tag produced by the merge (e.g. 'tribe')." },
|
|
"mode_ref": { "type": "string", "description": "params key selecting the founding-pop formula (start mode)." },
|
|
"cap_ref": { "type": "string", "description": "params key bounding the bonus founding pop." } } } },
|
|
{ "if": { "properties": { "op": { "const": "spawn_unit" } } },
|
|
"then": { "required": ["unit_id", "at"], "properties": {
|
|
"unit_id": { "type": "string" },
|
|
"at": { "description": "'centroid' | 'start' | {q,r}.", "oneOf": [ { "type": "string", "enum": ["centroid", "start"] }, { "type": "object", "additionalProperties": false, "required": ["q", "r"], "properties": { "q": { "type": "integer" }, "r": { "type": "integer" } } } ] },
|
|
"count": { "type": "integer", "minimum": 1, "default": 1 },
|
|
"controlled": { "type": "boolean", "default": false } } } },
|
|
{ "if": { "properties": { "op": { "const": "set_unit_actions" } } },
|
|
"then": { "required": ["actor", "actions"], "properties": {
|
|
"actor": { "type": "string" },
|
|
"actions": { "type": "array", "items": { "type": "string" } } } } },
|
|
{ "if": { "properties": { "op": { "const": "set_banner" } } },
|
|
"then": { "required": ["text_key"], "properties": {
|
|
"text_key": { "type": "string" },
|
|
"persist": { "type": "boolean", "default": false } } } },
|
|
{ "if": { "properties": { "op": { "const": "lift_fog" } } },
|
|
"then": { "required": ["area"], "properties": {
|
|
"area": { "type": "string", "description": "'spawn_box' or 'radius:N'." } } } },
|
|
{ "if": { "properties": { "op": { "const": "emit_chronicle" } } },
|
|
"then": { "required": ["event"], "properties": {
|
|
"event": { "type": "string", "description": "Chronicle event id." } } } },
|
|
{ "if": { "properties": { "op": { "const": "found_city" } } },
|
|
"then": { "required": ["from", "mode_ref", "cap_ref"], "properties": {
|
|
"from": { "type": "string", "description": "Actor tag to consume into the city." },
|
|
"mode_ref": { "type": "string", "description": "params key selecting the founding-pop formula (start mode)." },
|
|
"cap_ref": { "type": "string", "description": "params key bounding the bonus founding pop." } } } }
|
|
]
|
|
}
|
|
}
|
|
}
|