world

The places. Terrain, palettes, biomes, areas, points of interest, gates, rooms, encounters, dungeons, maps, and the clock.

Biome, then area, then point of interest. A biome is a theme: room templates, encounter tables, ambience, palette. An area belongs to one biome and joins others by declared connections. A point of interest is a place inside an area. A generated dungeon hangs off a point of interest and does not replace it.

How a map gets built
Three sources, highest first. map.static names a hand drawn grid. map.layout plus map.legend draws one inline with glyphs. Otherwise the size, palette, and starting number generate one. Maps are built on arrival and kept.
roomCount is a request
Spacing between rooms comes from the average of corridorLength, not from roomCount. Fifteen rooms with long corridors on a small floor can come out as two. The studio measures what a dungeon really generates and offers a size that matches.
Two guarantees
Every room is reachable. Every key lies before its lock. Both hold for any settings.

Three algorithms carve a floor:

"algorithm": "rooms"     places rectangles and joins them with corridors
"algorithm": "bsp"       splits the bounds recursively; the splits are the tree
"algorithm": "caverns"   noise and smoothing; keeps the largest connected floor

worldworld

FieldTypeReqDefaultWhat it does
terrainsobject[][]What a tile can be. Passability and cost are read from here, not baked in.
palettesobject[][]Which terrains a generator builds with. One generator, many looks.
biomesobject[][]A theme. Room templates, encounter tables, ambience, and a palette.
areasobject[][]A named place on the map, belonging to a biome and joined to others.
pointsOfInterestobject[][]Somewhere you go inside an area. A shrine, a camp, a dungeon mouth.
gatesobject[][]Something in the way, and what gets you past it.
roomTemplatesobject[][]The kinds of room a generated dungeon draws from.
encounterTablesobject[][]Weighted groups of monsters, drawn on arrival and in rooms.
dungeonsobject[][]A generated interior. Hangs off a point of interest rather than replacing it.
mapsobject[][]Hand authored grids, identical on every seed. No randomness is consulted.
timeobject{}The clock. Day length, phases, and what each action costs in minutes.
generationDefaultsobject{}Fallback chances for rooms that name none.

terrainsworld.terrains

FieldTypeReqDefaultWhat it does
ididyesId of the terrain, named by palettes, legends, and map cells.
namestringyesDisplay name. Used when something blocks you and has to say why.
descriptionstring""What it looks like underfoot.
tagsid[][]Free labels for grouping and gating.
glyphstring"."One or two characters drawn for it on the map.
colorredgreenyellowbluemagentacyanwhitegrayColour it is drawn in.
passablebooleantrueWhether anything can walk onto it.
opaquebooleanfalseWhether it blocks sight and any sense stopped by opaque things.
moveCostnumber1Movement spent entering it.
requiresModerules.movementModes[][]Movement modes that can cross it. Empty means any.
providesCoverrules.coverTypesCover a creature standing here gets.
onEntereffect[][]Effects run on whoever steps onto it.
onOccupyeffect[][]Effects run on whoever ends their turn on it.
lightRadiusnumber0Indexed but unread. There is no light model; a sense carries its own reach.
marks{ rules.senses: number }How well this ground keeps a trace, per sense. Zero keeps none.
isDoorbooleanfalseIndexed but unread. Doors live on the map's gate record, not on the terrain.
extra{ string: any }{}Open bag of your own data on this terrain.

palettesworld.palettes

FieldTypeReqDefaultWhat it does
ididyesId of the palette.
namestringDisplay name.
floorworld.terrainsyesTerrain used for open ground.
wallworld.terrainsyesTerrain used for solid rock.
doorworld.terrainsTerrain used where a corridor meets a room.
scatterobject[][]Terrains sprinkled over the floor, and how they are spread.
exteriorworld.terrainsTerrain filling everything outside the built area.
extra{ string: any }{}Open bag of your own data on this palette.

palettes → scatterworld.palettes.scatter

FieldTypeReqDefaultWhat it does
terrainworld.terrainsyesWhich terrain is scattered.
frequencynumber0.05Share of floor it covers, 0 to 1.
distributionspecklepatch"speckle"Speckle rolls each tile alone. Patch uses noise, so it comes out connected.
scalenumber8Size of the noise features, for patch.
octavesnumber2Layers of noise, for patch. More gives rougher edges.
edgeTerrainworld.terrainsTerrain ringing each patch.
edgeWidthnumber1How many tiles wide that ring is.
prioritynumber0Which scatter wins where two overlap. Higher wins.

biomesworld.biomes

