Skip to content

Conditions and events ​

Conditions ​

Conditions are strings, used by requires, exits (if, denials), room descriptions, onEnter, rules (instead, after, onUse), dialogue keys, hints, scoring, daemons, the finale’s epilogue and ambient lines. One parser handles all of them.

ConditionTrue when
flag:NAMEthe flag is set
has:ITEMthe item is in the inventory
held:ITEMthe item is carried at any depth: directly, or inside something carried (Zork’s HELD?). has: means carried directly
in:ROOMthe player is in that room
visited:ROOMthe player has been there
inside:ITEM:PLACEthe item is directly in PLACE: a room, another item, or player
open:ITEMthe container or door is open
locked:ITEMit’s locked
on:ITEMit’s switched on
here:ITEMthe player can reach it (in the room, carried, or in something open)
var:NAME<op>Na numeric variable compared with =, <, >, <= or >= (unset is 0)
carrying<op>Nhow many things the player holds directly
target:ID, indirect:IDthat slot of the command being run resolved to that ID (player for ME and MYSELF, and SELF or YOURSELF when nothing here is called that); false otherwise
direction:DIRthe direction typed in PUSH X NORTH (compass, up, down), or, in an orders.go rule, the place the character was told to go, as typed: “milton, go to the basement” is direction:basement. Any lowercase word passes the audit
number:N, number<op>Nthe number typed in the command being run (TURN DIAL TO 4, SET DIAL TO 776); false when the command has none. A rule with with: 'number' matches that command
said:WORDSthe words typed after a text verb (target: 'text', e.g. ANSWER A WELL), compared as lowercase whole words with punctuation and quotes ignored; false when the command has none
heaviest<op>Nthe weight of the heaviest thing the player holds directly, counting what’s inside it (Zork’s narrow passage: heaviest<=4)
score<op>Nthe score, as SCORE reports it (Zork wins at score>=350); a scoring entry can’t test it
lit:here, lit:ROOMthe room has light
following:NPCthe character is following the player ({ follow } set it; a character’s follows condition is separate)
alive:NPCthe character isn’t dead
awake:NPCalive and conscious
fighting:NPCin a fight with the player (and conscious)
with:NPCin the player’s room (hidden or not)
seen:NPCin the player’s room and not hidden
aboard, aboard:ITEMthe player is in a vehicle (that one)
water:here, water:ROOMthe room is water now
terrain:NAME, terrain:NAME:ROOMthe room (default: the player’s) is that terrain now: land, water, air, or one of the world’s own
!…negates any of the above
a & bevery part holds: in:break_room & !flag:lunch_freed

There’s no “or”. Write two rules, or two dialogue keys, instead. An unrecognized condition is false in play. auditWorld reports it, and a condition naming an item or room that doesn’t exist, in rules, triggers, exits, requires, daemons, hints and scoring (it doesn’t check dialogue keys, ambient lines or room descriptions).

Events and effects ​

An event is a named list of steps, run in order. A string is printed. An object is an effect:

