Appearance
Config, Save, Keybinds & Passage Utilities
Engine-wide configuration, the save/load system, keybindings, and passage existence/utility queries.
Engine Configuration
Configuration macros are called once in GameInit (@system(init)) and control engine behavior for the entire game session.
All config macros at a glance
Every macro below is called from GameInit @system(init). Grouped by system, with the section that documents it in full.
| System | Macro(s) | Sets up |
|---|---|---|
| Core | (layout: LayoutName) | The active layout template; see UI |
| Core | (start: PassageName) | The passage shown when a new game begins |
| Core | (systems-enable:) / (systems-disable:) | Turns optional systems (dice, quests, achievements, skills) on/off; see System Toggles |
| Variables | (declare: ...) | All variable registration (flat, batch, bounded, skills, per-NPC relationships); see Declaration Macros |
| Variables | (npc-define: "id", ...) | NPC entities (preferred over (declare: $npc.id, ...)); see NPC System |
| Variables | (ns-nested: "name") | A custom nested namespace (like npc); see Loops & Namespace Queries |
| Relationships | (rel-defaults: ...) / (rel-tiers: ...) | Global bounds/initial and tier labels for $rel.*; see Relationships |
| Skills | (skill-range: min, max) | Default bounds for $skill.* variables; see Skills |
| Items | (item-define: "id", ...) | Item templates (stackable or unique); see Items |
| Equipment | (equip-define: slotName[, capacity]) | Equipment slots; see Equipment |
| Containers | (container-define: "id") | Named containers; see Containers |
| Time | (time-periods: ...), (time-mode:), (time-start:), (time-calendar:), (clock-format:), (date-format:) | Day/period structure, calendar, and clock display; see Time |
| Dice | (dice-range: modMin, modMax) | The stat-modifier scale for (dice:); see Dice Rolling |
| Statuses | (status-define: "id", ...), (status-duplicate: mode) | Status effect templates and re-apply behavior; see Status Effects |
| Keybinds | (keybind-define: "id", ...) | Custom keyboard shortcuts; see Keybind System |
| Quests | (quest-define: "id", ...), (quest-link-format: ...) | Quest definitions and reward-link prefixes; see Quest System |
| Achievements | (achieve-define: "id", ...) | Achievement definitions and tiers; see Achievement System |
| Shops | (shop-define: "id", ...), (shop-add: ...) | Shop merchants/markup and stocked items; see Shop System |
| Currency | (currency-define: ...) | Named currencies; see Currency System |
| Theme | (theme-define: "id", "Label"), (theme: "id") | Available themes and the default/active one; see Theme System |
| Transitions | (default-transition: key, "value"[, durationMs]) | Default passage/text transition effects; see Transitions |
| UI / Feedback | (header-shortcuts:), (feedback-categories:), (feedback-default:), (notify-position:), (feedback-position:), (save-autosave:), (story-log:) | Header bar, feedback overlay position/filtering, and autosave/story-log toggles; documented below |
| Audio / Accessibility | (audio-channel: name), (font-scale: N) | Extra audio channels and UI text scale; see Audio / Accessibility |
(header-shortcuts: label, label, ...)
Registers labels for shortcuts shown in the header bar.
ana
(header-shortcuts: "Inventory", "Quests", "Map")(feedback-categories: category, category, ...)
Limits the feedback overlay to the listed categories. If omitted, all feedback is shown. (Per-call custom messages, feedback, "…", always show regardless of this filter.)
ana
(feedback-categories: "statchanges", "relationships", "items")(feedback-default: category, on|off, ...)
Turns the default feedback message for a category on or off, in alternating category/state pairs. Off means mutation macros in that category stop emitting their automatic message, but a per-call feedback, "message" override still shows. Use this to globally quiet a noisy category without sprinkling feedback, false on every call.
ana
(feedback-default: "statchanges", off) // silence automatic stat messages
(feedback-default: "items", on, "statuses", off) // multiple pairsFeedback behavior table: what emits a message by default, and in which category:
| Action | Emits by default? | Category |
|---|---|---|
(add:) / (sub:) numeric variable | Yes, "name ±N → value" | statchanges |
(add:) / (sub:) on $rel.* | Yes, "±N relationship with id" | relationships |
(add: $inv, …) / (remove: $inv, …) | Yes, "+N id" / "-N id" | items |
(add: $container, …) | No | None |
| equip displacement / unequip-to-inventory | Yes, "Unequipped: name" | items |
(destroy: …) | No | None |
(status-apply:) / (status-remove:) | Yes, "Name applied/removed" | statuses |
(pay:) / (earn:) | Yes, "Spent/Received: amount" | items |
(shop-buy:) / (shop-sell:) | Yes (success + failure reasons) | items |
| quest state changes | Yes, "Quest …: title" | quests |
Every macro in this table accepts a trailing feedback, "custom message" (replace) or feedback, false (silence) pair as its last arguments. Custom messages support $var interpolation and bypass the category filters above.
ana
(earn: 50, feedback, "He flips you a $50 bill.")
(status-apply: "poison", feedback, false)(notify-position: "position")
Sets the screen position of the notification stack. Valid positions: top-left, top-right (default), bottom-left, bottom-right, top-center, bottom-center. An unrecognized value falls back to top-right. Call in GameInit (or any time; it applies to subsequent notifications).
ana
(notify-position: "bottom-right")(feedback-position: "position")
Sets the screen position of the feedback overlay. Same valid positions as (notify-position:); default is top-left.
ana
(feedback-position: "top-center")(save-autosave: true/false)
Enables or disables autosave. When enabled, the engine writes a session save on every (goto:). Default: disabled.
ana
(save-autosave: true)
(save-autosave: false)(story-log: enabled/disabled)
Enables the player-facing story log: a scrollable modal listing visited passages in reverse order, opened with the configured hotkey (default J).
ana
(story-log: enabled)
(story-log: disabled)(story-log-data:)
Expression macro. Returns the display trace as a reversed array (most recent passage first) for use in the _Story_Log passage. Only meaningful when (story-log: enabled) is set.
ana
:: _Story_Log
@zone(modal)
(each: _entry in (story-log-data:))[
_entry
]
(link: "Close")[(modal-close:)]Save
(save: slot)
Saves to a numbered slot (1–9). User-created saves should use slots 1 and above.
Slot 0 is the autosave slot. The engine writes to slot 0 automatically when autosave is enabled (see (save-autosave:) below). Do not write to slot 0 manually from game passages; it is reserved for engine-managed autosaves.
ana
(save: 1)
(save: 2)(load: slot)
Loads from a slot. Triggers GameInitSave @system(init_save) after loading.
ana
(load: 1)(save-delete: slot) / (save-list:)
ana
(save-delete: 2)
(save-list:) // returns array of slot metadata for building a save UI(game-new:) / (game-end:)
(game-new:) resets all systems and starts a fresh game (runs @system(init) → the (start:) passage). (game-end:) does the opposite: it unloads the current game (resetting runtime state and systems, clearing the session, and restoring the layout your GameInit set with (layout:)) and returns to the @system(title) passage, so the title renders clean, with no stale clock or location. Neither macro confirms; pair (game-end:) with (confirm:) for a "Back to Title" prompt.
ana
:: _Engine_NewGame [action]
(game-new:)
// "Back to Title" with a confirmation prompt:
:: _Menu_BackToTitle
(confirm: $world.confirmReturn, "Return to the title screen? Any unsaved progress will be lost.", "Yes, quit", "Cancel")[
(if: $world.confirmReturn)[(action: _Menu_QuitToTitle)]
]
:: _Menu_QuitToTitle [action]
(game-end:)Autosave configuration (in GameInit)
ana
(save-autosave: false) // disabled
(save-autosave: true) // fires on (goto:) only (default)Keybind System
Engine-level keyboard shortcuts. Authors declare default bindings; players can remap them (overrides saved in localStorage). Keybinds are not saved in the game save; they are per-browser accessibility config.
Pre-registered engine defaults. The following keybinds are always active at engine boot. Authors do not need to register these; they can be overridden by calling (keybind-define:) with the same id in GameInit:
| ID | Default key | Target passage | Notes |
|---|---|---|---|
menu | Escape | GameMenu | Always closes an open modal first |
inventory | i | InventoryScreen | Opens as modal |
story-log | j | _Story_Log | Only active if story log is enabled |
achievements | a | AchievementsScreen | Opens as modal |
quests | q | QuestLog | Opens as modal |
Escape additionally always closes the top-most open modal regardless of keybind configuration; this is hardwired behavior and cannot be disabled.
(keybind-define: "id", key, "key", passage, "PassageName")
Declares a keybind. Call in GameInit. The engine pre-registers 5 default bindings (see table above); calling (keybind-define:) with the same id in GameInit overwrites them.
Optional kwarg action, "goto" navigates rather than opening a modal. Defaults to "modal".
ana
(keybind-define: "inventory", key, "i", passage, "InventoryScreen")
(keybind-define: "menu", key, "Escape", passage, "GameMenu")
(keybind-define: "map", key, "m", passage, "WorldMap", action, "goto")(keybind-disable: "id")
Disables a keybind without removing it. Useful for temporarily suppressing a shortcut (e.g., inventory disabled in combat).
ana
(keybind-disable: "inventory")(keybind-rebind: "id", "newKey")
Remaps a keybind to a new key. Saves the override to localStorage so it persists across sessions. Intended for player-facing settings screens.
ana
// In a settings action passage:
(keybind-rebind: "inventory", "k")
(notify: "Inventory key rebound to K.")(keybind-ids:)
Expression macro. Returns an array of all defined keybind IDs. Use in settings screens to build a rebinding UI without hardcoding IDs.
ana
(each: _id in (keybind-ids:))[
_id — current key: (print: (keybind-key: _id))
](keybind-key: "id")
Expression macro. Returns the effective key string for a keybind: the player's remapped key if set, otherwise the author default.
ana
Inventory is bound to: (print: (keybind-key: "inventory"))Behavior rules
- Form element focus: keybinds are suppressed when an
<input>,<textarea>, or<select>element is focused. - Modal open: when a modal is open, all keybinds except
Escapeare suppressed.Escapealways closes the top modal. - Engine defaults: if
GameInitdoes not call(keybind-define: "inventory", ...), the engine usesi → InventoryScreen. Same for"menu" → GameMenu. Override by calling(keybind-define:)with the same id.
Passage Utilities
(passage-exists: "PassageName") / (exists: "PassageName")
Expression macro. Returns true if a passage with that name exists in the game. Use to gracefully degrade when optional content (like a mod passage) might not be present. (exists:) is a shorter alias.
ana
(if: (passage-exists: "DartsMod_Game"))[
(link: "Play darts")[(goto: DartsMod_Game)]
]
(if: (exists: "SecretRoom"))[You notice a hidden door.](log: value, "level")
Writes a value to the browser developer console, prefixed with [Ana]. The optional second argument sets the level: debug, info (default), warn, or error. A debugging aid; it produces no in-game output.
ana
(log: $player.gold) // info level
(log: "reached the docks", "debug")
(log: $quest.find_thief, "warn")(trigger: eventName)
Manually fires an event hook, causing any @on(eventName) passages to execute. The preferred form passes the event name as a string, which is also how you fire custom mod events. An @on(...) directive form is accepted as an equivalent for the built-in events.
ana
(trigger: "dayAdvance") // preferred — string form (works for custom events too)
(trigger: "mymod.boss_defeated") // custom event
(trigger: @on(gameLoad)) // accepted equivalent for built-in events(modal-open: screenName) / (modal-close:)
Opens or closes a modal overlay (inventory screen, character sheet, etc.). Pauses passage execution. Closing returns to exactly the state before the modal opened.
ana
(modal-open: inventory)
(modal-open: questlog)
(modal-close:)Mods
The engine populates a reserved $mods namespace at boot, keyed by mod name, so a game can react to which mods are present. Each entry exposes author, version, requires, conflicts, passages (names of the mod's passages), wraps (passages it @wraps), and into (hooks it @intos).
ana
(get: $mods, "Extra Outfits", "version") // "1.2.0"
(each: _hook in (get: $mods, "Extra Outfits", "into"))[ … ]
$modsis engine-managed; don't(declare:)into it.modsis also a reserved namespace name.
(mod-list:)
Returns an array of the names of all loaded mods (the ids in $mods).
ana
(each: _m in (mod-list:))[
Loaded: _m
](mod: "Mod Name")
Returns true if a mod with that name is loaded, for optional, mod-aware content.
ana
(if: (mod: "Extra Outfits"))[
You see an extra rack of clothes against the wall.
]