Adventure Format

JSON format

An adventure is described using the JSON format. JSON data block consists of nested Objects with name-value pairs and Arrays (lists of values without names) like the following.

Object:

{
  health: 10,
  name: "John"
}

Array:

["sword", "axe"]

The value in an Objects or a value in an Array can be true/false, number, "text", an Object, or an Arrays. Name should consist of letters, underscore, and numbers (no spaces).

Adventure Object

The main adventure object contains global game properties and references to all rooms, items, and game logic.

Properties

Example

const advData = {
    title: "My Adventure",
    theme: "terminal",
    startRoom: "entrance",
    status: "Health: {player_health}/100",
    gameState: {
        player_health: 100,
        door_unlocked: false
    },
    rooms: { /* room definitions */ },
    items: { /* item definitions */ }
};

---

Room Definition

Rooms are the main building blocks of the adventure, containing an overall description, interactive objects, and items. Rooms should be constructed with a clear separation between static description and dynamic interactive elements.

Room Construction Guidelines

1. Keep main room description short (1-2 sentences) that establish the overall mood and basic layout
2. Don't mention specific objects or items in the room description - these are automatically added from object/item roomDescription fields
3. Create objects for all important interactable elements that have at least an examine action
4. Use containers to organize items visually and logically

Properties

Example: Well-Structured Room

This room relies on examine, open, and go templates being available.

rooms: {
    entrance: {
        description: "You stand in a grand entrance hall illuminated by torchlight.",
        image: "entrance.png",
        items: ["rusty_key"],
        objects: {
            // Important interactive objects
            chest: {
                name: "chest",
                roomDescription: "A massive oak [[]] stands by the north wall.",
                description: "It is closed and looks very old.",
                actions: ["examine", "open"]
            },
            table: {
                name: "wooden table",
                roomDescription: "A sturdy [[]] stands in the center of the room.",
                description: "There are empty plates and mugs standing on the table.",
                actions: ["examine"]
            }
            container: { // 'container' is the default container for items in room that did not find a better match
                roomDescription: [ { condition: "roomItems.container", value: "\n\n"} ] // this conditional \n\n will make it so that items not in any specific container are in their own paragraph in the final room description
                },
            west: { // "exit" object should be last
                name: "west",
                roomDescription: "A door leads to the [[|kitchen]].",
                actions: [
                    {
                        template: "go",
                        goto: "kitchen",
                        transition: "left"
                    }
                ]
            }
        }
    }
}

Container System

Containers allow logical grouping and conditional visibility of items within rooms. Object key is considered the container name. In the above example of a well structured room, there are 4 objects, 3 of them are supposed to be containers. The 'container' object uses the special name 'container' which indicates that all items that were not placed in a more suitable container will be placed here.

Item Assignment to Containers

items: {
    gold_coin: {
        name: "gold coin",
        roomContainer: "chest",  // Item prefers to be in the "chest" container
        roomDescription: [
            {
                condition: "container.chest",
                value: "A shiny [[]] lies in the chest.",
            },
            {
                value: "A shiny [[]] glints in the light.", // fallback (not in chest)
            }
        ],
        actions: ["examine", "take"]
    },
    silver_key: {
        name: "silver key",
        roomContainer: ["chest", "table"],  // Item can appear in multiple containers - order of preference is respected
        roomDescription: "A delicate [[]] rests here.", // It is not necessary to have specific descriptions for each container, but if there are, they must be in the same order as the roomContainer list
        actions: ["examine", "take"]
    }
}

Container Behavior

Room Item Display

The final room description is assembled from:
1. Room description (static mood text)
2. Object roomDescription fields (in order)
3. Item roomDescription fields (grouped by container)

This creates dynamic, context-aware room descriptions without manual text management. If objects have conditional descriptions or visibility, the room description updates automatically when game state is updated - changes are highlighted.

Item Definition

Items are very similar to room objects, but they can be created, deleted, collected, or dropped. Items do not support any actions by themselves, just like room objects, but unlike room objects they are defined outside of rooms. Items do not have actions like take or drop natively, these actions need to be defined for each item. This allows for example creating an NPC (that is present in multiple rooms and has the same behavior) as an item without the (awkward) take/drop actions and have the NPC only defined in one place.

Properties

Example

