YAML Schemas
A schema describes the fields a file can contain and which ones are required. An entity is one structured piece of game content, such as a location, character, or quest. Define each entity in a YAML file in the matching content/ subdirectory.
Every entity has an id. Ids must be unique within their type: two location
files cannot both be id: town, and validation reports the clash with both
file names. Different types may reuse an id (a location and an item can both
be called key).
Location
Section titled “Location”Directory: content/locations/
id: tavernname: '@location.tavern.name'description: '@location.tavern.description'banner: tavern.pngmusic: tavern_ambience.oggambient: fire_crackling.ogg| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
name |
string |
Display name (supports @key) |
description |
string |
Text shown at this location |
banner |
string |
Banner image filename |
music |
string |
Background music filename |
ambient |
string |
Ambient sound filename |
Character
Section titled “Character”Directory: content/characters/
id: bartendername: '@character.bartender.name'title: '@character.bartender.title'biography: '@character.bartender.bio'portrait: bartender.pnglocation: taverndialogue: bartender_greetingstats: strength: name: '@stat.strength' value: 16.2 class: name: '@stat.class' value: '@class.ranger'| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
name |
string |
Display name (supports @key) |
title |
string |
Optional title (supports @key) |
biography |
string |
Character background text |
portrait |
string |
Portrait image filename |
location |
string |
Starting location ID |
dialogue |
string |
Dialogue ID for conversations |
stats |
Record<string, CharacterStat> |
Numeric or string character traits |
Each stat has a stable key, a player-facing name, and a numeric or string
value. Both the name and a string value support @key localization. Decimal
numbers are valid. Prefix a stat key with _ to keep it off the built-in
character sheet while retaining it for conditions, effects, and text
interpolation.
The shorter strength: 16 form is also accepted. Its key becomes the display
name.
PlayerCharacter
Section titled “PlayerCharacter”File: content/player.yaml
The player is defined separately from NPCs. It does not have a location, dialogue, relationship, or party-membership field.
For a profile supplied by the game:
name: '@player.name'title: '@player.title'biography: '@player.biography'portrait: hero.pngstats: strength: name: '@stat.strength' value: 16The same file can provide the replaceable profile text used by the project template:
name: '@player.name'title: ''biography: ''portrait: ''stats: class: name: '@stat.class' value: '@class.ranger'| Field | Type | Description |
|---|---|---|
name |
string |
Optional fixed name (supports @key) |
title |
string |
Optional fixed title (supports @key) |
biography |
string |
Optional fixed biography (supports @key) |
portrait |
string |
Optional fixed portrait filename |
stats |
Record<string, CharacterStat> |
Stats defined by the game |
The profile mode is selected by playerCreatesProfile in content/game.yaml.
When it is true, the built-in renderer asks for a name, title, and biography;
only the name is required. Those entered values replace the corresponding
profile text. The modal does not provide portrait uploads. When the flag is
false, the profile fields from player.yaml are used directly. Stats always
come from player.yaml.
Directory: content/items/
id: old_coinname: '@item.old_coin.name'description: '@item.old_coin.description'icon: old_coin_icon.pngimage: old_coin.pnglocation: inventorystats: {}| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
name |
string |
Display name (supports @key) |
description |
string |
Full description |
icon |
string |
Small icon filename |
image |
string |
Large image filename |
location |
string |
Starting location: location ID, "inventory", or character ID |
stats |
Record<string, unknown> |
Stats for game-specific data |
Directory: content/maps/
id: townname: '@map.town.name'image: town_map.pngscale: 1locations: - id: tavern x: 200 y: 350 - id: market x: 500 y: 200| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
name |
string |
Display name (supports @key) |
image |
string |
Map background image filename |
scale |
number |
Pixels per hour of travel. Must be greater than zero. Higher values mean faster travel. |
locations |
MapLocation[] |
Location markers |
MapLocation
Section titled “MapLocation”| Field | Type | Description |
|---|---|---|
id |
string |
Location ID (must match a location entity) |
x |
number |
X coordinate on the map image |
y |
number |
Y coordinate on the map image |
A game can contain multiple maps. At runtime, the engine shows the map that contains the player’s current location. Validation rejects maps that reference missing locations or put the same location on multiple maps.
Directory: content/quests/
id: odd_jobsname: '@quest.odd_jobs.name'description: '@quest.odd_jobs.description'stages: - id: started description: '@quest.odd_jobs.stage.started' - id: talked_to_merchant description: '@quest.odd_jobs.stage.talked_to_merchant' - id: complete completesQuest: true description: '@quest.odd_jobs.stage.complete'| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
name |
string |
Display name (supports @key) |
description |
string |
Quest description |
stages |
QuestStage[] |
Ordered list of quest stages |
QuestStage
Section titled “QuestStage”| Field | Type | Description |
|---|---|---|
id |
string |
Stage identifier (used with SET questStage) |
description |
string |
Text shown in journal for this stage |
completesQuest |
boolean |
Optional; marks this as a finishing stage |
JournalEntry
Section titled “JournalEntry”Directory: content/journal/
id: tavern_discoverytitle: '@journal.tavern_discovery.title'text: '@journal.tavern_discovery.text'category: places| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
title |
string |
Display title (supports @key) |
text |
string |
Entry content |
category |
string |
Category for grouping (e.g., "lore", "people", "places", "quest") |
Interlude
Section titled “Interlude”Directory: content/interludes/
id: chapter_onebackground: dusk_road.jpgtext: | Chapter One: A New Beginning
The road behind you stretches long and empty. Ahead, the lights of town flicker through the evening mist.| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier |
background |
string |
No | Optional background image filename |
text |
string |
Yes | Narrative text (supports @key) |
banner |
string |
No | Optional decorative frame/border image |
music |
string |
No | Music track to play |
voice |
string |
No | Narration audio file |
sounds |
string[] |
No | Ambient sounds to play |
scroll |
boolean |
No | Auto-scroll text (default: true) |
scrollSpeed |
number |
No | Auto-scroll speed in px/s (default: 30) |
triggerLocation |
string |
No | Location ID where this auto-triggers on enter |
triggerConditions |
Condition[] |
No | Conditions that must pass for auto-trigger |
effects |
Effect[] |
No | Effects applied when the interlude triggers (e.g. set a “seen” flag) |
Triggered via the INTERLUDE <id> DSL effect, or automatically when traveling to triggerLocation if all triggerConditions pass. See the Interludes guide.
GameConfig
Section titled “GameConfig”File: content/game.yaml
title: My Gamesubtitle: A text-based adventureauthor: North Road StudioplayerCreatesProfile: truestartLocation: tavernstartTime: day: 1 hour: 8startFlags: {}startVariables: gold: 100 reputation: 0 _drinksBought: 0startInventory: []| Field | Type | Description |
|---|---|---|
title |
string |
Player-facing game title |
subtitle? |
string |
Optional line beneath the title |
author? |
string |
Author or studio shown above the title when no title logo is set |
playerCreatesProfile |
boolean |
Ask the player for profile text in the built-in renderer |
shell? |
ShellConfig |
Shell screen configuration |
startLocation |
string |
Starting location ID |
startTime |
{ day: number, hour: number } |
Starting time |
startFlags |
Record<string, boolean> |
Initial flags |
startVariables |
Record<string, number | string> |
Initial variables |
startInventory |
string[] |
Item IDs the player starts with |
playerCreatesProfile defaults to false. It controls how the player profile
is initialized. The profile content and starting stats remain in
content/player.yaml.
Locale
Section titled “Locale”Directory: content/locales/
Locale files contain localization keys and their translated text. The filename sets the locale code.
location.tavern.name: 'The Rusty Tankard'location.tavern.description: 'A cozy tavern with worn wooden tables.'character.bartender.name: 'Greta'bartender.greeting: 'Welcome! What can I do for you?'| Key | Value |
|---|---|
| Any string | Translated text |
Referenced with @key syntax in other content files. See Localization.