# Base building and economy for strategy skirmishes (rts.production) ## What it is for `rts.production` is the economy and the base of a real-time strategy skirmish: the map (obstacles, resource nodes, each faction's start), a stock of resources and a supply count per faction, buildings placed by workers and raised over time, units trained in queues with rally points and tech requirements, and the end of the match when a faction has no building left. Every faction plays the same documents (units, buildings, maps). Workers of `units.command` bank their loads here. The computer opponent is a separate kit: see [Strategy opponent](guide:rts-ai). ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "rts.production" } gripforge_gamekit_configure { project_id: "gkp_…", id: "rts.production", config: { "map": "crossing", "player": "north", "queueSize": 5 } } ``` It requires `game.engine`, `units.command` and `combat.health`. Resource nodes on the map are documents of `resource.nodes`. A neutral starter set of units (worker, soldier, archer, guardian), buildings and one map (`crossing`) is built in; a project that defines the collections uses its own. Targets: web and the Rust kit host. Its interface (the resource bar and the command card) is a fragment of `ui.webui`. ## First example The parity fixture `rts.production.skirmish` configures a small map and two factions, then plays the services: ```json { "map": "small", "player": "north", "factions": [ { "id": "north", "name": "North", "color": "#4f8cff", "start": 0, "ai": "none" }, { "id": "south", "start": 1 } ], "startStock": { "ore": 300, "wood": 100 }, "startUnits": [ { "type": "worker", "count": 2 } ], "queueSize": 2, "supplyLimit": 9 } ``` The calls are `check("north", "unit", "worker")`, `place("north", "house", [-12.5, -12.5], builders)` (the site is paid at once and raised by the builders), `place("north", "barracks", …)` once `house` is finished, then `train(building, "worker")` several times against the queue size. Each answer is `{ ok, reason? }`, and the Rust host gives the frozen trace the TypeScript side recorded. ## How it plays - The match begins on the first tick: obstacles and nodes go down, each faction gets its `startStock`, its `startBuildings` (finished) and its `startUnits` at its start of the map. The player's own body stays hidden at the base and only anchors the strategy camera. - Workers gather and bank loads into their faction's stock. With workers selected, the keys Q W E R T (or the command card) pick a building type; the next click places the site if the ground is free and the stock covers it. A site has a tenth of its health and gains the rest as it goes up. - Training: with a finished building selected, the same keys queue its units, up to `queueSize`. A unit is paid when queued and refunded in full on cancel (Backspace, or a click on the queue). A unit needs the buildings of its `requires` and room in the supply: living and queued units against what finished buildings provide, at most `supplyLimit`. It walks out toward the rally point; a worker rallied on a node starts gathering. - End: a faction with no building left is defeated; when one faction is left (or the player is out) the match is over and the end screen says Victory or Defeat (`victory`). ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `map` | string | `crossing` | The `rts_maps` document played (the first one when unknown). | | `factions` | list (up to 8) | north (a person) and south (`normal` AI) | `id`, `name`, `color`, `start` (index of the map start), `ai` (`none` or an `rts.ai` plan id). | | `player` | string | `north` | The faction the local player commands. | | `resources` | list (up to 6) | ore, wood | The resources shown in the bar: `item`, `name`, `color`. | | `startStock` | object | `{ ore: 200, wood: 100 }` | Stock each faction starts with. | | `startBuildings` | list | one `hall` at offset `[0, 0]` | Finished buildings at each start. | | `startUnits` | list | 5 `worker` | Units each faction starts with. | | `supplyLimit` | integer | `100` | Highest supply any number of buildings provides. | | `queueSize` | integer | `5` | Units waiting in one building, at most. | | `victory` | boolean | `true` | End the game when one faction is left or the player lost. | | `moneyItem` | string | empty | With `economy.money`: the local player's stock of this item is the money balance. | Inputs: the actions rts.slot_1 to rts.slot_5 (keys Q W E R T) and rts.cancel_train (Backspace). ## Data: units, buildings, maps - `rts_units`: `id`, `name`, `health`, `speed` (required); `radius`, `sight`, `cost` (e.g. `{ "ore": 60 }`), `seconds` (training time), `supply`, `attack` (`damage`, `range`, `cooldown`, `windup`, `projectileSpeed`), `worker` (`carry`, `seconds`), `passive`, `requires` (building types, finished), `scale`, `slot`. - `rts_buildings`: `id`, `name`, `health`, `size` (required); `height`, `cost`, `seconds`, `supply` (provided once finished), `trains` (unit types, command card order), `dropoff` (true, or a list of items), `requires`, `slot`. - `rts_maps`: `id`, `name`, `size` (edge of the square field, centred on the origin), `starts` (required); `resources` (`{ node, at }`, a `resource_nodes` document) and `obstacles` (`{ at, size, yaw?, height? }`). Models come from the asset slots `rts_building` and `rts_obstacle` (variant = id); units use `rts_unit`. ## Services, events and variables Service `rts.production`: `factions()`, `player()`, `stock(faction)`, `supply(faction)`, `resources()`, `unitTypes()`, `buildingTypes()`, `map()`, `buildings(faction?)`, `check(faction, kind, type)` (`{ ok, reason, missing }`), `train(building, unit)`, `cancel(building, index?)`, `place(faction, type, at, builders?, { queue })`, `startPlacement(type)`, `setRally(building, point, target?)`, `outcome()` (`{ over, winner, defeated }`), `clock()`. Service `economy.stockpile`: `deposit(faction, item, count)`, `balance(faction, item)`, `spend(faction, cost)`, `add(faction, cost)`. | Event | Detail | |---|---| | `rts.started` | `{ map, factions, player }`. | | `rts.placed` / `rts.built` / `rts.destroyed` | `{ building, faction, type }`: a site placed and paid, a building finished, a building lost. | | `rts.queued` / `rts.cancelled` / `rts.trained` | `{ building, faction, unit }` (`rts.trained` also carries `type`); cancel refunds in full. | | `rts.refused` | `{ faction, type, reason }`: the player's placement was refused. | | `rts.defeated` | `{ faction, player }`: a faction lost its last building. | | `rts.victory` | `{ faction, player }`: the match is over. | | `rts.supply` | Variable (number): supply used. | | `rts.supply.max` | Variable (number): supply available. | | `rts.winner` | Variable (string): the winning faction. | Reasons of `check`, `place` and `train`: `unknown`, `over`, `requires` (with `missing`), `cost` (with `missing`), `supply`, `blocked`, `queue`, `unfinished`, `refused`. Web views: `resources` and `card` (the HUD bar and the command card). ## Hooks and modding | Hook | Payload | Mutable | Cancelled | |---|---|---|---| | `production.queue` | `{ building, faction, unit, seconds, costScale, queue }` | `seconds`, `costScale` | Nothing paid or queued; `train` answers `refused`. | | `production.complete` | `{ building, faction, unit }` | `unit` | The entry is gone and no unit comes out (no refund). | | `production.place` | `{ faction, type, x, z, builders, costScale }` | `costScale` | Nothing paid or placed; `place` answers `refused`. | | `production.deposit` | `{ faction, item, count }` | `count` | Nothing is banked. | ```json { "id": "rich_south", "hook": "production.deposit", "match": { "faction": "south" }, "mul": { "count": 2 } } ``` A mod may override `map`, `startStock`, `startBuildings`, `startUnits`, `supplyLimit` and `queueSize`, and add, patch or remove documents of `rts_units`, `rts_buildings` and `rts_maps`. ## When it does not do what you expect - A unit will not queue, reason `requires`: the faction does not own the finished buildings of its `requires`. Reason `supply`: build more supply or raise `supplyLimit`. - `place` answers `blocked`: the ground is not free, or `units.command` is not installed. - The computer faction does nothing: its `ai` is `none`, or `rts.ai` is not installed. - The match does not end: `victory` is off. ## Limits - Fixed rules for every faction: all play the same unit, building and map documents. - The kit draws nothing in the scene: models come from assets, the interface from `ui.webui`. ## Versions Kit 1.1.0; services `rts.production` and `economy.stockpile` 1.0.0. Hooks and moddable data since 1.1.0 (`game.engine` 1.2).