item: {
    name: "iron key",
    description: "A rusty iron key.",
    roomDescription: "A [[]] is laying in the corner.",
    actions: ["examine", "take", "drop"]  // Use template-based examine, take, and drop actions
}

Exit Object (Movement via Objects)

There are no built-in go or move commands, room transitions are now handled through object or item (think scroll of teleportation) actions.

Simple Exit Object

objects: {
    north_door: {
        name: "north door",
        roomDescription: "A door leads north",
        actions: [
            {
                command: ["go north", "north", "enter door"],
                message: "You go through the north door.",
                goto: "dining_room",
                transition: "fade"
            }
        ]
    }
}

Exit Object with Template-Based Action

objects: {
    west_door: {
        name: "west",
        roomDescription: "A door leads [[]]",
        actions: [
            {
                template: "go", // provides command names
                outcomes: [
                    {
                        condition: "!door_locked",
                        goto: "library",
                        message: "You enter the library.",
                    },
                    {
                        message: "The door is locked.",
                    }
                ]
            }
        ]
    }
}

Template Definition for Movement

This is a simple template that just defines the commands for movement, but it can be enhanced by for example counting the number of room transitions (time => exhaustion), printing a message.

templates: {
    go: {
        command: ["go {object}", "move {object}", "{object}"],
        icon: "move"
    }
}

Room Object Properties

Objects are interactive elements within rooms that players can usually examine and manipulate.

Properties

### Example

object: {
    name: ["wooden door", "door"],
    description: "A sturdy wooden door.",
    roomDescription: "A [[]] stands to the north.", // [[]] is replaced with clickable link to items/objects first action (typically examine)
    visible: "!player_blind",
    actions: { /* action definitions */ },
}

Action Properties

Actions define what players can do with objects and items.

Properties

Example

actions: [
    {
        command: ["open {object}", "unlock {object}"],
        condition: "door_locked && inventory.key",
        message: "You unlock the door.",
        sets: { door_locked: false }
    }
]

Prefix Field for Say Commands

Actions can define a prefix field to allow both direct commands and prefixed commands (like "say hello" vs just "hello").

Example: Localized Say Commands

actions: [
    {
        command: { en: "hello", cz: "ahoj" },
        prefix: { en: "say", cz: "řekni" },
        message: { en: "Hello there!", cz: "Ahoj!" }
    }
]

This allows both:

Clickable Links with Prefixes

When using hello in room descriptions, the generated clickable command will automatically include the prefix: "say hello" (localized based on current language).

Achievement System

Achievements provide recognition for player accomplishments and milestones. They are automatically unlocked when specific actions are performed and persist across game sessions.

Achievement Field in Actions

Actions can include an achievement field to unlock achievements when the action is successfully performed.

Properties

- name: string - Unique identifier for the achievement (used internally)
- description: string|object - Achievement description (supports localization)
- hidden: boolean (optional) - Currently not used (default: false)

Example Achievement Actions

actions: [
    {
        command: ["open {object}", "unlock {object}"],
        condition: "door_locked && inventory.key",
        message: "You unlock the door with the key!",
        sets: { door_locked: false },
        achievement: {
            name: "First unlock",
            description: "Unlocked your first door",
        }
    },
    {
        command: "examine statue",
        condition: "!statue_examined",
        message: "You discover ancient runes on the statue!",
        sets: { statue_examined: true },
        achievement: {
            name: "scholar",
            description: "Discovered hidden knowledge"
        }
    }
]

Achievement Usage Ideas

Audio System

The audio system allows adding sound effects and background music that enhances the game experience. Sounds play once, while music loops with smooth crossfading transitions.

Audio Resource Definition

Define audio resources in the adventure's audio object:

audio: {
    // Simple format - just a file path
    "stone_steps": "sounds/stone-steps.mp3",

    // Advanced format with volume adjustment
    "combat_music": {
        url: "music/fighting-theme.mp3",
        volume: 0.8  // 0.0 to 1.0, multiplied by master volume
    },
}

Initial Music

Set music in the adventure object to play when the game starts, set music in room objects to override (can use conditions, so different music can play in the same room depending on game state):

music: "ambient_theme"  // ID from audio object

Sound Effects in Actions

Use the sound field to play sound effects when actions occur:

