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.
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 #
| 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,pickorended),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()healsrestHealof 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 withui.endscreen).
Verbs, variables, events #
- Verbs:
start_run { seed },gain_gold(an amount, negative takes gold),grant_relic, and the conditionhas_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(resultwonorlost, floor, act, gold). - Views:
bar(HUD),mapandroomscreens.
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),eliteFromandrestFrom(default 3),restBeforeBoss(default true).run_encounters:id,kind(fight,elite,boss),enemies(1 to 6turn_enemiesids), optionalact,rows,gold.relics:id,name,description,rarity(starter,common,uncommon,rare,boss,shop),price,color,icon, and up to 6triggers{ on, every?, turn?, cardType?, effects }whereonispickup,combat_start,turn_start,turn_end,card_played,enemy_killed,combat_wonorrest.run_events:id,name,text, optionalact, and 1 to 4options{ label, text?, requires?, cost?, effects }, withrequiresandcostingoldandhp. Effects includegain_gold,lose_gold,heal,lose_hp,gain_max_hp,add_card,remove_card,upgrade_card,upgrade_random,gain_relicandfight.
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 |
{ "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 #
travelanswersfalse: the id is not amongchoices(), the run is not on the map, or arun.traveltap cancelled it.- No run exists:
autoStartisfalseand nobody callednewRunorstart_run. - A fight has no enemies: the
run_encountersentry names aturn_enemiesid that does not exist. - A shop sells nothing you expect: check
shopCards,shopRelicsand therelicsrarities. - 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 iscards.deck). - One run at a time. It targets the web player and the Rust kit host; the screens are the
bar,mapandroomviews.
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.