EffectDoes
{ say: 'text' }Prints, like a plain string.
{ set: 'flag' }, { clear: 'flag' }Turns a flag on or off.
{ move: 'item', to: 'room' }Moves an item to a room, 'player', 'here' (the player’s room), another item, a character, or null (offstage).
{ moveNpc: 'npc', to: 'room' }Moves a character, or null (gone).
{ follow: 'npc' }, { unfollow: 'npc' }Sets or clears the character’s following state (npcs.<id>.following in the game state; no flag): the character then goes where the player goes (GO, doors, ENTER, CLIMB; not scripted moves), like a follows condition on the character.
{ npcState: 'npc', fighting?, staggered?, strength?, hidden?, scenery? }Sets a character’s combat state, hides and reveals it, or (scenery) keeps it out of the room’s list and puts it back (Zork’s NDESCBIT in play).
{ script: 'name', arg? }Runs one of the world’s scripts and the steps it returns.
{ hide: 'item' }, { reveal: 'item' }Hides an item where it is (not seen, listed or taken), or shows it again.
{ board: 'item' }, { disembark: true }Puts the player in a vehicle that’s in the room, or takes them out.
{ touch: 'item' }Marks an item handled (Zork’s TOUCHBIT): its first-seen sentence is over.
{ unlist: 'item' }, { relist: 'item' }Keeps an item where it is, seen and usable but out of the room’s list, or lists it again (the tied rope).
{ open }, { close }, { lock }, { unlock }Changes a container’s or door’s state.
{ switch: 'item', on: true }Switches a light (or anything switchable).
{ add: 'var', by: n }, { setVar: 'var', to: n }Changes a numeric variable.
{ setVar: 'var', from: 'number' }Sets it to the number typed in the command (0 if none).
{ score: n }Adds to the score.
{ go: 'room' }Moves the player there and describes it. With quiet: true, without describing it (Zork’s mirror).
{ look: true }Describes the player’s room in full, as LOOK does.
{ unvisit: 'room' }Forgets the player has been there, so the next arrival shows the full description (Zork clears TOUCHBIT when the dam drains).
{ schedule: 'event', in: n }, { cancel: 'event' }Runs an event after n turns, or stops it.
{ chance: 80, then: [...], else: [...] }Picks a branch at random (from a seeded generator, so saves replay exactly).
{ if: 'condition', then: [...], else: [...] }Picks a branch by a condition. else is optional.
{ stopLine: true }, { stopLine: 'text' }Drops the rest of the command line (Zork’s P-CONT). The text, if any, is said only if commands were left.
{ moveVehicle: 'item', to: 'room' }Moves a vehicle to a room. If the player is aboard they arrive with it, as on any arrival; otherwise its leave and arrive lines are said when the player sees it go or come. It isn’t a move the player makes, so followers stay behind.
{ free: true }The turn takes no time: no move, no timers or daemons (a capture’s echo).
{ noDarkLine: true }A line already said the light went out, so the engine doesn’t add its own.
{ run: 'event' }Runs another event here.
{ die: 'cause' }Kills the player. See Death. Nothing after it runs.
{ end: 'ending' }Plays an ending. See Endings. Nothing after it runs.

Effects print nothing unless they say so. An effect naming something that doesn’t exist does nothing in play, and auditWorld reports it. It also checks the events named by rules, onEnter, onTake, onWear, onSmash, onGive, daemons and the finale.

Templates ​

{var:NAME} (the variable, 0 when unset) and {number} (the number typed in the command; left as written when there is none) fill in when a line is printed: event lines, descriptions, and an item’s initialDescription and roomDescription wherever the engine lists it (in the room, in a container or on a surface, or when opening a container shows it):

ts
vars: { tally: 3 },
items: {
  tally: { name: 'tally board', description: 'Chalk marks.', portable: false, tags: [], roomDescription: 'A tally board on the wall reads {var:tally}.' },
},
> look
📍 Hall
A hall. The study is north.
A tally board on the wall reads 3.

Bracket lines (the older form) ​

Some lines are printed and change the game. They still work, and in an Infocom-style world they act without being shown:

LineEffect
[Flag set: Gary is happy]Sets the flag flagLabels['gary is happy'] names. An unmapped label does nothing, so test for it.
[Added to inventory: badge]Gives the player the item whose name is “badge”.
[Badge consumed]Removes the item whose name is “badge” from the inventory.

Facts worth knowing:

  • Events from onEnter, onTake, onWear, onSmash and the finale’s bareHanded fire once per game. Events from a use rule’s then and from onGive run every time the rule matches, so guard them with a !flag: condition if they should happen once.
  • Bracket lines only turn flags on. To turn one off, use the { clear: 'flag' } effect.
  • Item lines match display names, not IDs. To upgrade an item, consume the old one and add the new: [Lamp consumed], then [Added to inventory: lit lamp].
  • intro plays when a new game starts. A chapter break is just a line, such as ✨ CHAPTER 2: THE AFTERNOON.

How lines are styled ​

Every line the player sees is classified by how it starts, which sets its look and typewriter speed:

Starts withStyleUse it for
📍room headeradded by the engine; don’t start your own lines with it
💼 🌀 👔 💕 💻 💾 🔨 💥 📎 ✨ 🪄 🤜 😖 😴 🐟 🌺 📬 📞scripted momentevents (18ms/char)
═ or “banner, narration12ms/char
[systemflags, inventory, hints, score (instant)
anything elseprosedescriptions, replies (10ms/char)

A line starting with an emoji that isn’t in the list renders as prose. The set is fixed in this version, so pick from the list for scripted moments.

Copy style: the built-in replies use curly quotes and apostrophes (“ ” ’). Your world will read best if it does too.

Brass Lantern v2.1.1 · Released under the MIT License. · Why a brass lantern?