actions: [
    {
        command: "walk north",
        message: "You walk north on the stone floor.",
        sound: "stone_steps",  // Play sound once
        goto: "courtyard"
    },
    {
        command: "drop key",
        message: "You put the key down.",
        sound: "drop",  // Use stock sound (no stock sounds at this time)
        takes: "rusty_key"
    }
]

Music Control in Actions

Use the music field in a room to set background music with crossfading:

dining_room: {
    ...
    music: [
        {
            condition: "uncle_insulted",
            value: "tense_theme"  // Fade to new music
        },
        {
            value: "cheerful_theme"  // Fade to new music
        }
    ]
    ...
}

Audio Behavior

Framework Stock Audio

The framework may provide some built-in sounds that don't need to be defined in your adventure:

Examples of potential additions:

Template System

Templates provide reusable action definitions that can be automatically expanded during game initialization.

Template Definition

templates: {
    examine: {
        command: ["examine {object}", "look {object}"],
        icon: "look",
        message: "{description}"
    },
    take: { // includes localizaion
        command: { en: ["take {object}", "get {object}"], cz: ["vezmi {object}", "seber {object}", "zvedni {object}"] },
        icon: "take",
        outcomes: [
            {
                condition: "roomItem.{id}",
                message: { en: "You take the {name}.", cz: "{Name} byl přidán do inventáře." },
                takes: "{id}"
            }
        ]
    },
    drop: {
        command: { en: ["drop {object}", "put {object}", "place {object}"], cz: ["polož {object}", "odlož {object}", "vlož {object}", "zahoď {object}"] },
        icon: "drop",
        outcomes: [
            {
                condition: "inventory.{id}",
                message: { en: "You drop the {name}.", cz: "{Name} byl odebrán z inventáře." },
                drops: "{id}"
            }
        ]
    }
}

Template Usage

Templates can be used in actions through string references or template objects:

String Reference (Simple)

actions: ["examine", "take", "drop"]

Template Override (Advanced)

actions: [
    "examine",  // Use template as-is
    {
        template: "take",  // Base on template
        outcomes: [        // Custom outcomes override template
            {
                condition: "player_strength < 10",
                message: "You are not strong enough to pick {name} up.",
            }
            // if the above condition is not met, the outcome in template is executed and item is picked up
        ]
    }
]

Template Processing

Template Fields

Templates support all standard action properties:

Standard Text Adventure Actions

All usual text adventure built-in commands like take and drop have been removed in favor of template-based actions that support full localization and allow much more flexibility. You can customize for example pick up messages to fit your adventure language style.

Basic Take/Drop Setup

templates: {
    examine: {
        command: { en: ["examine {object}", "look {object}", "look at {object}", "inspect {object}"], cz: ["prozkoumej {object}", "prohlédni {object}", "podívej {object}"] },
        icon: "look",
        message: "{description}"
    },
    go: {
        command: { en: ["go {object}", "move {object}", "{object}"], cz: ["jdi {object}", "jdi na {object}", "jdi do {object}", "{object}"] },
        icon: "move",
    },
    take: {
        command: { en: ["take {object}", "get {object}"], cz: ["vezmi {object}", "seber {object}"] },
        icon: "take",
        outcomes: [
            {
                condition: "roomItem.{id}",
                message: { en: "You take the {name}.", cz: "Vezmeš {name}." },
                takes: "{id}"
            }
        ]
    },
    drop: {
        command: { en: ["drop {object}", "put {object}"], cz: ["polož {object}", "odlož {object}"] },
        icon: "drop",
        outcomes: [
            {
                condition: "inventory.{id}",
                message: { en: "You drop the {name}.", cz: "Položíš {name}." },
                drops: "{id}"
            }
        ]
    },
    say: {
        prefix: { en: "say", cz: "řekni" }
    }
}

Global Commands (Hints & Error Messages)

The framework supports global command handlers that can execute actions when specific commands are entered. This includes contextual error messages and help systems:

