# Roguelite runs on a branching map (run.map) ## What it is for `run.map` drives a deckbuilder-style run. Each act is a seeded grid of rooms joined by paths that never cross, and every top room leads to the boss. The player walks from room to room: fights and elites (played by `combat.turns`), rest sites, shops, events with choices, and rewards of gold, cards (from `cards.deck`) and relics. Relics are data: a list of triggers with effects that act on the fight. The whole run is plain JSON that the save keeps as it is. The same seed gives the same maps, fights, rewards and shops. The kit comes with a HUD bar, a map screen and a room screen; the end of the run sets the finished phase. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "run.map" } gripforge_gamekit_configure { project_id: "gkp_…", id: "run.map", config: { "seed": 9, "startGold": 120, "starterRelics": ["lantern"], "restHeal": 0.3 } } ``` It requires `combat.turns`, `cards.deck` and `game.engine`. With `autoStart` (the default) a run starts when the game starts; with `autoStart` false, start one with the verb `start_run` or the service method `newRun`. Your data provides the `relics`, the `run_encounters` and the `run_events` documents (the fights use `turn_enemies` ids). With no `run_acts` document, one built-in act is used. ## First example With a starter relic that pays 5 gold on pickup and a starting purse of 50: ```text run.map.gold() // 55: the starter relic paid on pickup run.map.choices() // the rooms of the first row the player can walk to run.map.travel(first.id) // true; mode() is now "combat" // the fight is won: mode() is "reward", state().reward holds the gold and the card offer run.map.claimGold() // true (a second call answers false) run.map.takeCard(0) // true: the card joins the deck run.map.proceed() // back to the map, the next row is reachable ``` If the hero dies, the run is lost and the game enters the `finished` phase. ## Settings | Setting | Type | Default | Meaning | |---|---|---|---| | `autoStart` | boolean | `true` | a run starts when the game starts | | `seed` | integer | `0` | seed of the first run (0: drawn from the game seed) | | `acts` | list of ids | `[]` | `run_acts` ids in order; empty: every act document | | `startGold` | integer | `99` | gold when a run starts | | `starterRelics` | list of ids | `[]` | up to 4 relics held at the start | | `restHeal` | number 0 to 1 | `0.3` | share of max health a rest heals | | `goldFight`, `goldElite`, `goldBoss` | `[min, max]` | `[10, 20]`, `[25, 35]`, `[95, 105]` | gold after each kind of fight | | `prices` | object | cards 50 / 75 / 150 | `common`, `uncommon`, `rare` for cards; `relic_common`, `relic_uncommon`, `relic_rare`, `relic_shop` for relics (150 / 250 / 300); a relic's own `price` wins | | `removePrice` | integer | `75` | price of removing a card at a shop | | `removeStep` | integer | `25` | added to the removal price for each card removed | | `shopCards` | integer 0 to 10 | `5` | cards offered by a shop | | `shopRelics` | integer 0 to 6 | `2` | relics offered by a shop | ## Services `run.map` (service) methods; those that act return `true` or `false`: - Run: `newRun(seed?)`, `state()`, `mode()` (`map`, `combat`, `reward`, `rest`, `shop`, `event`, `pick` or `ended`), `map()`, `choices()` (reachable rooms), `travel(node)`. - Gold and relics: `gold()`, `addGold`, `relics()`, `grantRelic` (no id: a random relic not owned), `hasRelic`. - Reward room: `claimGold`, `takeCard(index)`, `skipCards`, `claimRelic`, `proceed()`. - Rest room: `rest()` heals `restHeal` of max health; `smith()` opens the choice of a card to upgrade. - Shop: `buyCard`, `buyRelic`, `removeService` (opens the choice of a card to remove). - Card choice: `pickCard(uid)`, `cancelPick`. - Event room: `event()` (the open event), `chooseOption(i)`. - Other: `runEffects(effects)` applies turn effects to the run; `endStats(lang)` returns the figures for the end screen (registered with `ui.endscreen`). ## Verbs, variables, events - Verbs: `start_run { seed }`, `gain_gold` (an amount, negative takes gold), `grant_relic`, and the condition `has_relic`. - Variables: `run.mode`, `run.gold`, `run.floor`, `run.act`, `run.relics`. - Events: `run.started` (seed, act), `run.travelled` (node, type, floor), `run.room` (mode, node, floor), `run.act` (act, name), `run.relic` (relic, on), `run.relic_gained` (relic), `run.event_choice` (event, option), `run.ended` (result `won` or `lost`, floor, act, gold). - Views: `bar` (HUD), `map` and `room` screens. ## Data collections - `run_acts`: `id`, `name`, `rows` (2 to 20, default 7), `columns` (2 to 9, default 5), `paths` (1 to 8, default 3), `weights` (`fight`, `elite`, `event`, `shop`, `rest`), `eliteFrom` and `restFrom` (default 3), `restBeforeBoss` (default true). - `run_encounters`: `id`, `kind` (`fight`, `elite`, `boss`), `enemies` (1 to 6 `turn_enemies` ids), optional `act`, `rows`, `gold`. - `relics`: `id`, `name`, `description`, `rarity` (`starter`, `common`, `uncommon`, `rare`, `boss`, `shop`), `price`, `color`, `icon`, and up to 6 `triggers` `{ on, every?, turn?, cardType?, effects }` where `on` is `pickup`, `combat_start`, `turn_start`, `turn_end`, `card_played`, `enemy_killed`, `combat_won` or `rest`. - `run_events`: `id`, `name`, `text`, optional `act`, and 1 to 4 `options` `{ label, text?, requires?, cost?, effects }`, with `requires` and `cost` in `gold` and `hp`. Effects include `gain_gold`, `lose_gold`, `heal`, `lose_hp`, `gain_max_hp`, `add_card`, `remove_card`, `upgrade_card`, `upgrade_random`, `gain_relic` and `fight`. ## Hooks and modding Each hook is asked just before the decision; none draws a random number. | Hook | Payload | What a tap can do | |---|---|---| | `run.travel` | `node`, `type`, `row`, `floor`, `act` | cancel: `travel` answers `false` | | `run.reward` | `kind`, `gold`, `floor`, `act`, `encounter` | change `gold`, or cancel: no reward room | | `run.choose` | `event`, `option`, `label`, `gold`, `hp` | cancel: `chooseOption` answers `false` | | `run.finish` | `result`, `floor`, `act`, `gold` | change `result`; the run ends either way | ```json { "id": "rich_fights", "hook": "run.reward", "mul": { "gold": 1.5 } } ``` A mod can change the settings (all but `autoStart` and `seed`) and add, patch or remove documents of the four collections. ## When it does not do what you expect - `travel` answers `false`: the id is not among `choices()`, the run is not on the map, or a `run.travel` tap cancelled it. - No run exists: `autoStart` is `false` and nobody called `newRun` or `start_run`. - A fight has no enemies: the `run_encounters` entry names a `turn_enemies` id that does not exist. - A shop sells nothing you expect: check `shopCards`, `shopRelics` and the `relics` rarities. - The same seed gives different maps after a data change: the map depends on the act documents too. ## Limits - It plays no fights itself (that is `combat.turns`) and holds no cards (that is `cards.deck`). - One run at a time. It targets the web player and the Rust kit host; the screens are the `bar`, `map` and `room` views. ## Versions Kit version 1.1.0 (1.0.0 first release: maps, rooms, relics, rewards). Version 1.1.0 added the four hooks and made the settings and collections moddable. It requires `combat.turns` 1.x, `cards.deck` 1.x and `game.engine` 1.x.