Appearance
Hooks & Events
Injecting and wrapping passages, event handlers, and passage-tag-driven event design.
Reference: Events, Tags, Status & Dice for the full event catalog.
Hooks and Injection: @hook / @into
Hooks are named insertion points in a passage. Other passages inject content into them using @into(hookName).
Declaring a hook:
ana
:: BarScene [location]
The bartender watches you without expression.
@hook(bar_options) ← injection point
@zone(options)
(link: "Leave")[(goto: MainStreet)]Injecting into a hook (from another passage, typically a mod or character file):
ana
:: Bartender_BarOption @into(bar_options)
(link: "Approach the bar")[(update: Bar_BartenderGreet)]The injected passage's content appears at the @hook location, before the passage's own content continues. Multiple injections are stacked in load order.
Placement: injections append by default. Use @into(hook, prepend) to put content ahead of other injections at the same hook, or @into(hook, append) to be explicit. All prepends render before all appends; load order is preserved within each group. (@into only adds content; to replace base content, use @wrap without @wrapped().)
Why this matters for mods: a mod can add new NPCs, options, and content to any scene that has hooks, without modifying the base game files at all. This is the primary extension mechanism.
Wrapping Passages: @wrap
A wrapper passage frames another passage. The wrapped passage's content appears wherever @wrapped() is placed inside the wrapper.
ana
:: CinematicFrame @wrap(Bar_BigReveal)
(audio: dramatic_sting, sfx, play)
@zone(text)
[The screen dims slightly.]
@wrapped()
[The screen returns to normal.]When Bar_BigReveal is navigated to, CinematicFrame renders first, and Bar_BigReveal's content is inserted at the @wrapped() position.
Event Handlers: @on
Event handler passages run when a named event fires. They don't need to be navigated to; they execute automatically.
ana
:: Player_OnDayAdvance @on(dayAdvance)
(set: $world.todayEvents to []) // clear today's event array
(add: $world.days, 1)
(notify: "A new day begins.")Fire events manually with (trigger:), or let the engine fire them (e.g., dayAdvance fires automatically when (time-advance:) crosses midnight).
Common engine events: goto, gameLoad, dayAdvance, periodChange, locationEnter, questStart, and shopBuy. See Tag & Event Architecture for the complete list.
@on(goto): persistent UI zones
The goto event fires on every navigation, after the main passage has rendered. Use it to populate persistent zones that should update on every screen (sidebars, header) without putting that logic in each passage.
ana
:: UI_CharPanel @on(goto)
@zone(sidebar_character)
(portrait: character, player, neutral)
$player.name
(meter: $player.health, $player.maxHealth)
Gold: $player.goldAuthor contract: @on(goto) passages must not call (goto:) themselves. Doing so creates infinite recursion: the handler fires, which triggers another goto, which fires all handlers again. Handlers should only write to zone targets.
Passage Tags & Event-Driven Design
Passage tags let the engine automatically fire events when the player navigates to certain kinds of locations. Combined with @on handlers, they allow you to write reactive logic without polluting every passage with callbacks.
Tagging a passage
Tags go in brackets after the passage name:
ana
:: Bar_Main [location bar]
:: Chapter2_Starts [chapter]
:: TheBlacksmith [location shop]
:: TrueEnding [ending]Multiple tags are allowed. A shop that is also a location uses both:
ana
:: Marketplace [location shop]What happens on navigation
When (goto:) reaches a tagged passage, the engine fires events after the passage renders:
| Tag | Event | Side effect |
|---|---|---|
location | locationEnter | $world.location = passage name |
chapter | chapterStart | $world.chapter = passage name |
shop | shopEnter | None |
ending | endingReached | None |
| (custom tag) | (tag name verbatim) | None |
$world.location and $world.chapter are declared lazily, with no (declare:) needed.
Handling tag events
ana
:: _On_LocationEnter @on(locationEnter)
(feedback: "You are now at $world.location.")
:: _On_ChapterStart @on(chapterStart)
(set: $world.chapterSeen to true)
(notify: "Chapter begins: $world.chapter")Full system event reference
For all engine events (including questStart, shopBuy, statusApply, etc.) and their context temp vars, see Tag & Event Architecture in the macro reference.
Custom events
Tag any passage with your own word:
ana
:: Dungeon_Level2 [dungeonDepth2]This fires a dungeonDepth2 event on arrival. Handle it:
ana
:: _On_DungeonDepth2 @on(dungeonDepth2)
(if: $world.torches < 1)[(notify: "It's very dark down here.")]You can also fire custom events manually with (trigger: "eventName"). The same handlers respond.
The $world.location pattern
$world.location always holds the name of the last [location] passage visited. Use it for location-relative displays:
ana
:: _On_LocationEnter @on(locationEnter)
@zone(header)
$world.locationThis puts the current location name in the header automatically on every location arrival, with no per-passage code.