commands: [

   // Hint command with conditional messages
   {
       command: "hint",
       buttonType: "simple",
       message: [
           {
               condition: "room.entrance && !visited.dungeon",
               value: "Try examining the door and surrounding areas.",
           },
           {
               condition: "inventory.lockpick",
               value: "You might find a use for that lockpick somewhere.",
           },
           {
               value: "Try examining everything you come across.",
           }
       ]
   },
   // Help command
   {
       command: "help",
       buttonType: "simple",
       message: "Welcome to this text adventure! Type commands to explore, solve puzzles, and complete quests. Common commands include: 'go north/south/east/west', 'examine [object]', 'take [item]', 'use [item]'. Use the menu toggle button in the upper right corner to access settings.",
   },
   // Contextual error messages
   { template: "go", message: "You can't go that way." },
   { template: "examine", message: "You don't see that here." },
   { template: "take", message: "You don't see that here." },
   { template: "drop", message: "You don't have that item." },
   // Last item = generic fallback
   { message: "I don't understand that command." }

]
`

Command Processing:

  1. During template initialization, commands with template fields are enhanced with base commands (e.g., "examine" from "examine {object}")
  2. When commands fail to find valid targets, the system checks processed command definitions for contextual messages
  3. Falls back to generic error if no specific match is found
  4. Global commands can execute actions with state changes and conditional content

Item with Take/Drop Actions

items: {
    sword: {
        name: ["sword", "blade"],
        description: "A sharp steel sword.",
        actions: ["take", "drop"]  // Enables take/drop commands for this item,  will work for both 'take sword' and 'take blade'
    }
}

Advanced Take/Drop Customization

items: {
    treasure_chest: {
        name: "treasure chest",
        description: "A heavy chest filled with gold.",
        actions: [
            {
                template: "take", // note that if you did not include the "take" action at all, the pleyer would not be able to pick the chest up, but they will receive the default error message
                outcomes: [
                    {
                        condition: "roomItem.treasure_chest", // override the standard outcome
                        message: "The treasure chest is too heavy to carry!",
                    }
                ]
            },
            "drop"  // Normal drop behavior (using drop template)
        ]
    },
    potion: {
        name: { en: ["healing potion", "potion"], cz: ["léčivý lektvar", "lektvar"] },
        description: { en: "A red potion that glows faintly.", cz: "Červený lektvar, který slabě září." },
        actions: [
            "take",
            {
                template: "drop",
                outcomes: [
                    {
                        condition: "inventory.potion && room.garden",
                        message: { en: "You carefully place the potion on the soft grass.", cz: "Opatrně položíš lektvar na měkkou trávu." },
                        drops: "potion"
                    },
                    {
                        condition: "inventory.potion", // important to use condition to take precedence over template
                        message: { en: "You drop the potion. The vial shatters on impact!", cz: "Položíš lektvar. Lahvička se při dopadu rozbije!" },
                        removes: "potion"
                    }
                ]
            }
        ]
    }
}

Commands Available

With the above setup, players can use these commands:

Take Commands:

Drop Commands:

Conditional Take/Drop

items: {
    guarded_key: {
        name: "guarded key",
        description: "A key guarded by a sleeping dragon.",
        actions: [
            {
                template: "take", // customized take behavior
                outcomes: [
                    {
                        condition: "!dragon_awake",
                        message: "You quietly take the key while the dragon sleeps.",
                        takes: "guarded_key"
                    },
                    {
                        condition: "dragon_awake",
                        message: "The dragon wakes and blocks your attempt!",
                        adjusts: { player_health: -20 }
                    }
                ]
            },
            "drop"  // Normal drop behavior
        ]
    }
}

Dialogue and Speech (Using Actions with Prefixes)

Speech and dialogue interactions are handled through regular actions with the prefix field, providing full localization support.

Basic Speech Action

objects: {
    guard: {
        name: { en: "guard", cz: "strážce" },
        roomDescription: {
            en: "A stern-looking guard stands watch.",
            cz: "Přísně vypadající strážce stojí na stráži."
        },
        actions: [
            {
                command: { en: "hello", cz: "ahoj" },
                prefix: { en: "say", cz: "řekni" },
                condition: "!guard_defeated",
                message: {
                    en: "The guard nods politely.",
                    cz: "Strážce zdvořile přikývne."
                },
                sets: { guard_greeted: true }
            }
        ]
    }
}

Multiple Dialogue Options

objects: {
    shopkeeper: {
        name: { en: "shopkeeper", cz: "obchodník" },
        actions: [
            {
                command: { en: ["hello", "hi"], cz: ["ahoj", "čau"] },
                prefix: { en: "say", cz: "řekni" },
                message: {
                    en: "\"Welcome to my shop!\" the shopkeeper says.",
                    cz: "\"Vítejte v mém obchodě!\" řekne obchodník."
                }
            },
            {
                command: { en: ["buy sword", "purchase sword"], cz: ["koupit meč", "zakoupit meč"] },
                prefix: { en: "say", cz: "řekni" },
                condition: "coin >= 10",
                message: {
                    en: "\"Here's your sword!\" You hand over 10 coins.",
                    cz: "\"Tady je tvůj meč!\" Předáš 10 mincí."
                },
                adjusts: { coin: -10 },
                creates: "sword"
            },
            {
                command: { en: "goodbye", cz: "na shledanou" },
                prefix: { en: "say", cz: "řekni" },
                message: {
                    en: "\"Come back anytime!\" the shopkeeper waves.",
                    cz: "\"Přijďte kdykoliv!\" obchodník zamává."
                }
            }
        ]
    }
}

Conditional Dialogue

objects: {
    mysterious_figure: {
        name: "mysterious figure",
        actions: [
            {
                template: "say",
                command: "who are you",
                outcomes: [
                    {
                        condition: "!figure_revealed",
                        message: "\"That information will cost you.\" The figure smirks.",
                    },
                    {
                        condition: "figure_revealed",
                        message: "\"I am the keeper of secrets.\" The figure bows deeply.",
                    }
                ]
            }
        ]
    }
}

Clickable Dialogue Links

roomDescription: {
    en: "The guard eyes you suspiciously. Try [[hello]] or [[who are you|ask who he is]].",
    cz: "Strážce vás podezřívavě pozoruje. Zkus [[ahoj]] nebo [[kdo jsi|zeptat se kdo je]]."
}

Localization Format

Languages supported in an adventure should be declared.

translations: {
    en: {
        name: "English",
    },
    cz: {
        name: "Čeština",
        diacritics: true,
        inventory: "Inventář",
        submit: "Odeslat",
        placeholder: "Zadejte příkaz..."
    }
},

Localization is available via a structured object format for all text fields.

message: {
    en: "English text",
    cz: "Czech text"
}

instead of the normal

message: "English text"

Supported Fields

All text fields can be localized:

Fallback Behavior

Condition System

Conditions determine when features are available, visible, or active.

Condition Types

Simple Boolean

condition: "door_unlocked"
condition: "!guard_defeated"

Comparisons (<, <=, >, >=, == (equal), != (not equal))

condition: "player_health > 50"
condition: "roomItems >= 2"
condition: "inventory.sword == 1"
condition: "location == 'dungeon'"  // String literals in single quotes

String Literals

String values must be enclosed in single quotes:

condition: "room.entrance && player_name == 'hero'"
condition: "current_quest != 'completed'"

Complex Expressions

&& (logical AND) and || (logical OR) can be used; && has higher priority than || during evaluation; () are not supported.

condition: "door_unlocked && player_health > 0"
condition: "guard_defeated || guard_befriended"
condition: "location == 'dungeon' && inventory.key"

Condition Context

Condition Examples

// Door is unlocked
condition: "!door1_locked"

// Player has sword in inventory
condition: "inventory.sword"

// In library and fire is lit
condition: "room.library && fire1_lit"

// Health is critical
condition: "player_health <= 10"

Game State

Game state tracks variables that change during gameplay.

Initial State

gameState: {
    player_health: 100,
    coin: 30,
    door1_locked: true,
    guard1_defeated: false,
    treasure_found: false
}

State Modifications (During Action Execution)

sets: {
    door1_locked: false,           // Fixed value
    damage: [20, 30],              // Random 20-30
    treasure_found: true
}
adjusts: {
    player_health: -20,            // Fixed subtraction
    enemy_health: "damage",        // Add gameState.damage value
    enemy_health: "-damage",       // Subtract gameState.damage value
    gold: 50                       // Fixed addition
}

Random Numbers

The framework supports random number generation for dynamic gameplay, allowing adventures to include variable combat, treasure, and other mechanics.

Random Values in sets

Use arrays [min, max] in the sets field to generate random numbers:

actions: [
    {
        command: "use {object}",
        sets: {
            club_damage: [20, 30],  // Generate random 20-30
            gold_found: [5, 15]     // Generate random 5-15
        },
        adjusts: {
            enemy_health: "club_damage",  // Apply the random damage
            player_gold: "gold_found"     // Add the random gold
        },
        message: "You deal {club_damage} damage and find {gold_found} gold!"
    }
]

Random Values in Messages

Random values stored in gameState can be displayed in messages using {variableName} placeholders:

message: "You hit the enemy for {club_damage} damage!"

State References in adjusts

Use string values in adjusts to reference previously generated random values. Prefix with - to subtract instead of to add:

adjusts: {
    enemy_health: "-club_damage",   // Subtract stored random damage
    player_gold: "gold_found"       // Add stored random gold
}

Complete Combat Example

actions: [
    {
        command: "attack",
        triggers: [ // generate numbers first, then we can use them in outcome conditions
            {
                sets: {
                    base_damage: [15, 25],     // Base attack damage
                    hit_roll: [1, 10]       // Hit chance
                }
            }
        ]
        outcomes: [
            {
                condition: "hit_roll >= 8",
                adjusts: { enemy_health: "-base_damage" },
                message: "You deal {base_damage} damage."
            },
            {
                message: "You miss."
            }
        ]
    }
]

Random Number Properties

Usage Patterns

Variable Combat Damage

sets: { sword_damage: [8, 12] },
adjusts: { enemy_health: "sword_damage" },
message: "Your sword strikes for {sword_damage} damage!"

Treasure Discovery

sets: { coins_found: [1, 10], gems_found: [0, 2] },
adjusts: { player_coins: "coins_found", player_gems: "gems_found" },
message: "You find {coins_found} coins and {gems_found} gems!"

Random Events

sets: { event_chance: [1, 100] },
message: [
    { condition: "event_chance <= 25", value: "You find a hidden passage!" },
    { condition: "event_chance <= 50", value: "You hear strange noises." },
    "Nothing unusual happens."
]

Note: you would have to use triggers+outcomes to actually "create" the hidden passage
and not just print a message that you found it.

Triggers

Triggers allow multiple independent conditions to execute simultaneously within a single action. Triggers execute before outcomes.

Triggers vs Outcomes

Trigger Syntax

Triggers use the same structure as outcomes but execute all matching conditions:

actions: [
    {
        command: "rest",
        message: "You have finished resting.",
        triggers: [
            {
                condition: "poisoned",
                message: "The poison continues to weaken you.",
                adjusts: { health: -3 }
            },
            {
                condition: "bank_account > 100",
                message: "Your savings earn interest.",
                adjusts: { gold: 5 }
            },
            {
                condition: "hungry",
                message: "Resting eases your hunger.",
                sets: { hungry: false }
            }
        ]
    }
]

Execution Flow

  1. Triggers: Execute all matching trigger conditions in sequence (loops possible)
  2. Main Action/Outcome: Execute base action message and state changes
  3. Combine Results: Merge all messages and state changes

Result Combination

Complete Rest Example

Trigger Execution:

Combined Output:

The poison continues to weaken you.

Your savings earn interest.

Resting eases your hunger.

You have finished resting.

Usage Patterns

Status Effects (Poison, Hunger, Bank Interest)

triggers: [
    {
        condition: "poisoned",
        message: "Poison courses through your veins.",
        adjusts: { health: -5 }
    },
    {
        condition: "bleeding",
        message: "Blood continues to seep from your wounds.",
        adjusts: { health: -3 }
    },
    {
        condition: "hungry",
        message: "Your stomach growls with hunger.",
        adjusts: { health: -1 }
    }
]

Environmental Effects

triggers: [
    {
        condition: "room_temperature > 80",
        message: "The heat saps your strength.",
        adjusts: { health: -2 }
    },
    {
        condition: "room_humidity > 90",
        message: "The oppressive humidity makes breathing difficult.",
        adjusts: { health: -1 }
    },
    {
        condition: "hour > 20 || hour < 6",
        message: "The darkness presses in around you.",
        sets: { visibility: "poor" }
    }
]

Time-Based Effects

triggers: [
    {
        adjusts: { game_time: 1 }
    },
    {
        condition: "game_time > 100",
        message: "You feel the weight of time pressing upon you.",
        adjusts: { morale: -1 }
    }
]

Repeat Field in Triggers

Triggers support a repeat field for executing the same trigger multiple times, enabling complex command behaviors like buying multiple items.

Repeat Syntax

triggers: [
    {
        condition: "inventory.food < 5 && credits >= price_food",
        repeat: true,  // Repeat until condition fails
        message: "You buy 1 unit of food.",
        adjusts: { credits: "-price_food", cargo_used: 1 },
        creates: "food"
    }
]

Repeat Values

Repeat Execution Rules

Complete Multi-Buy Example

actions: [
    {
        command: "buy 5 food",
        message: "Purchasing food...",
        triggers: [
            {
                // setup
                sets: { bought: 0, cost: 0 }
            },
            {
                condition: "bought < 5 && credits >= price_food",
                repeat: true,
                adjusts: { credits: "-price_food", bought: 1, cost: "price_food" },
                creates: "food",
                takes: "food"
            },
            {
                // report results
                message: "You bought {bought} unit(s) of food for {cost} credits.",
            },
            {
                // cleanup
                sets: { bought: 0, cost: 0 }
            }
        ]
    }
]

Advanced Repeat Patterns

// Repeat with step back (sleep for 8 hours, but wake up if ambush occurs)
triggers: [
    { sets: { counter: 0, ambush_chance: 0 } }
    { sets: { ambush_chance: [0, 100] }, adjusts { time: 1, hunger: 1 } }
    { condition: "ambush_chance < 90 && counter < 8", adjusts: { counter: 1 }, repeat: -1 }, // Go back 1 step
    { message: [ { condition: "counter >= 8, value: "You slept for 8 hours." }, "You were ambushed!" ] }
]
// Conditional repeat
triggers: [
    {
        condition: "health_potions_needed > 0 && credits >= 10",
        repeat: 0,  // Repeat current stepm sane as repeat: true
        message: "You buy a health potion.",
        adjusts: { credits: -10, health_potions_needed: -1 },
        creates: "health_potion"
        takes: "health_potion"
    }
]

End Conditions

Define when the game ends (victory or defeat).

Properties

- condition: string - Condition that triggers the ending
- message: string|object - Ending message (supports localization)

Example

ends: {
    treasure: {
        condition: "treasure_found && map_collected",
        message: "You found the treasure!"
    },
    death: {
        condition: "player_health <= 0",
        message: "You have died!"
    }
}

Conditional Content Arrays

Many fields support conditional content using arrays with condition-value pairs.

Format

description: [
    {
        condition: "treasure_found",
        value: "The treasure chest is open!",
    },
    {
        condition: "!treasure_found",
        value: "An empty chest lies here.",
    },
    "A mysterious chest." // Fallback text
]

Evaluation Order

  1. Check each condition-value pair in order
  2. Use first matching condition's value
  3. If no conditions match, use fallback string
  4. If no fallback exists, return empty string

Interactive Elements

Placeholders

Text can include placeholders that are replaced with entity information:

Clickable Commands

Text can include interactive command links:

Note: there is no space between the [ [, but the same format is used by the web, so space was added to prevent web formatting.

Examples

Basic Placeholders

roomDescription: "A {name} stands to the north."
// Results in: "A wooden door stands to the north." (no clickable text)

Indexed Names (for languages with declension and finetuning)

message: "You give the {name2} to the blacksmith."
// Results in: "You give the blade to the blacksmith."
// object has name field with ["sword", "blade"]

Intelligent Command Links

description: "A locked door. Try to [[unlock]] it!"
// Creates clickable "unlock" that finds the unlock action on the door (executes for example "unlock wooden door" if the action belongs to a "wooden door" object)

Custom Text And Command

description: "You [[examine painting|look closely]] at the painting."
// Shows "look closely" but executes "examine painting"

Custom Clickable Text

description: "You see [[|a mysterious door]] leading north."
// Shows "a mysterious door" as clickable text with auto-generated command (like [[]])

Auto-Generated Links

description: "There is [[]] on the table."
// Automatically creates clickable spans for the entity's first action using object name

Image System

Rooms can display images based on conditions.

Static Image

image: "entrance.png"

Conditional Images

image: [
    {
        condition: "guard_defeated",
        value: "guard_dead.png"
    },
    {
        condition: "!guard_defeated",
        value: "guard_alive.png"
    }
]

speech bubble iconRecent comments

user icon