FieldTypeReqDefaultWhat it does
ididyesId of the biome.
namestringyesDisplay name.
descriptionstring""What this kind of place is like.
tagsid[][]Free labels for grouping and gating.
layeroverworldunderworldboth"underworld"Whether places here sit above ground, below it, or either.
roomTemplatesworld.roomTemplates[][]Rooms a dungeon in this biome may draw.
encounterTablesworld.encounterTables[][]What may be met here.
lootTablescontent.lootTables[][]What may be found here.
trapscontent.traps[][]Traps that may be placed here.
ambienceKeynarrative.textGrammarText pool for the background of the place.
triggersobject[][]Things that happen anywhere in this biome.
paletteworld.palettesTerrains generators use here. Overrides a palette named on the map.
extra{ string: any }{}Open bag of your own data on this biome.

biomes → triggersworld.biomes.triggers

FieldTypeReqDefaultWhat it does
ididyesId of the trigger. Also the key under which firing is remembered.
descriptionstring""What happens and why.
modeonceeveryEntryuntilCompletelooprestart"once"How often it may fire. Choosing this correctly is most of the work.
onenterexitrestsearchcombatStartcombatEndtimePasscustom"enter"The occasion it listens for.
eventstringEvent name to watch, when on is custom.
requiresobjectGate on firing, in the requirement vocabulary.
whenpredicatePredicate checked on top of requires.
cooldownMinutesnumber0World minutes between firings, for loop mode.
completeWhenpredicatePredicate that ends untilComplete mode once it holds.
rememberbooleantrueWhether the world records that it happened. Off makes the event ambient.
chancenumber1Chance it fires once the gates pass. Rolled last, so a failed gate costs no randomness.
effectseffect[][]What it does.
textKeynarrative.textGrammarText pool narrating it.

areasworld.areas

