Run map

A seeded roguelite run: maps of fights, elites, rests, shops and events, relics as data, gold, rewards and the end of the run, built on combat.turns and cards.deck.

Source: module-kits:kits/run_map/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs run-mapMCP tool: gripforge_docs { "topic": "run-map" }

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 #

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 #

<!-- test: kits/game_engine/host/tests/run_map.rs::a_room_is_fought_and_its_reward_taken --> With a starter relic that pays 5 gold on pickup and a starting purse of 50:

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 #

SettingTypeDefaultMeaning
autoStartbooleantruea run starts when the game starts
seedinteger0seed of the first run (0: drawn from the game seed)
actslist of ids[]run_acts ids in order; empty: every act document
startGoldinteger99gold when a run starts
starterRelicslist of ids[]up to 4 relics held at the start
restHealnumber 0 to 10.3share of max health a rest heals
goldFight, goldElite, goldBoss[min, max][10, 20], [25, 35], [95, 105]gold after each kind of fight
pricesobjectcards 50 / 75 / 150common, uncommon, rare for cards; relic_common, relic_uncommon, relic_rare, relic_shop for relics (150 / 250 / 300); a relic's own price wins
removePriceinteger75price of removing a card at a shop
removeStepinteger25added to the removal price for each card removed
shopCardsinteger 0 to 105cards offered by a shop
shopRelicsinteger 0 to 62relics 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.

HookPayloadWhat a tap can do
run.travelnode, type, row, floor, actcancel: travel answers false
run.rewardkind, gold, floor, act, encounterchange gold, or cancel: no reward room
run.chooseevent, option, label, gold, hpcancel: chooseOption answers false
run.finishresult, floor, act, goldchange result; the run ends either way
{ "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.