{
  "$comment": "GENERATED by tools/generate_script_api.ts from src/game/scripting/api/manifest.ts. Do not edit.",
  "apiLevel": 4,
  "ops": [
    {
      "path": "units.seatOf",
      "op": "u.seat",
      "kind": "query",
      "summary": "The seat owning a unit, or -1 if it exists but is unowned, or null if there is no such unit.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        }
      ],
      "returns": "int",
      "gas": 4,
      "door": "seatOf",
      "since": 1
    },
    {
      "path": "units.get",
      "op": "u.get",
      "kind": "query",
      "summary": "A snapshot of one unit: {id, seat, x, y, hp, maxHp, alive}. Null if it does not exist.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        }
      ],
      "returns": "json",
      "gas": 6,
      "door": "unitSnapshot",
      "since": 1
    },
    {
      "path": "players.resource",
      "op": "p.res",
      "kind": "query",
      "summary": "A seat's current stock of one resource, or 0 if that seat is not in the lobby.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "resource",
          "type": "string",
          "optional": false,
          "description": "Resource kind."
        }
      ],
      "returns": "number",
      "gas": 4,
      "door": "resourceOf",
      "seatParam": "seat",
      "since": 1
    },
    {
      "path": "time.tick",
      "op": "t.now",
      "kind": "query",
      "summary": "The current sim tick. The only clock a script has: `Date` does not exist inside the VM.",
      "params": [],
      "returns": "int",
      "gas": 2,
      "since": 1
    },
    {
      "path": "units.spawn",
      "op": "u.new",
      "kind": "mutation",
      "summary": "Spawn units for a seat at a tile. Returns the created ids, or an empty list if the spawn was refused.",
      "params": [
        {
          "name": "unitTypeId",
          "type": "string",
          "optional": false,
          "description": "Unit type id: base game, or a defOverrides clone this map declares."
        },
        {
          "name": "count",
          "type": "int",
          "optional": false,
          "description": "How many."
        },
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "at",
          "type": "point",
          "optional": false,
          "description": "Spawn tile."
        },
        {
          "name": "facing",
          "type": "number",
          "optional": true,
          "description": "Facing in RADIANS, like every other authored angle."
        }
      ],
      "returns": "entityList",
      "gas": 64,
      "gasPerResult": 8,
      "door": "spawnUnits",
      "seatParam": "seat",
      "catalogue": [
        "createUnits"
      ],
      "since": 1
    },
    {
      "path": "units.remove",
      "op": "u.del",
      "kind": "mutation",
      "summary": "Take a unit out of the world: no death, no corpse, no bounty, no kill credit.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        }
      ],
      "returns": "void",
      "gas": 16,
      "door": "removeUnit",
      "catalogue": [
        "removeUnit"
      ],
      "since": 1
    },
    {
      "path": "units.kill",
      "op": "u.kill",
      "kind": "mutation",
      "summary": "Kill a unit through the normal death path, so it drops a corpse, pays a bounty and grants kill credit.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        }
      ],
      "returns": "void",
      "gas": 16,
      "door": "killUnit",
      "catalogue": [
        "killUnit"
      ],
      "since": 1
    },
    {
      "path": "units.move",
      "op": "u.mv",
      "kind": "mutation",
      "summary": "Teleport a unit to a tile, handing back the tile it was holding.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "to",
          "type": "point",
          "optional": false,
          "description": "Destination tile."
        }
      ],
      "returns": "void",
      "gas": 24,
      "door": "moveUnit",
      "catalogue": [
        "moveUnit"
      ],
      "since": 1
    },
    {
      "path": "units.setHp",
      "op": "u.hp",
      "kind": "mutation",
      "summary": "Set current HP, clamped to 0..max. A non-finite value is refused rather than written.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "value",
          "type": "number",
          "optional": false,
          "description": "HP, or a percentage of max when `percent` is true."
        },
        {
          "name": "percent",
          "type": "boolean",
          "optional": true,
          "description": "Treat `value` as a percentage of max HP."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "setUnitHp",
      "catalogue": [
        "setUnitHp"
      ],
      "since": 1
    },
    {
      "path": "units.setMana",
      "op": "u.mana",
      "kind": "mutation",
      "summary": "Set current mana, clamped to 0..max. A non-finite value is refused.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "value",
          "type": "number",
          "optional": false,
          "description": "Mana."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "setUnitMana",
      "catalogue": [
        "setUnitMana"
      ],
      "since": 1
    },
    {
      "path": "units.setOwner",
      "op": "u.own",
      "kind": "mutation",
      "summary": "Hand a unit to another seat. The seat is range-checked, which the trigger action deliberately is not.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "setUnitOwner",
      "seatParam": "seat",
      "catalogue": [
        "setUnitOwner"
      ],
      "since": 1
    },
    {
      "path": "units.setInvulnerable",
      "op": "u.inv",
      "kind": "mutation",
      "summary": "Turn permanent invulnerability on or off. Turning it off also ends a timed shield.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "on",
          "type": "boolean",
          "optional": false,
          "description": "Invulnerable."
        }
      ],
      "returns": "void",
      "gas": 16,
      "door": "setUnitInvulnerable",
      "catalogue": [
        "setUnitInvulnerable"
      ],
      "since": 1
    },
    {
      "path": "units.order",
      "op": "u.ord",
      "kind": "mutation",
      "summary": "Order a group, applied inside this tick. The acting seat is REQUIRED — it is not inferred from the group, so a mixed group cannot be silently part-dropped.",
      "params": [
        {
          "name": "units",
          "type": "entityList",
          "optional": false,
          "description": "Units to order."
        },
        {
          "name": "order",
          "type": "string",
          "optional": false,
          "description": "One of move, attack_move, attack, follow, patrol, hold, stop."
        },
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "to",
          "type": "point",
          "optional": true,
          "description": "Target tile, for move / attack_move / patrol."
        },
        {
          "name": "target",
          "type": "entity",
          "optional": true,
          "description": "Target unit, for attack / follow."
        },
        {
          "name": "queue",
          "type": "boolean",
          "optional": true,
          "description": "Queue after the current orders."
        }
      ],
      "returns": "void",
      "gas": 32,
      "gasPerResult": 1,
      "door": "issueOrder",
      "seatParam": "seat",
      "catalogue": [
        "order"
      ],
      "since": 1
    },
    {
      "path": "units.board",
      "op": "u.board",
      "kind": "mutation",
      "summary": "Board a transport through the ordinary ownership, reach and capacity rules. No instant relocation by the authoring layer.",
      "params": [
        {
          "name": "units",
          "type": "entityList",
          "optional": false,
          "description": "Original units to board."
        },
        {
          "name": "transport",
          "type": "entity",
          "optional": false,
          "description": "Original transport."
        },
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "void",
      "gas": 32,
      "gasPerResult": 1,
      "door": "boardTransport",
      "seatParam": "seat",
      "catalogue": [
        "boardTransport"
      ],
      "since": 1
    },
    {
      "path": "units.isAboard",
      "op": "u.aboard",
      "kind": "query",
      "summary": "Whether an original unit is in this original transport cargo, not merely walking toward it.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "transport",
          "type": "entity",
          "optional": false,
          "description": "Original transport."
        }
      ],
      "returns": "boolean",
      "gas": 6,
      "door": "unitAboard",
      "since": 1
    },
    {
      "path": "players.setResource",
      "op": "p.set",
      "kind": "mutation",
      "summary": "Set a seat's stock of one resource. Rounded, floored at zero, and a non-finite value is refused.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "resource",
          "type": "string",
          "optional": false,
          "description": "Resource kind."
        },
        {
          "name": "value",
          "type": "number",
          "optional": false,
          "description": "New amount."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "setResource",
      "seatParam": "seat",
      "catalogue": [
        "setResource"
      ],
      "since": 1
    },
    {
      "path": "players.addResource",
      "op": "p.add",
      "kind": "mutation",
      "summary": "Add to a seat's stock. A negative delta subtracts; the result is floored at zero.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "resource",
          "type": "string",
          "optional": false,
          "description": "Resource kind."
        },
        {
          "name": "delta",
          "type": "number",
          "optional": false,
          "description": "Amount to add."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "addResource",
      "seatParam": "seat",
      "catalogue": [
        "addResource"
      ],
      "since": 1
    },
    {
      "path": "players.setAlliance",
      "op": "p.ally",
      "kind": "mutation",
      "summary": "Ally B to A's team, or break B out onto a team of its own. Directional, and both seats are checked.",
      "params": [
        {
          "name": "a",
          "type": "seat",
          "optional": false,
          "description": "The seat whose team is joined."
        },
        {
          "name": "b",
          "type": "seat",
          "optional": false,
          "description": "The seat that moves."
        },
        {
          "name": "allied",
          "type": "boolean",
          "optional": false,
          "description": "Allied."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "setAlliance",
      "seatParam": "a",
      "catalogue": [
        "setAlliance"
      ],
      "since": 1
    },
    {
      "path": "game.victory",
      "op": "g.win",
      "kind": "mutation",
      "summary": "Declare a seat victorious. Winning does not remove it from the board.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "declareOutcome",
      "seatParam": "seat",
      "catalogue": [
        "victory"
      ],
      "since": 1
    },
    {
      "path": "game.defeat",
      "op": "g.lose",
      "kind": "mutation",
      "summary": "Declare a seat defeated. Clears `isAlive`, which every win condition and the AI read.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "declareOutcome",
      "seatParam": "seat",
      "catalogue": [
        "defeat"
      ],
      "since": 1
    },
    {
      "path": "units.ofPlayer",
      "op": "q.seat",
      "kind": "query",
      "summary": "Ascending unit ids owned by a seat, capped at 500. Every scanned entity is metered.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "entityList",
      "gas": 8,
      "door": "queryUnits",
      "seatParam": "seat",
      "since": 2
    },
    {
      "path": "units.ofType",
      "op": "q.type",
      "kind": "query",
      "summary": "Ascending unit ids of a definition, capped at 500.",
      "params": [
        {
          "name": "unitTypeId",
          "type": "string",
          "optional": false,
          "description": "Definition id."
        }
      ],
      "returns": "entityList",
      "gas": 8,
      "door": "queryUnits",
      "since": 2
    },
    {
      "path": "units.inRadius",
      "op": "q.radius",
      "kind": "query",
      "summary": "Ascending unit ids in a disc, capped at 500.",
      "params": [
        {
          "name": "at",
          "type": "point",
          "optional": false,
          "description": "Centre in tiles."
        },
        {
          "name": "radius",
          "type": "number",
          "optional": false,
          "description": "Radius in tiles."
        }
      ],
      "returns": "entityList",
      "gas": 8,
      "door": "queryUnits",
      "since": 2
    },
    {
      "path": "physics.getCircle",
      "op": "ph.read",
      "kind": "query",
      "summary": "Read a circle body’s position in pixels, velocity, radius, active/static flags and contact count, or null.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        }
      ],
      "returns": "json",
      "gas": 8,
      "door": "circleSnapshot",
      "since": 2
    },
    {
      "path": "physics.setCircle",
      "op": "ph.circle",
      "kind": "mutation",
      "summary": "Attach a bounded deterministic circle body. Options use world pixels and velocity per RTS tick; walls default to the map bounds.",
      "params": [
        {
          "name": "unit",
          "type": "entity",
          "optional": false,
          "description": "Entity id of the unit."
        },
        {
          "name": "options",
          "type": "json",
          "optional": false,
          "description": "Circle2DBody options."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "setCircleBody",
      "since": 2
    },
    {
      "path": "timers.after",
      "op": "tm.after",
      "kind": "mutation",
      "summary": "Fire lifecycle.onTimer with this name after at least one tick. Names are local to this script.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "optional": false,
          "description": "Timer name."
        },
        {
          "name": "ticks",
          "type": "int",
          "optional": false,
          "description": "Ticks to wait."
        }
      ],
      "returns": "boolean",
      "gas": 12,
      "door": "scheduleScriptTimer",
      "since": 2
    },
    {
      "path": "timers.repeat",
      "op": "tm.repeat",
      "kind": "mutation",
      "summary": "Fire lifecycle.onTimer periodically. Replacing the same name resets its due tick.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "optional": false,
          "description": "Timer name."
        },
        {
          "name": "ticks",
          "type": "int",
          "optional": false,
          "description": "Period in ticks."
        }
      ],
      "returns": "boolean",
      "gas": 12,
      "door": "scheduleScriptTimer",
      "since": 2
    },
    {
      "path": "timers.cancel",
      "op": "tm.cancel",
      "kind": "mutation",
      "summary": "Cancel this script's named timer.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "optional": false,
          "description": "Timer name."
        }
      ],
      "returns": "boolean",
      "gas": 8,
      "door": "cancelScriptTimer",
      "since": 2
    },
    {
      "path": "triggers.run",
      "op": "tr.run",
      "kind": "mutation",
      "summary": "Validate and execute trigger actions through the shared trigger engine. Provides UI, camera, spells and all supported trigger actions.",
      "params": [
        {
          "name": "actions",
          "type": "json",
          "optional": false,
          "description": "Array of trigger action nodes."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "runTriggerActions",
      "since": 2
    },
    {
      "path": "custom.state",
      "op": "cg.read",
      "kind": "query",
      "summary": "Read the active custom runtime's plain state, or null for an RTS map.",
      "params": [],
      "returns": "json",
      "gas": 8,
      "door": "customState",
      "since": 2
    },
    {
      "path": "custom.command",
      "op": "cg.cmd",
      "kind": "mutation",
      "summary": "Send a validated command to the map's declared custom runtime.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "optional": false,
          "description": "Public command name."
        },
        {
          "name": "value",
          "type": "json",
          "optional": false,
          "description": "Command payload."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "customCommand",
      "since": 2
    },
    {
      "path": "progress.get",
      "op": "pg.get",
      "kind": "query",
      "summary": "Read one creator-owned player save bucket (plain JSON or null). Requires customGame and local simulation.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name, 1–64 letters, digits, underscores or hyphens."
        }
      ],
      "returns": "json",
      "gas": 32,
      "door": "progressGet",
      "since": 3
    },
    {
      "path": "progress.set",
      "op": "pg.set",
      "kind": "mutation",
      "summary": "Save finite JSON, up to 32 KiB per bucket and eight buckets per game. Survives rounds and browser restarts; signed-in cloud saving is optional.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name."
        },
        {
          "name": "data",
          "type": "json",
          "optional": false,
          "description": "Creator-defined progression, unlocks or inventory."
        }
      ],
      "returns": "boolean",
      "gas": 64,
      "door": "progressSet",
      "since": 3
    },
    {
      "path": "progress.exportCode",
      "op": "pg.export",
      "kind": "query",
      "summary": "Export a map-bound portable save code for a bucket up to 16 KiB. Returns an empty string if unavailable. Public map salts are not server secrets.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name."
        }
      ],
      "returns": "string",
      "gas": 256,
      "door": "progressExport",
      "since": 3
    },
    {
      "path": "progress.importCode",
      "op": "pg.import",
      "kind": "mutation",
      "summary": "Validate and import a portable save code into its matching bucket and game version. Refuses corrupt, foreign or oversized codes.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name."
        },
        {
          "name": "code",
          "type": "string",
          "optional": false,
          "description": "Portable save code."
        }
      ],
      "returns": "boolean",
      "gas": 256,
      "door": "progressImport",
      "since": 3
    },
    {
      "path": "progress.loadCloud",
      "op": "pg.load",
      "kind": "mutation",
      "summary": "Queue an optional signed-in cloud load. Completion raises progress-cloud-result; the creator chooses when to replace or merge offline data.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "progressCloud",
      "since": 3
    },
    {
      "path": "progress.syncCloud",
      "op": "pg.sync",
      "kind": "mutation",
      "summary": "Queue an optional cloud save. Completion raises progress-cloud-result. Local saving works when signed out or offline.",
      "params": [
        {
          "name": "bucket",
          "type": "string",
          "optional": false,
          "description": "Save bucket name."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "progressCloud",
      "since": 3
    },
    {
      "path": "network.request",
      "op": "nw.request",
      "kind": "mutation",
      "summary": "Queue a declared creator HTTPS endpoint after player permission. Fixed URL/method, JSON only, no cookies, two concurrent requests, six per minute, five-second timeout. Completion raises creator-api-result with requestId, endpointId, ok, status and data. Requires local simulation and browser CORS permission.",
      "params": [
        {
          "name": "endpoint",
          "type": "string",
          "optional": false,
          "description": "Declared endpoint id."
        },
        {
          "name": "payload",
          "type": "json",
          "optional": false,
          "description": "Request JSON for POST/PUT; ignored for GET."
        }
      ],
      "returns": "int",
      "gas": 64,
      "door": "creatorRequest",
      "since": 3
    },
    {
      "path": "city.stats",
      "op": "c.stats",
      "kind": "query",
      "summary": "A seat's city: {seat, founded, name, population, jobs, lots, tier, tierName, hallTier, taxPercent, incomePerMinute, upkeepPerMinute, demand: {residential, commercial, industrial}, coverage (percent of built lots per service), food: {made, eaten, percent}, problems: {blight, crime, burning, burnt, plague}, harmony, wants (what it is short of, worst first), goods (stock by good), lore}. Null without a city.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "json",
      "gas": 24,
      "door": "cityStats",
      "seatParam": "seat",
      "since": 4
    },
    {
      "path": "city.coverageAt",
      "op": "c.cov",
      "kind": "query",
      "summary": "Each service's coverage at a tile, 0 to 255, as of the last city step: {water, safety, faith, fire, health, learning, entertainment, market, park, academy}. Null off the map or without a city.",
      "params": [
        {
          "name": "at",
          "type": "point",
          "optional": false,
          "description": "The tile."
        }
      ],
      "returns": "json",
      "gas": 8,
      "door": "cityCoverageAt",
      "since": 4
    },
    {
      "path": "city.lotsIn",
      "op": "c.lots",
      "kind": "query",
      "summary": "The lots overlapping a rectangle of tiles (corners inclusive, any order), by id, at most 500: [{id, seat, x, y, w, h, zone, service, level, people, archetype, residents, jobs, landValue, distress, burning, burnt, sick, abandoned}]. Null without a city.",
      "params": [
        {
          "name": "from",
          "type": "point",
          "optional": false,
          "description": "One corner."
        },
        {
          "name": "to",
          "type": "point",
          "optional": false,
          "description": "The opposite corner."
        }
      ],
      "returns": "json",
      "gas": 32,
      "gasPerResult": 4,
      "door": "cityLotsIn",
      "since": 4
    },
    {
      "path": "city.lot",
      "op": "c.lot",
      "kind": "query",
      "summary": "One lot, in the shape city.lotsIn returns, or null.",
      "params": [
        {
          "name": "id",
          "type": "int",
          "optional": false,
          "description": "Lot id."
        }
      ],
      "returns": "json",
      "gas": 16,
      "door": "cityLot",
      "since": 4
    },
    {
      "path": "city.districts",
      "op": "c.dist",
      "kind": "query",
      "summary": "A seat's districts: [{id, seat, name, kind, people, hall: {x, y, w, h}, population, jobs, lots, level}]. Null without a city.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        }
      ],
      "returns": "json",
      "gas": 16,
      "gasPerResult": 2,
      "door": "cityDistricts",
      "seatParam": "seat",
      "since": 4
    },
    {
      "path": "city.command",
      "op": "c.cmd",
      "kind": "mutation",
      "summary": "Send any city command the HUD can, for a seat: the command object without its playerId, such as {type: 'city_road', x0, y0, x1, y1, tier} or {type: 'city_set_funding', service, percent}. Returns {ok, reason}; the seat pays as the player would. Null without a city.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "command",
          "type": "json",
          "optional": false,
          "description": "The city command, without playerId."
        }
      ],
      "returns": "json",
      "gas": 32,
      "door": "cityCommand",
      "seatParam": "seat",
      "since": 4
    },
    {
      "path": "city.zone",
      "op": "c.zone",
      "kind": "mutation",
      "summary": "Zone a rectangle of tiles for a seat: 'residential', 'commercial', 'industrial', 'farm', or 'none' to clear. Only tiles near the seat's roads take paint, as with the brush.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "from",
          "type": "point",
          "optional": false,
          "description": "One corner."
        },
        {
          "name": "to",
          "type": "point",
          "optional": false,
          "description": "The opposite corner."
        },
        {
          "name": "zone",
          "type": "string",
          "optional": false,
          "description": "The zone."
        },
        {
          "name": "density",
          "type": "int",
          "optional": true,
          "description": "Highest level a lot may reach, 1 to 3 (default 3)."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "cityCommand",
      "seatParam": "seat",
      "catalogue": [
        "cityZone"
      ],
      "since": 4
    },
    {
      "path": "city.placeService",
      "op": "c.svc",
      "kind": "mutation",
      "summary": "Place a service (water, safety, faith, fire, health, learning, entertainment, market, park, academy) with its top-left at a tile touching the seat's road. The seat pays its price.",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "service",
          "type": "string",
          "optional": false,
          "description": "The service."
        },
        {
          "name": "at",
          "type": "point",
          "optional": false,
          "description": "Its top-left tile."
        },
        {
          "name": "size",
          "type": "string",
          "optional": true,
          "description": "A size the service offers: safety 'small' (a watch post), 'medium' (the watch house) or 'large' (a citadel); faith 'small' (the shrine) or 'medium' (a temple). Omitted: the usual building."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "cityCommand",
      "seatParam": "seat",
      "catalogue": [
        "cityPlaceService"
      ],
      "since": 4
    },
    {
      "path": "city.setTax",
      "op": "c.tax",
      "kind": "mutation",
      "summary": "Set a seat's tax rate, percent; held to the map's ceiling (20 unless rules.city.tuning says otherwise).",
      "params": [
        {
          "name": "seat",
          "type": "seat",
          "optional": false,
          "description": "The seat this acts for. Checked against the calling script's authority."
        },
        {
          "name": "percent",
          "type": "number",
          "optional": false,
          "description": "Tax percent."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "cityCommand",
      "seatParam": "seat",
      "catalogue": [
        "citySetTax"
      ],
      "since": 4
    },
    {
      "path": "city.raiseDisaster",
      "op": "c.strike",
      "kind": "mutation",
      "summary": "Set a built home, shop, works or farm alight ('fire'), or start a plague outbreak in a home ('plague'). The city's own rules then run: a fire watch puts a fire out, healers cure a plague, else they spread. False if the lot cannot take it.",
      "params": [
        {
          "name": "lot",
          "type": "int",
          "optional": false,
          "description": "Lot id."
        },
        {
          "name": "kind",
          "type": "string",
          "optional": false,
          "description": "'fire' or 'plague'."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "cityStrike",
      "catalogue": [
        "cityDisaster"
      ],
      "since": 4
    },
    {
      "path": "terrain.set",
      "op": "te.set",
      "kind": "mutation",
      "summary": "Set a rectangle of tiles (corners inclusive, at most 4,096 tiles) to 'grass', 'dirt' or 'shallowWater'. Only grass, dirt, stumps and shallow water change; trees, ore, rock, deep water, lava and cliffs never do, and land and water never swap under a building, a city lot or a ship, or beside lava. Pathing, the minimap and the ground follow. Returns the tiles changed, or -1 if refused.",
      "params": [
        {
          "name": "from",
          "type": "point",
          "optional": false,
          "description": "One corner."
        },
        {
          "name": "to",
          "type": "point",
          "optional": false,
          "description": "The opposite corner."
        },
        {
          "name": "kind",
          "type": "string",
          "optional": false,
          "description": "'grass', 'dirt' or 'shallowWater'."
        }
      ],
      "returns": "int",
      "gas": 32,
      "gasPerResult": 1,
      "door": "setTerrain",
      "catalogue": [
        "setTerrain"
      ],
      "since": 4
    },
    {
      "path": "overlay.define",
      "op": "ov.def",
      "kind": "mutation",
      "summary": "Define or redefine a map layer: {name, legend, colors: ['#rrggbb', ...] (2 to 6, low to high), min (default 0), max (default 100), opacity (percent, default 60), smooth (default true), seats (who may see it; default everyone), show (turn it on now)}. Up to 8 layers; painted values are kept on a redefine.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id: 1 to 32 letters, digits, _ or -."
        },
        {
          "name": "options",
          "type": "json",
          "optional": false,
          "description": "The layer."
        }
      ],
      "returns": "boolean",
      "gas": 32,
      "door": "overlayDefine",
      "catalogue": [
        "overlayDefine"
      ],
      "since": 4
    },
    {
      "path": "overlay.paint",
      "op": "ov.paint",
      "kind": "mutation",
      "summary": "Paint a block of values, row by row, `width` wide, with its top-left at a tile: values[r * width + c]. Each value is placed on the ramp between min and max; a value that is not a number empties its tile. Up to 65,536 values a call, each metered.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id."
        },
        {
          "name": "at",
          "type": "point",
          "optional": false,
          "description": "Top-left tile."
        },
        {
          "name": "width",
          "type": "int",
          "optional": false,
          "description": "Tiles per row."
        },
        {
          "name": "values",
          "type": "json",
          "optional": false,
          "description": "The numbers, row-major."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "gasPerResult": 1,
      "door": "overlayPaint",
      "since": 4
    },
    {
      "path": "overlay.fill",
      "op": "ov.fill",
      "kind": "mutation",
      "summary": "Paint one value over a rectangle of tiles (corners inclusive). Null empties it.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id."
        },
        {
          "name": "from",
          "type": "point",
          "optional": false,
          "description": "One corner."
        },
        {
          "name": "to",
          "type": "point",
          "optional": false,
          "description": "The opposite corner."
        },
        {
          "name": "value",
          "type": "json",
          "optional": false,
          "description": "A number, or null."
        }
      ],
      "returns": "boolean",
      "gas": 24,
      "door": "overlayFill",
      "catalogue": [
        "overlayFill"
      ],
      "since": 4
    },
    {
      "path": "overlay.clear",
      "op": "ov.clear",
      "kind": "mutation",
      "summary": "Empty every tile of a layer; it stays defined.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id."
        }
      ],
      "returns": "boolean",
      "gas": 16,
      "door": "overlayClear",
      "catalogue": [
        "overlayClear"
      ],
      "since": 4
    },
    {
      "path": "overlay.remove",
      "op": "ov.del",
      "kind": "mutation",
      "summary": "Take a layer away.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id."
        }
      ],
      "returns": "boolean",
      "gas": 8,
      "door": "overlayRemove",
      "since": 4
    },
    {
      "path": "overlay.show",
      "op": "ov.show",
      "kind": "mutation",
      "summary": "Ask the HUD to show a layer now (true) or stop asking (false). Each player can still switch it in the overlay frame.",
      "params": [
        {
          "name": "id",
          "type": "string",
          "optional": false,
          "description": "Layer id."
        },
        {
          "name": "on",
          "type": "boolean",
          "optional": false,
          "description": "Show it."
        }
      ],
      "returns": "boolean",
      "gas": 8,
      "door": "overlayShow",
      "since": 4
    },
    {
      "path": "log",
      "op": "x.log",
      "kind": "utility",
      "summary": "Write a line to the trigger diagnostics. Never sim state, never hashed, and absent in a normal match.",
      "params": [
        {
          "name": "message",
          "type": "string",
          "optional": false,
          "description": "The line."
        }
      ],
      "returns": "void",
      "gas": 8,
      "since": 1
    }
  ],
  "registrations": [
    {
      "path": "lifecycle.onTimer",
      "handler": "onTimer",
      "summary": "A named timer became due.",
      "argFields": [
        {
          "name": "tick",
          "type": "int",
          "description": "Current tick."
        },
        {
          "name": "name",
          "type": "string",
          "description": "Timer name."
        }
      ],
      "returns": "ignored",
      "since": 2
    },
    {
      "path": "events.onEvent",
      "handler": "onEvent",
      "summary": "A simulation or custom-controller event delivered as plain data. Reentrant delivery is deferred.",
      "argFields": [
        {
          "name": "name",
          "type": "string",
          "description": "Event name."
        },
        {
          "name": "data",
          "type": "json",
          "description": "Event payload."
        }
      ],
      "returns": "ignored",
      "since": 2
    },
    {
      "path": "lifecycle.onInit",
      "handler": "onInit",
      "summary": "Run once, before tick 0, under the init gas budget. The place to build state a map needs from the start.",
      "argFields": [
        {
          "name": "tick",
          "type": "int",
          "description": "Always 0."
        }
      ],
      "returns": "ignored",
      "since": 1
    },
    {
      "path": "lifecycle.onLoad",
      "handler": "onLoad",
      "summary": "Run once after a save is loaded. READ-ONLY: a write that moves the storage digest disables the script, because a loaded match must hash identically to one that ran straight through.",
      "argFields": [
        {
          "name": "tick",
          "type": "int",
          "description": "The tick the save was taken at."
        }
      ],
      "returns": "ignored",
      "since": 1
    },
    {
      "path": "lifecycle.onTick",
      "handler": "onTick",
      "summary": "Run every tick, under the per-script budget. Events raised this tick arrive as one batch, not one call each.",
      "argFields": [
        {
          "name": "tick",
          "type": "int",
          "description": "The current tick."
        }
      ],
      "returns": "ignored",
      "since": 1
    },
    {
      "path": "hooks.onDamage",
      "handler": "onDamage",
      "summary": "Adjust a damage amount. Return a number to replace it; return nothing to leave it alone.",
      "argFields": [
        {
          "name": "attacker",
          "type": "entity",
          "description": "The dealing unit, or 0."
        },
        {
          "name": "target",
          "type": "entity",
          "description": "The receiving unit."
        },
        {
          "name": "amount",
          "type": "number",
          "description": "The amount as computed so far."
        }
      ],
      "returns": "number",
      "since": 1
    },
    {
      "path": "hooks.onOrder",
      "handler": "onOrder",
      "summary": "Observe an order before it is applied. Return 'cancel' to stop it.",
      "argFields": [
        {
          "name": "seat",
          "type": "seat",
          "description": "The seat issuing it."
        },
        {
          "name": "order",
          "type": "string",
          "description": "The order kind."
        },
        {
          "name": "units",
          "type": "entityList",
          "description": "The ordered group."
        }
      ],
      "returns": "cancel",
      "since": 1
    }
  ]
}