FieldTypeReqDefaultWhat it does
ididyesId of the area. Its map is keyed area:<id>.
namestringyesDisplay name.
descriptionstring""What the place is.
tagsid[][]Free labels for grouping and gating.
biomeworld.biomesyesTheme it belongs to. Supplies rooms, encounters, ambience, and palette.
layeroverworldunderworld"overworld"Whether it sits above ground or below. Decides whether the sky is visible.
descriptionKeynarrative.textGrammarText pool read on arrival.
connectionsobject[][]Roads out. Travel is only possible along a declared connection.
triggersobject[][]Things that happen here.
encounterTablesworld.encounterTables[][]What may be met here, on top of the biome's.
controllingFactioncontent.factionsWho holds it.
dangerLevelnumber1How dangerous it is. Feeds the default encounter chance.
encounterChanceexpression{"min":[0.75,{"mul":[{"ref":"dangerLevel"…Formula for the chance of an encounter, overriding the danger based default.
recommendedLevelnumberParty level the area is written for. Advisory.
requiresobjectGate on entering at all.
mapobject{}How the ground here is built. Size, palette, or a hand authored grid.
entryPointobjectTile the party arrives on. Without one the engine picks a free tile.
extra{ string: any }{}Open bag of your own data on this area.

areas → connectionsworld.areas.connections

FieldTypeReqDefaultWhat it does
toworld.areasyesArea at the other end.
travelMinutesnumber60World minutes the journey costs.
gateworld.gatesSomething in the way on this road.
oneWaybooleanfalseSet on the destination's back link. A road listed both ways is two way.
encounterTablesworld.encounterTables[][]What may be met on the road itself.

areas → mapworld.areas.map

FieldTypeReqDefaultWhat it does
widthdice"7"Tiles across, as dice notation so it can vary by seed.
heightdice"7"Tiles down, as dice notation.
paletteworld.palettesTerrains to build with. Dead whenever the biome names one, since the biome wins.
staticworld.mapsA hand authored map to use instead. Beats layout and generation.
layoutstring[][]Rows of glyphs drawn by hand. Beats generation. All rows must be equal length.
legend{ string: world.terrains }{}Which terrain each layout glyph means.
extra{ string: any }{}Open bag of your own data on this map spec.

areas → entryPointworld.areas.entryPoint

FieldTypeReqDefaultWhat it does
xnumberyesColumn, counting from zero.
ynumberyesRow, counting from zero.

pointsOfInterestworld.pointsOfInterest

FieldTypeReqDefaultWhat it does
ididyesId of the place. Its map is keyed poi:<id>.
namestringyesDisplay name.
descriptionstring""What is here.
tagsid[][]Free labels for grouping and gating.
areaworld.areasyesArea it sits in. A place whose area does not exist never appears.
kindsettlementshrineruincampdungeonEntrancelandmarkcrossinglairmarketwilds"landmark"What sort of place it is. Decides the icon and some phrasing.
descriptionKeynarrative.textGrammarText pool read on arrival.
gateworld.gatesSomething in the way of going in.
hiddenbooleanfalseWhether it must be found before it can be travelled to.
discoverobjectThe check that finds it. Without one a hidden place stays hidden.
triggersobject[][]Things that happen here.
encounterTablesworld.encounterTables[][]What may be met here.
encounterChancenumberChance of an encounter here, overriding the area.
residentscontent.npcs[][]Who lives here. The other way to place people, alongside an NPC's home.
lootcontent.lootTables[][]Tables drawn when the place is searched.
dungeonworld.dungeonsInterior reached from here. The dungeon hangs off the place, it does not replace it.
servicesinnmarkettemplesmithguildstablehealer[][]What can be done here. Trade, rest, healing, training.
rumourReachnumber1How many hops news of this place travels among its people.
controllingFactioncontent.factionsWho holds it.
travelMinutesnumber0World minutes to reach it from the area entrance.
positionobjectWhere it sits on the area map.
mapobjectHow the ground here is built, when the place has an interior of its own.
extra{ string: any }{}Open bag of your own data on this place.

pointsOfInterest → discoverworld.pointsOfInterest.discover

FieldTypeReqDefaultWhat it does
skillcontent.skillsyesSkill rolled to find it.
difficultyexpressionyesDifficulty of that roll. Often a formula over what the party has heard.

gatesworld.gates

FieldTypeReqDefaultWhat it does
ididyesId of the gate. Its state is flagged gate:<id>:open.
namestringyesDisplay name.
descriptionstring""What is in the way.
tagsid[][]Free labels for grouping and gating.
kindlockwardpuzzletollstoryhazard"lock"What sort of obstacle it is.
requiresobjectWhat opens it outright. Items marked consume are spent doing so.
bypassobjectA check that gets past it instead.
opensWithcontent.abilities[][]Abilities that open it.
onOpeneffect[][]Effects run when it opens.
onBlockedeffect[][]Effects run when someone fails to open it.
blockedTextKeynarrative.textGrammarText pool for being turned away.
staysOpenbooleantrueWhether opening it is permanent.

gates → bypassworld.gates.bypass

FieldTypeReqDefaultWhat it does
skillcontent.skillsyesSkill rolled to get past.
difficultyexpressionyesDifficulty of that roll.
onSuccesseffect[][]Effects run on getting past.
onFailureeffect[][]Effects run on failing.
retryablebooleantrueWhether a failure may be tried again. False records the attempt and refuses more.

roomTemplatesworld.roomTemplates

FieldTypeReqDefaultWhat it does
ididyesId of the template.
namestringyesDisplay name.
tagsid[][]Free labels for grouping and gating.
descriptionKeynarrative.textGrammaryesText pool read on first entering a room built from it.
weightnumber1Relative likelihood of being drawn against other templates.
roleentrancecorridorchambervaultbossshrinelair"chamber"What the room is for. Generation guarantees some roles exist.
minExitsnumber1Fewest doors the room may have.
maxExitsnumber4Most doors it may have. Connectivity wins over this cap.
encounterChancenumber0.3Chance the room is occupied, 0 to 1.
trapChancenumber0.1Chance the room is trapped, 0 to 1.
lootChancenumber0.25Chance the room holds something, 0 to 1.
alwaysEncounterbooleanfalseForce an occupant regardless of the chance.
neverEncounterbooleanfalseForbid an occupant regardless of the chance.
neverTrapbooleanfalseForbid a trap regardless of the chance.
requiresobjectGate on the template being drawn at all.
triggersobject[][]Things that happen in a room built from it.
mapobject{}How the room itself is built.
extra{ string: any }{}Open bag of your own data on this template.

encounterTablesworld.encounterTables

FieldTypeReqDefaultWhat it does
ididyesId of the table.
namestringDisplay name.
descriptionstring""What this table is for.
minDepthnumber0Shallowest dungeon depth it may be drawn at.
maxDepthnumber999Deepest dungeon depth it may be drawn at.
chancenumber1Chance a draw yields anything at all, 0 to 1.
emptyWeightnumber0Weight of the nothing row against the groups.
scalePerLevelsnumber2Party levels per step of scaling on entries that scale.
groupsobject[]yesThe weighted rows. Each is a whole encounter.

encounterTables → groupsworld.encounterTables.groups

FieldTypeReqDefaultWhat it does
ididyesId of the group.
namestringDisplay name.
weightnumber1Relative likelihood against the other groups.
requiresobjectGate on the group. Failing removes it before the draw, not after.
entriesobject[]yesThe creatures in it. At least one is required.
onEncountereffect[][]Effects run when this group is drawn.
textKeynarrative.textGrammarText pool narrating the meeting.
hostilebooleantrueWhether they attack. False makes it a meeting rather than a fight.

encounterTables → groups → entriesworld.encounterTables.groups.entries

FieldTypeReqDefaultWhat it does
monstercontent.monstersyesWhich statblock appears.
countdice"1"How many, as dice notation.
scaleWithLevelbooleanfalseWhether the count grows with party level.

dungeonsworld.dungeons

FieldTypeReqDefaultWhat it does
ididyesId of the dungeon. Its maps are keyed by it and the depth.
namestringyesDisplay name.
descriptionstring""What is down there.
biomeworld.biomesyesTheme it is built from. Supplies room templates and encounters.
roomCountdice"8"Rooms requested per floor. A request, not a promise; packing may fit fewer.
depthdice"1"How many floors deep it goes.
branchinessnumber0.35Extra loops added past the spanning tree, 0 to 1. 0 gives a pure tree.
lockedDoorChancenumber0.15Chance a tree edge gets a lock. Keys are always placed before their lock.
doorGatesworld.gates[][]Gates that may be used as locks.
guaranteedRolesstring[]["entrance","boss"]Room roles that must exist. Bare strings, not checked references.
safeEntrancebooleantrueWhether the entrance room is guaranteed empty.
bossTableworld.encounterTablesDrawn in the boss room. Without one the deepest room generates like any other.
completionTriggersobject[][]Things that happen when the dungeon is finished.
paletteworld.palettesTerrains to build with, overriding the biome.
corridorLengthdice"3d3"Corridor length, which is what actually sets the spacing between rooms.
windingobject{}How much corridors wander, when the corridor style is winding.
roomSizedice"2d3+3"How large a room is on a side.
algorithmroomsbspcaverns"rooms"How the floor is carved. Placed rooms, split rectangles, or grown caves.
bspobject{}Settings for the bsp algorithm.
cavernsobject{}Settings for the caverns algorithm.
staticMapworld.mapsA hand authored map to use instead. Skips generation entirely.
rollEncountersbooleanfalseWhether rooms roll their own encounters as well as the placed ones.
corridorobject{}Shape and width of the corridors.
widthdiceFloor width in tiles. Without one the engine sizes it from the room count.
heightdiceFloor height in tiles.
extra{ string: any }{}Open bag of your own data on this dungeon.

dungeons → windingworld.dungeons.winding

FieldTypeReqDefaultWhat it does
continueChancenumber0.6Chance a corridor keeps going the way it was, 0 to 1.
turnPenaltynumber0.4How much a turn is discouraged, 0 to 1.

dungeons → bspworld.dungeons.bsp

FieldTypeReqDefaultWhat it does
minLeafnumber5Smallest rectangle the splitting stops at.

dungeons → cavernsworld.dungeons.caverns

FieldTypeReqDefaultWhat it does
fillnumber0.45Share of the floor filled with rock before smoothing, 0 to 1.
smoothingPassesnumber4How many rounds of smoothing are run.
birthThresholdnumber5Neighbours needed for a cell to become rock.

dungeons → corridorworld.dungeons.corridor

FieldTypeReqDefaultWhat it does
stylelstraightwinding"l"Whether corridors turn once, run straight, or wander.
widthnumber1Tiles across. Crossings are never widened, so a locked door stays one tile.

mapsworld.maps

FieldTypeReqDefaultWhat it does
ididyesId of the map, named by an area, place, dungeon, or room template.
namestringDisplay name.
descriptionstring""What this map is.
tagsid[][]Free labels for grouping and gating.
entryid"entry"Marker the party arrives at.
layersobject, 7 variants[]yesGrids stacked in draw order. The layer kind decides what its cells mean.
extra{ string: any }{}Open bag of your own data on this map.

maps → layersworld.maps.layers

FieldTypeReqDefaultWhat it does
kindterrainyesWhat the cells name. Terrain, items, monsters, npcs, gates, traps, or free markers.
nameidNames the layer in the editor and its file on disk.
cells | world.terrains[][]yesRows of ids, one per tile. An empty cell means nothing here.
extra{ string: any }{}Open bag of your own data on this layer.

timeworld.time

FieldTypeReqDefaultWhat it does
minutesPerDaynumber1440How long a day is.
minutesPerHournumber60How long an hour is.
daysPerMonthnumber30How long a month is.
monthNamesstring[][]Names of the months, in order.
dayPhasesobject[][]Named parts of the day and when each begins.
startMinutenumber480Minute of the first day play begins at.
actionMinutes{ string: number }{"search":10,"disarm":10,"sense":1,"wait"…World minutes each action costs outside combat.
minutesPerTilenumber0World minutes a step costs. Only charged outside combat.

time → dayPhasesworld.time.dayPhases

FieldTypeReqDefaultWhat it does
ididyesId of the phase, read as world.phase.
namestringyesDisplay name.
startMinutenumberyesMinute of the day it begins at.

generationDefaultsworld.generationDefaults

FieldTypeReqDefaultWhat it does
encounterChancenumber0.3Encounter chance for a room whose template names none.
lootChancenumber0.25Loot chance for a room whose template names none.
trapChancenumber0.1Trap chance for a room whose template names none.