Effect tokens#
Effect tokens are the verbs you chain in interactions, puzzle rules and cutscenes to
change the game: set a flag, grant an item, move an NPC, toggle a walkable area. Each
token is written as VERB:arg1|arg2 (or a bare VERB when it takes no argument), and the
editors autowire the arguments for you.
| Token | What it does | Arguments |
|---|---|---|
SETFLAG |
Remembers that something happened (flips a switch on). | Flag |
CLEARFLAG |
Forgets that something happened (flips a switch off). | Flag |
TOGGLEFLAG |
Flips a remembered switch to its opposite. | Flag |
SETVAL |
Stores a value like a password or number (blank clears it). | Flag, Value (blank = clear) |
APPENDVAL |
Adds a chunk onto a stored value, like a keypad digit. | Flag, Append, Max length (blank = no cap) |
ADDVAL |
Adds to a counter (use a minus to subtract). | Flag, Amount (+/-) |
RANDVAL |
Rolls a random number and stores it, like dice. | Flag, Min, Max |
BACKSPACEVAL |
Deletes the last character of a stored value. | Flag |
CATCHTARGET |
Picks which of your characters a chaser catches. | Antagonist NPC, Candidate chars, Mode, Out flag (def caughtChar) |
PARTYTOGGLE |
Adds or removes a character from your chosen team. | Character |
PICKUP |
Takes an object from the scene into your inventory. | Hotspot to remove, Item to grant (blank = same id) |
REMOVEITEM |
Removes an item from your inventory. | Item |
GIVEITEM |
Hands an item to a specific character. | Item, Target character |
RESTOREHOTSPOT |
Brings a removed object back into the scene. | Hotspot id |
WALKABLE_ON |
Opens up a walkable path in a room. | Walkable area |
WALKABLE_OFF |
Blocks off a walkable path in a room. | Walkable area |
REGION_ON |
Turns on a walk-over trigger zone. | Region |
REGION_OFF |
Turns off a walk-over trigger zone. | Region |
LIGHTZONE_ON |
Switches a light zone on, lighting or tinting whoever stands in it. | Light zone |
LIGHTZONE_OFF |
Switches a light zone off, leaving the area unlit. | Light zone |
NPCEXIT |
Sends a character walking off to another room. | NPC, Dest room, Walk-to |
NPCWALKTO |
Walks a character to a spot and has them wait there. | NPC, Destination, Facing (opt), Speed (dur=ms | speed=px/s), Options |
NPCRETURN |
Brings a character back from another room. | NPC, Dest room |
NPCWARPTOROOM |
Instantly places a character in a room. | NPC, Room, At, Facing (opt) |
FOLLOW |
Makes a character trail after another one. | NPC, Target, Stop distance px (opt) |
UNFOLLOW |
Stops a character from following. | NPC |
DETACHCOMPANION |
Parks a companion so it stops tagging along. | Companion |
ATTACHCOMPANION |
Brings a companion back to tag along with you. | Companion |
NPCSTOP |
Stops a character in its tracks. | NPC |
NPCWAIT |
Makes a character pause for a few seconds. | NPC, Seconds |
BEHAVIOUR |
Starts a character's daily routine of wandering about. | Behaviour |
STOPBEHAVIOUR |
Stops a character's wandering routine. | NPC |
STARTTIMER |
Starts a countdown you can react to. | Timer name, Seconds |
CLEARTIMER |
Cancels a running countdown. | Timer name |
CLEARWATCHER |
Lets a one-time rule fire again. | Watcher id |
SELECTVERB |
Pre-selects one of the nine verbs, like Use. | Verb |
SETCURSOR |
Takes over the mouse cursor: which cursor, frame or position, even visible during a cutscene. | Cursor overrides |
INVENTORY_UP |
Scrolls the inventory bar up a row. | (none: bare token) |
INVENTORY_DOWN |
Scrolls the inventory bar down a row. | (none: bare token) |
SETSENTENCE |
Pins your own text onto the action line at the bottom (the 'Walk to…' bar). | Sentence (#lid | text, {flag}) |
CLEARSENTENCE |
Clears a pinned action-line text, back to normal. | (none: bare token) |
SAY |
Makes your character or narrator speak a line. | Text ({flag}) |
NPCSAY |
Makes a specific character speak a line. | NPC, Text ({flag}) |
NARRATE |
Shows a narrator caption, not tied to any character. | Narrator text ({flag}) |
CLEARNARRATE |
Removes the narrator caption early. | (none: bare token) |
SETFRAME |
Swaps an object's picture for a different one. | Sprite path, Display text (opt) |
SETANIMFRAME |
Jumps an animation to a specific frame. | Frame index, Options |
STARTANIM |
Plays or resumes an animation on a character or object. | Target, Animation (opt), Facing (opt) |
STOPANIM |
Stops a character or object mid-animation, settling to a still pose. | Target, Animation (opt), Facing (opt) |
SHOWHOTSPOT |
Makes an object appear in the scene. | Hotspot id |
HIDEHOTSPOT |
Makes an object vanish from the scene. | Hotspot id |
ENABLEHOTSPOT |
Makes an object clickable again. | Hotspot id |
DISABLEHOTSPOT |
Makes an object stop responding to clicks. | Hotspot id |
TOGGLELIGHT |
Turns the room's light on or off. | Options (dur=ms) |
FADEOUT |
Fades an object out until it vanishes, with an optional caption. | Caption |
MOVEHOTSPOT |
Slides an object to a new spot in the scene. | Hotspot, Destination, Speed (dur=ms | speed=px/s), Options |
SETHOTSPOTMOTION |
Animates an object's scale, rotation and opacity, each one loopable (pulse, spin, fade). | Hotspot, Motion channels |
PLAYMOTION |
Starts or resumes an object's Motion from the Room Editor (sweep, breathe, float). Saved with the game. | Hotspot, Options |
STOPMOTION |
Freezes an object's Motion where it is, or sends it back to rest. | Hotspot, Options |
SETHOTSPOTFX |
Restyles an object's sprite at runtime: shadow, glow, blur, opacity or outline. | Hotspot, FX overrides |
MOVEGUI |
Slides an on-screen panel element to a new spot. | GUI widget (gui.widget), Destination, Speed (dur=ms | speed=px/s), Options |
MODIFYWIDGET |
Restyles a panel element at runtime: colour, border or text. | GUI widget (gui.widget), Overrides |
DIALOG |
Starts a branching conversation. | Dialog id |
CUTSCENE |
Plays a scripted sequence inside the room. | Cutscene |
WARPTOROOM |
Moves the player to another room. | Room id |
WALKTO |
Walks your active character to a spot in the room. | Destination, Facing (opt), Speed (dur=ms | speed=px/s), Options |
CAMERAPAN |
Pans the camera to a point or a hotspot during a cutscene, and holds it there until the cutscene ends. | Destination, Speed (dur=ms | speed=px/s), Options |
HIDEPLAYER |
Takes the player's sprite off screen; they keep walking and taking clicks. | (none: bare token) |
SHOWPLAYER |
Brings the player's sprite back on screen. | (none: bare token) |
OPENGUI |
Opens an on-screen panel. | GUI id, Options |
CLOSEGUI |
Closes the open on-screen panel, or only the one you name. | GUI id (empty = active one) |
OPENCOIN |
Pops the verb coin on a hotspot. | Hotspot id |
CLOSECOIN |
Dismisses the verb coin. | (none: bare token) |
HIDEGUI |
Hides all on-screen panels from view. | (none: bare token) |
SHOWGUI |
Brings the on-screen panels back into view. | (none: bare token) |
SETLANG |
Switches the game's language. | Language |
SWITCHCHAR |
Switches which character you control. | Target character |
SWITCHPARTYSLOT |
Switches control to the team member in a slot. | Party slot (1-based) |
SAVEGAME |
Saves the game to a slot. | Slot (0-5) |
LOADGAME |
Loads the game from a slot. | Slot (0-5) |
SOUND |
Plays a one-off sound effect. | Sound id |
STOPSOUND |
Stops a sound effect that is still playing. It's the way to end one set to loop. | Sound id |
VOICE |
Plays a spoken voice line with subtitle. | Voice line |
MUSIC |
Changes the background music. | Music id |
STOPMUSIC |
Fades out the background music. | (none: bare token) |
AMBIENCE |
Starts a looping background sound, like a fan. | Ambient id |
STOPAMBIENCE |
Stops a looping background sound. | Ambient id |
TOGGLEHOTSPOTAUDIO |
Flips an object's looping sound, like a radio. | Hotspot id |
SETHOTSPOTOPEN |
Sets a door or container to open or closed. | Open state |
SETHOTSPOTLOCK |
Locks or unlocks a door with the Lock and Key card. Locking it doesn't close it. | Lock state |
PLAYSCENE |
Plays a full-screen cinematic clip. | Scene |
REWINDSCENE |
Plays a cinematic clip backwards, like a rewind. | Scene (|speed=N) |
PLAYARCADE |
Launches a mini-game to play. | Arcade stage |
PLAYFX |
Plays a visual effect, like a flash or shake. | FX name, Params (key=val|…) |
ADJUST |
Applies a full-screen mood filter, like night or sepia. | Screen FX preset, Preset params |
TOGGLEROOMFX |
Switches one room effect type on or off in the current room: rain, fog, glitch… | Room FX type |
SETROOMFX |
Turns the dials of one room effect type at runtime: rain density, fog colour, lightning gaps… Merges on repeat and survives a save. | Room FX type, FX params |
UNLOCK |
Grants the player an achievement. | Achievement |
NEXTCHAPTER |
Moves the story on to the next chapter. | (none: bare token) |
NEXTBLOCK |
Moves the story on to the next part. | (none: bare token) |
GOTOBLOCK |
Jumps the story to any part you choose: back to the beginning, or on to a bonus screen. | Block |
RESETGAME |
Wipes the current playthrough so the next one starts clean. Achievements are kept. | (none: bare token) |
WAIT |
Holds the rest of the chain for a few seconds, so a line has time to be read. | Seconds |
PARTYCOMMIT |
Confirms your chosen team and moves on. | (none: bare token) |
WAITFOR |
Holds the rest of the chain until the line on screen has finished, with no seconds to guess. | Event |
QUITGAME |
Quits the game, the right way for each platform (closes the window or app). | (none: bare token) |
SHOWTEXT |
Shows free-floating text anywhere on screen. | Pos, Color, Size px, Font, Dur sec (0=fixed), Id (opt), Text ({flag}) |
CLEARTEXT |
Removes on-screen text you showed earlier. | Id (blank=all) |
Notes#
- Token IDs are literal and always English (
SETFLAG,GIVEITEM…). With the editor in Spanish, each token also shows a Spanish name next to its ID (Activar flag · SETFLAG), the same one the Spanish version of this page lists. That name is only a label: what gets saved in your project is still the ID, so a project opens the same with the editor in either language, and renaming an ID would break every saved rule. GIVEITEM:<item>|<character>is the only token that moves an item between playable characters.- A few tokens take no argument and serialize as a bare verb (e.g.
INVENTORY_UP). - Most arguments are required, and leaving one blank is an authoring bug the engine
can't warn you about:
SETFLAG:with no name sets the empty flag, which no condition can ever read back. The Puzzle Editor's Validate now catches exactly that and blocks Save. Some arguments are optional by design and may be left blank: aPICKUPwith no item grants the hotspot's own id,CLEARTEXTwith no id clears every overlay,SETVALwith no value clears the buffer, and the trailingspeed/options/ volume-pan parts of a token can all be omitted.
Gating a path, a trigger or a light with a puzzle#
Three token pairs (WALKABLE_ON/WALKABLE_OFF, REGION_ON/REGION_OFF and
LIGHTZONE_ON/LIGHTZONE_OFF) flip a room's walkable areas, walk-over regions and light
zones on and off at runtime, so a puzzle can open a path, arm a trigger, or switch on a lamp.
All three share the same shape: TOKEN:<id> targets that entity in the current room, and
TOKEN:<id>|<roomId> reaches into another room (a lever in one room opening a path, arming
a trigger, or switching a light somewhere else entirely).
WALKABLE_ON/WALKABLE_OFFenable or disable a walkable area (a floor a character can actually stand on), the way a secret passage stays closed until a switch is thrown.REGION_ON/REGION_OFFenable or disable a walk-over region: a trigger a puzzle needs armed or disarmed, so a trap doesn't fire before its cutscene, or an intro beat doesn't refire once it's already played.LIGHTZONE_ON/LIGHTZONE_OFFswitch a light zone on or off: a lamp, a spotlight, a tinted area. It's visual only: light zones are deliberately decoupled from walkable areas, so switching one off never moves the floor, and switching a walkable area off never dims a light.
All three are overrides, not one-shot toggles: the state is remembered per room, takes priority over whatever the room author set as the default, and is re-applied every time the player re-enters that room, so a path a puzzle opened, a trigger it armed, or a lamp it switched off stays exactly as the player left it across a room change, and survives save/load.
Read the state back with the matching predicate (isWalkableOn, isRegionOn and
isLightZoneOn) in a puzzle rule's condition; see the full predicate list on the
Puzzle Editor page.
Locking and unlocking a door#
SETHOTSPOTLOCK:<state> locks or unlocks a door that has the room editor's Lock and Key
card, with <state> being locked, unlocked or toggle. With no id it targets the hotspot the
reaction belongs to, so renaming the door never breaks it; SETHOTSPOTLOCK:<hotspot>|<state>
reaches another door in the same room. Like the tokens above, it is an override: remembered per
room, and it survives save/load.
Locking a door does not shut it, and unlocking does not open it: chain SETHOTSPOTOPEN beside
it when you want both. A door that locks itself again after a while is a timer plus
SETHOTSPOTOPEN:closed and SETHOTSPOTLOCK:locked. Read the state back with isLocked. The
key itself rarely needs the token: the card already unlocks the door when the player uses the
right item on it.
Turning the room's lights on and off#
TOGGLELIGHT flips the whole room between lit and dark: the light switch by the door,
the fuse the player pulls. It is the room-wide darkness veil, so it is a different thing from
LIGHTZONE_ON/LIGHTZONE_OFF, which turn one lamp's glow on or off inside a room.
How dark the room gets is the room's own Darkness when unlit setting. How fast it gets
there is Fade duration, the fader right beneath it. Set it to 0 and the light snaps
instantly, the way a real switch does. That is the setting to reach for first: you author it
once and every light switch in that room obeys it.
When one particular switch needs to behave differently (a lamp that fades slowly while every other switch in the room snaps), give the token a duration of its own:
TOGGLELIGHT:dur=0 instant, whatever the room says
TOGGLELIGHT:dur=1500 a slow one-and-a-half-second fade
TOGGLELIGHT no opinion: use the room's fader
Leave the field blank and the room decides, which is what you want almost every time.
Walking a character to a point#
Two tokens walk a character to a spot on the floor from any rule, reaction or watcher,
without opening a cutscene. WALKTO:<x>|<y> walks the active player;
NPCWALKTO:<character>|<x>|<y> walks a named character and leaves them standing there.
Both ride the same click-to-walk pathfinding the player uses, so the character routes around
obstacles and stops the same way a normal click would.
Both share the same optional tail, and its parts can appear in any order:
- facing: a direction (
up/down/left/right) applied once the character arrives; while walking, the character faces the way it's moving. - speed: either
speed=<pixels-per-second>ordur=<milliseconds>for the trip; it's a temporary override, and the character's normal speed is restored on arrival. - block: add
block(orblock=true) to freeze the game until the character arrives, the way a cutscene step waits. Without it the walk is fire-and-forget: the game stays interactive and a player click can re-target the active character mid-walk.
For example, WALKTO:120|340|left|block walks the player to (120, 340), holds input until they
get there, and turns them to face left on arrival.
Moving the camera in a cutscene#
By default the camera follows the player. Inside a cutscene, CAMERAPAN:<x>|<y> sends it
somewhere else (to the door opening on the far side of the room, to the NPC who just walked in)
and waits until it gets there before the next step runs. The camera then stays on that
point for the rest of the cutscene, so it keeps looking at the door while someone talks about
it. When the cutscene ends it goes back to following the player on its own; there's no token to
undo it.
The optional tail has the same shape as WALKTO's, and its parts can appear in any order:
- at:
at=<hotspot>aims at a hotspot instead of the coordinates, and wins over them.at=playerswings the camera back to where the player is standing before the cutscene ends; if the player walks off afterwards, the camera doesn't follow until the cutscene is over. In the editor both live in the Hotspot picker, next to 🎮 Player (active). - speed:
speed=<pixels-per-second>ordur=<milliseconds>for the trip; set both anddurwins. With neither, the camera travels at 800 pixels per second, so a short hop is quick and a long one takes longer. - easing: the curve of the move:
linear,inQuad,outQuad,inOutQuad(the default: it eases in and out),outBack,outElasticoroutBounce.
Two pans in a row chain smoothly: each one starts from wherever the camera is, not from the player. The camera never shows past the edges of the room (a point close to a wall stops where the room's camera limits do) and if the player hold-to-skips the cutscene, the camera lands straight on its destination, the same way a blocking walk teleports.
For example, CAMERAPAN:1500|400|dur=1200, a SAY line, then CAMERAPAN:0|0|at=player looks
across the room for a little over a second, lets the line play, and swings back to the player
before the cutscene carries on.
Where it works: only inside a cutscene. Add it as a token step in the Cutscene Editor. In a hotspot reaction, a dialogue choice or a puzzle rule it does nothing: put the pan in a cutscene and fire it with
CUTSCENE:<id>. A destination that can't be resolved (a hotspot that isn't in the room) is skipped instead of stalling the cutscene.
Pausing between tokens#
Every token in a chain fires in the same instant, which is usually what you want, but not when
one of them needs a moment to be seen. A spoken line is the clearest case: SAY puts the line
on screen and immediately hands over to the next token, so SAY followed by a token that changes
the scene means the line never gets read.
WAIT:<seconds> holds the rest of the chain, then carries on where it left off. Put one
between the line and the jump (SAY:…, WAIT:2, GOTOBLOCK:credits) and the line gets its
two seconds before the game moves on.
WAITFOR:say does the same job without you naming a number: it holds the chain until the
line currently on screen is gone, whoever is speaking (the player, an NPC, or the narrator).
Write SAY:…, WAITFOR:say, GOTOBLOCK:credits and the jump lands the moment the bubble does,
even if you later rewrite the line, or record a voice clip for it and let the subtitle stretch to
the audio. Reach for it whenever the number you'd type into WAIT is really just "however long
that line takes". With nothing being said it simply moves on.
The game holds still while it waits: input is locked, the same way it is during a cutscene, and the player can't skip it, so keep waits short and deliberate. Pausing still works, and a paused wait stops counting down until the player unpauses.
Where it works: anywhere you chain tokens in live play (hotspot and character reactions, item interactions, panel buttons, dialogue choices, room-entry events and walk-over regions). Scene timelines take neither of them, and the scene editor won't offer them: a scene is already a timeline, so you space its beats by moving them, not with a token. Cutscenes have their own Wait step that does the same job. Single waits are capped at 60 seconds, and the game refuses to save while one is running.
Taking the player off screen#
Sometimes the character has to leave without going anywhere: walking into a portal before the credits roll, stepping out of frame so a scene can play on an empty room, or simply not being in a shot they'd spoil.
HIDEPLAYER takes the player's sprite off screen, and SHOWPLAYER brings it back. Both
are bare tokens, no argument. The portal beat reads exactly the way you'd say it out loud:
WALKTO:588|816|up|block, HIDEPLAYER, WAIT:1, PLAYSCENE:credits.
Two things worth knowing before you reach for it:
- It only hides; it doesn't switch the character off. A hidden player still walks, still
collides, still takes clicks. If you want them to stop moving too, say so with the tokens that
already do that (
WALKTOwithblock, or a cutscene). The one thing that does follow the sprite is its shadow and its reflection in a puddle or mirror. Those go with it, and a spoken line stops drawing a bubble rather than dangling one off an invisible head. For a voice with nobody on screen, useNARRATE. - It's remembered. The hidden state goes into the save, so a player who quits mid-puzzle and comes back finds the room exactly as they left it. Starting a new game always brings the player back, so you can't strand anyone invisible.
Showing a panel (or the verb coin) from a cutscene#
A cutscene hides the HUD, but a panel the cutscene itself opens is meant to be seen:
OPENGUI:<gui> in a cutscene step paints and animates its modal over the room. It's a
showcase: the cutscene keeps the input, so a click still advances the line rather than
pressing a button. Follow it with a WAIT and then a close, and a guided tour can show the
inventory or a keypad instead of only talking about it.
CLOSEGUI takes an optional target. Bare, it closes whatever modal is open, what every
close button does. CLOSEGUI:<gui> closes it only when that one is actually on screen, so a
cutscene can tidy up its own panel without snatching away a menu the player opened meanwhile.
The pop-up verb coin can't be addressed by id (it's built fresh at each click, wherever you
clicked), so it has its own pair. OPENCOIN:<hotspot> pops the real coin centred on that
hotspot's artwork (not on the floor point you'd walk to) and CLOSECOIN dismisses it.
Inside a cutscene it's a showcase like any panel; outside one the coin stays armed, and picking a
verb runs the ordinary action on that hotspot.
Restyling a panel element on the fly#
MODIFYWIDGET:<gui>.<widget> overrides a single GUI widget's look at runtime: its
background colour and opacity, its border (width and colour), or its text, without touching the
authored GUI. Like the other panel tokens it's a draw-time overlay, so the GUI's own descriptor
and layout are never mutated; but unlike a one-off visual effect it's durable: the override
rides save/load, so a colour or text change can stand in for remembered state (a lever panel
that turns green once you've pulled it). The tail is order-free key=value pairs, every one
optional, and firing the token again merges into the same override rather than replacing it.
reset=true clears the override and restores the widget's authored look. The text override
takes priority over a label's other text sources and accepts either a literal or a #lid (a
string-table entry, so it still localizes).
The target isn't limited to your own panels. The game's main HUD bar and the pop-up verb coin
are regular GUIs underneath, so MODIFYWIDGET:mainBar.verb-look|bg=#a00 tints a verb button
and MODIFYWIDGET:verbCoin.coin-talk|text=Shout relabels a coin cell. The editors suggest
both under their real ids (mainBar.*, verbCoin.*), engine defaults included when the
project hasn't authored its own. The coin is rebuilt fresh at every click, but the override
re-applies each time it opens; like every widget override these are durable, ride save/load,
and reset=true returns the widget to the game's configured default look. One caveat: a
button drawn with sprite artwork ignores the background, border and text overrides; they
apply to text-label buttons (the classic nine-verb bar and the default coin are all text).
Once a panel has been dressed up by a handful of these, putting it back one reset=true per
widget gets tedious, so leave the widget off and the reset covers the whole panel:
MODIFYWIDGET:<gui>|reset=true drops every override belonging to that GUI at once. The same
thing is available at the moment a panel opens, as OPENGUI:<gui>|reset=true, for the common
"this screen should always come up clean" case. Both are opt-in on purpose: because widget
overrides are durable and can stand in for remembered state, a plain OPENGUI never clears
them; otherwise a panel that remembers something would forget it every time it was opened.
Restyling a hotspot's sprite on the fly#
SETHOTSPOTFX:<hotspot> is the same idea aimed at the room: it overrides a sprite
hotspot's visual FX (shadow, glow, blur, opacity, and silhouette stroke) on top of whatever
the room editor authored, without touching the room file. The tail is order-free key=value
pairs (glowsize=8|glowcolor=#f0a03c, blur=3, opacity=0.5, strokew=2|strokecolor=#fff),
every one optional; firing the token again merges into the same override, and it's
durable across save/load: an amulet that keeps its glow once activated, a portrait that
stays faded after the séance. reset=true restores the authored look.
The shadow keys pick between the two shadow styles from the room editor's Sprite FX panel:
shadow=basic with shdx/shdy/shblur/shcolor/shalpha for a simple drop shadow, or
shadow=cast with shangle/shlen/shsoft/shalpha for the real silhouette shadow that
characters cast. Leave the cast numbers out and it inherits the room's lighting, so the
hotspot's shadow points the same way everyone else's does. shadow=off kills the shadow
entirely, authored one included. Zero sizes switch an effect off (glowsize=0, strokew=0,
blur=0), matching the editor's "0 = off" knobs. Only hotspots with a sprite take FX:
there is nothing to outline or glow on a bare polygon.
Turning a room effect's dials from a rule#
SETROOMFX:<type> does for the room's ambient effects what SETHOTSPOTFX does for a
sprite: it overrides the dials of every effect of that type in the current room (rain, snow,
fog, clouds, starfield, glow, vignette, colour wash, lightning, bolt, spotlight, fire, water
jet, glitch, blur) on top of what the Room Effects panel
authored, without touching the room file. The tail is the same order-free key=value list, and
the keys are the panel's own knobs for that type (SETROOMFX:rain|count=400|speed=1.6|opacity=0.6,
SETROOMFX:fog|color=#5a6070|opacity=0.5, SETROOMFX:lightning|mingap=800|maxgap=2500),
clamped to the same ranges the panel allows; the editor builds the picker from the type you
choose, so there is no spelling to remember. Firing it again merges (a storm that gets
heavier in three steps is three tokens, each touching only what changes) and the override is
durable across save/load, so a downpour that broke at the end of Act 1 is still pouring after
a reload. reset=true drops the override and the room returns to its authored look.
Two things it deliberately does not do. It only turns dials that already exist: an effect the
room does not have gets nothing to override (add it in the panel and gate it with a condition or
TOGGLEROOMFX), and geometry, seeds and image assets stay the panel's business. And it sets
instantly: a slow build is a WAIT between tokens, the same way every other chain paces itself.
Animating a hotspot, not just a character#
The Animations group holds the three tokens that drive playback, and all of them are
polymorphic: the same verb aims at a character or at a hotspot's object animation, so
there is one STOPANIM to remember rather than one per kind of thing.
STARTANIM:<target> resumes whatever the target was playing and STOPANIM:<target>
freezes it in place: no frame jump, which is the ergonomic SETANIMFRAME doesn't have. Aimed
at a character, STARTANIM also takes an optional clip and facing (STARTANIM:bernard|talk|left)
to hold a pose. Aimed at a hotspot, the optional clip swaps which animation the prop plays:
STARTANIM:grandfatherclock|clock_chime moves the clock from its tick loop to its chime, and
a later STARTANIM:grandfatherclock with no clip puts it back on whatever the room authored.
Facing is character-only: a prop has no facing, and the editor hides the field once you pick
a hotspot.
The clip picker follows your target: choose a character and it offers that character's clips
by their short name (talk, blink); choose a hotspot and it offers the project's object and
room animations by their full name (grandfatherclock_ticktock). Name a clip that doesn't
exist and the prop falls back to its authored animation rather than freezing: a frozen prop
looks exactly like a token that did nothing.
The swap is runtime-only: it is forgotten when the player leaves the room, and it isn't saved. The room file stays the source of truth for what a hotspot plays by default.
Pulsing, spinning and fading a prop without the animations editor#
A baked FX clip usually already loops cleanly from frame 0. Making it also grow as it opens, pulse while it waits, or fade in and out used to mean baking more clips, but none of that is animation at all: a ramp is a curve.
SETHOTSPOTMOTION:<hotspot>|key=value|… animates three channels of a hotspot's sprite at
runtime: scale (sc* keys), rotation (rot*, degrees about the sprite centre) and
opacity (op*, 0-1), each with its own ramp, easing and loop mode. The tail is
order-free and every key is optional; a token replaces only the channels it names, so a scale
pulse keeps running while a later token retunes the opacity.
SETHOTSPOTMOTION:chest|scfrom=1|scto=1.15|scdur=400|scease=inOutQuad|scloop=pingpong
SETHOTSPOTMOTION:fan|rotspeed=180
SETHOTSPOTMOTION:ghost|opfrom=0.4|opto=1|opdur=900|oploop=pingpong
Each channel takes the same key family, prefixed sc / rot / op:
Xfrom=/Xto=: the ramp's endpoints. LeaveXfromblank and the ramp picks up from wherever the channel already is, so two tokens in a row chain smoothly; type it and the sprite snaps there first: the pop-in shape (scfrom=0.2|scto=1).Xfromalone with noXtois an instant set.Xloop=loop|pingpong: leave it out for a one-shot that runs once and freezes at the target.loopcuts hard back to the start each cycle;pingpongplays the ramp forward and then mirrored backward, which is what makes a pulse decelerate at both extremes; useinOutQuadfor pulses.Xdur=<ms>,Xspeed=<units/s>, and (scale only)scframes=<n>: how long a leg takes.framescounts real per-frame timing of the clip the hotspot is playing at that moment, so re-timing the animation keeps the ramp in step.durwins overframeswins overspeed.Xease=<curve>: the same curve listMOVEHOTSPOTuses.outBackovershoots slightly before settling, which is what makes a portal read as opening rather than inflating.Xstop=true: kill that channel, freezing its value in place (exactly what a finished one-shot leaves behind). To ease back to rest, send a one-shot instead:scto=1|scdur=300.origin=bottom: keep the sprite's foot planted while it scales. The default scales about the anchor, which is right for anything airborne and wrong for anything standing on the floor, which would appear to sink and then rise. If the hotspot has a Sprite pivot set in the Room Editor, the pivot wins: the scale anchors there andorigin=bottomadds nothing.reset=true: all three channels off and back to rest (scale 1, rotation 0, opacity 1).
Two of the channels have a special trick. rotspeed= with no rotto= spins forever at
that rate in degrees per second, the way to make a fan or a windmill turn in place. And the
opacity channel multiplies the durable SETHOTSPOTFX opacity rather than replacing it: FX
opacity is the prop's base look ("this ghost is 50% translucent"), motion opacity is an animated
modulation on top, and when the channel resets the base look returns untouched.
A scale of 1 is the sprite's authored size and 0 is legal: it means "not there yet", which
is the natural starting point of a pop-in.
Two things worth knowing. The scale is uniform: for a permanent non-uniform flatten there is
the room editor's Sprite distort panel, which is authored rather than animated. And the hotspot's
polygon does not scale: a portal drawn at 20% is still clickable across its whole authored
polygon, the same generosity that already applies to a rotated sprite. Like MOVEHOTSPOT, the
motion is runtime-only: it is forgotten on room change and is not saved. For a pulse that should
survive re-entry, re-fire the token from a room-enter rule, or give the hotspot a Motion
instead (next section), which plays on its own and is saved.
Starting and stopping a hotspot's Motion#
A Motion (the Room Editor's Motion card) is a keyframed loop a hotspot plays on its own from the moment the player enters the room: a spotlight sweeping, a sign breathing, a balloon floating. Two tokens drive it from a rule.
PLAYMOTION:<hotspot> starts it. Name a motion to play a different one
(PLAYMOTION:spotlight|spotlight_fast), and add phase=<ms>, speed=<x> or
mirror=true|false to override what the card says; whatever you leave out comes from the card.
It never makes the hotspot jump: if the motion is the one already on the hotspot, it carries on
from where it is, so a frozen spotlight resumes mid-sweep. A different motion starts from its
beginning, and so does a Once motion that has already finished: a bell rings again on every
click.
STOPMOTION:<hotspot> freezes it in its current pose, and a later PLAYMOTION resumes it.
STOPMOTION:<hotspot>|reset=true sends it back to rest instead (the sprite as you authored
it) and keeps it stopped, even if the card says Starts on its own.
PLAYMOTION:spotlight_left
PLAYMOTION:sign|speed=2
STOPMOTION:spotlight_left
STOPMOTION:sign|reset=true
Unlike SETHOTSPOTMOTION, a Motion is saved: where it is in its cycle and whether it's
playing or stopped. Leave the room and come back, or load a save, and the spotlight is where it
left off. Both tokens act on the hotspots of the room the player is in.
The two can share a hotspot. A SETHOTSPOTMOTION channel (rotation, say) wins over the Motion's
lane of the same name until SETHOTSPOTMOTION:<hotspot>|reset=true hands it back or the player
leaves the room; a MOVEHOTSPOT with spin= does the same for rotation. The Motion's offset
lanes add to MOVEHOTSPOT instead, so a floating prop keeps bobbing while it slides.
Taking over the cursor#
SETCURSOR overrides the mouse cursor itself: which cursor (and with it, which
animation) is drawn, which frame it's frozen on, where it sits, and whether it shows at all.
The tail is order-free key=value pairs, every one optional; firing the token again
merges into the same override, and reset=true restores normal cursor behaviour. Unlike
its two sibling overrides above, this one is deliberately not saved: a forced cursor is
presentation, not puzzle state, and it clears itself the moment a cutscene ends or is
skipped.
cursor=<id>forces a cursor from the project's cursor set; it beats the armed inventory item, the selected verb and any hover override.frame=<n>freezes an animated cursor on one frame; leave it out and the animation plays normally.x=<px>|y=<px>pins the cursor to a canvas position (send both); without them it keeps following the real mouse.visible=showkeeps the cursor drawn during a cutscene, where it's normally hidden. Input stays locked (the player can't click anything), so the cursor is purely yours to drive from the cutscene's own tokens: a tutorial "ghost hand" showing where to click, an eye tracking the scene.visible=hideis the mirror image: it hides the sprite cursor outside cutscenes.
A cutscene beat: SETCURSOR:cursor=eye|visible=show|x=640|y=360 shows the eye cursor pinned
mid-screen while the scene plays; the moment the cutscene ends, everything snaps back to
normal on its own.
Changing the whole screen's look#
ADJUST:<preset> drops a filter over the finished picture: room, characters, panels and
cursor together. It is how you play a flashback in black and white, a poisoning in sickly
green, or a memory as a soft dream. ADJUST:none clears it. The look is saved, so a
chapter that starts in black and white is still black and white after the player quits and
comes back.
Every preset can be dosed with an order-free key=value tail, and the editor shows the dials
for whichever preset you picked, each one sitting at that preset's own value. Leave a dial
alone and that preset looks exactly as it always did; move one and only that part changes.
The tonal presets (everything but pixelate) share one set of dials:
brightness,contrast,saturate: the tone,1being untouched.nightstarts dark and slightly washed out;ADJUST:night|brightness=0.8is the same night, less deep.hue=<degrees>andblur=<px>: spin the colours or soften the whole picture.tint=<colour>,alpha=<0..1>,blend=multiply|screen|overlay|normal: the coloured wash on top: which colour, how strong, how it mixes.warm,night,poisonanddreamcome with one; the others take one the moment you name a tint.scan=<0..1>andgap=<px>: the scanlines and how far apart they run.crtstarts with them on; any preset can borrow them.vignette=<0..1>: the darkened edgesdreamuses.strength=<0..1>: how much of the whole look mixes over the untouched picture.0is a clean way to switch it off for one beat without losing the rest.
Grey, sepia and negative are the identity of grayscale, sepia and invert: they have no
dial, because a sepia that is not sepia is just another preset. Switching preset in the editor
resets the tail: night's wash means nothing to crt.
pixelate has its own set of dials:
block=<px>: how big each square is. The main dial.levels=<n>: how many shades per colour channel survive. Cutting this is what makes it read as pixel art instead of a blurry photo with squares.strength=<0..1>: how much of the effect mixes over the untouched picture.0is a clean way to switch the look off for one beat without losing the settings around it.dither=none|b4|b8: scatters the colour steps so flat areas break into a pattern. It needs the levels cut down to do anything.down=area|nearest: area averages for a softer result, nearest keeps edges hard.scope=world|all: how far the blocks reach. world stops at the edge of the room and leaves the interface sharp (verb bar, dialogs, speech bubbles, on-screen text, cursor). all takes the panels and their lettering too, which is what you want when the unreadable interface is the effect. Unlike the tonal presets above, which always cover the whole screen, pixelate is the one that can be told where to stop.
Every one is optional, and whatever you leave out keeps whatever the project already uses. So
if the game is already pixelated in Hub Config, ADJUST:pixelate|block=12 pushes the blocks
up for a moment and leaves the palette alone; ADJUST:none puts it back to the project's own
look rather than switching pixelation off. The tail is saved with the look, so a dosed night
survives a quit and reload the same way the plain one does.
A bigger block is cheaper here too: the effect works on a smaller copy of the picture, so pushing it further asks for less, not more.
Working with values & counters#
SETVAL, APPENDVAL and ADDVAL build up a value in a flag (a combination lock, a
dialled number, a counter) that you then check with the valueEquals or compare
condition.
SETVAL:<flag>|<value>writes an exact value, replacing whatever was there. Leave the value blank (SETVAL:code|) to wipe the flag clean: that's the Clear button on a keypad.APPENDVAL:<flag>|<text>|<max>adds text to the end of a flag, a piece at a time: it's what each digit button on a keypad fires. The optionalmaxcaps how long the value can grow, so a four-digit lock stays four digits no matter how many times the player mashes a key: extra presses simply do nothing. Leavemaxout and the value grows without limit. A text box the player types into has its own max length setting;maxis the same idea for entry driven by buttons, which never passes through the typing path.ADDVAL:<flag>|<n>adds a number to a counter: the universal "+1".nmay be negative to count down (ADDVAL:tries|-1). The result is stored as text, socompareandvalueEqualskeep reading it; an absent or non-numeric flag counts as0.RANDVAL:<flag>|<min>|<max>writes a random whole number betweenminandmax(inclusive) into a flag; then branch on it withcompare/valueEquals(e.g. "say one of three lines", a dice roll, a 30% chance). Randomness is intentionally an effect, never a condition: it's rolled once when the step fires, so a watcher checking the flag stays stable. The stream is seeded and saved, so a given playthrough is reproducible and a save/load doesn't change an already-rolled result.
Fine-tuning audio tokens: volume, pan and crossfade looping#
MUSIC, AMBIENCE, SOUND and VOICE accept an optional tail of key=value pairs after
their main argument, so you can tune a single cue without touching the mixer. The editors
show these as mini-knobs, but they're plain token text underneath.
|vol=<0-100>|pan=<-100..100>sets that cue's volume and stereo position. Volume multiplies on top of the player's own volume settings (aVOICEline's volume also multiplies the player's voice-volume slider); pan sweeps left/right, with0centered. Leave either one out and it keeps the default (full volume, centered): a plainSOUND:door_creakstill works exactly as before.|xfade=<s>|xfadeat=<s>: (MUSICandAMBIENCEonly) turns a track's loop into a smooth crossfade instead of jumping straight back to the start.xfadeis how many seconds the outgoing and incoming copies overlap;xfadeatis the point where that crossfade begins, which also sets how often the track loops (a 60-second track withxfadeat=52loops every 52 seconds). Reach for this on music or ambience whose ending doesn't already loop cleanly.
A fully tagged example: MUSIC:theme|vol=80|pan=0|xfade=1.5|xfadeat=52.
Making a sound effect loop, and stopping it#
SOUND is a one-shot: it plays once and reaps itself. To make a particular sample loop, you
don't change the token; you mark the sample in the Audio Editor,
which records loop points and a loop mode in projects/<id>/audio.json. Every SOUND that plays
that sample then loops, and STOPSOUND:<id> is what ends it. STOPSOUND stops every live
copy of that id, one-shots included, so it also works as a "cut that noise now" for a long sample
that hasn't finished.
Two things worth knowing before you lean on it: a looping SOUND started by a rule does not
survive a save/load (room ambience does, because the room re-triggers it on entry), and looping
background sound that belongs to a place is usually better expressed as AMBIENCE, which is
keyed, layered and restored automatically.
Text interpolation in spoken / on-screen text#
SAY, NPCSAY, NARRATE, SETSENTENCE and SHOWTEXT substitute {flagName} tokens in their text
from the current flag values at display time (e.g. SAY:Tienes {coins} monedas shows
Tienes 5 monedas). A missing flag becomes empty text (never an error); only {...}
names made of letters, digits and _ are touched, so stray braces and $9.99-style
amounts are left alone. Combine it with ADDVAL/RANDVAL to narrate live counters and
rolls.
Some tokens write a name into a flag for exactly this purpose. CATCHTARGET picks one
of several candidate characters (by priority or random) and stores the chosen id in an
out-flag (default caughtChar), which you then consume by interpolation, e.g.
SWITCHCHAR:{caughtChar} or FOLLOW:monster|{caughtChar} in the same cutscene.