Core mechanics

The engine has four key building blocks, and they make up most of what a Stage game does. Learn just these and you'll already be most of the way there.

Triggers

Triggers are things that happen, such as rendering a response to the player, adjusting a measure, adding an object to the inventory and more. Every name is the thing it acts on, then what it does to it, so the triggers that belong together sit together. The one exception is response, which is just the game talking.

scene:change { scene }

Moves the player.

- type: scene:change
  data: { scene: cellar }

inventory:add { object, announce? }

Gives the player an object.

- type: inventory:add
  data: { object: brass-key, announce: true }

inventory:remove { object }

Takes it from their hands, leaving it in the room.

- type: inventory:remove
  data: { object: brass-key }

object:move { object, to, announce? }

Puts an object in a scene, inventory, offstage, bin, held: somebody, or here. With object: player it takes the player to a room instead, which is handy when a character shoves them somewhere.

- type: object:move
  data: { object: lantern, to: offstage }

object:remove { object }

Throws an object away, into the bin.

- type: object:remove
  data: { object: letter }

object:move-tagged { tag, from, to, announce? }

Moves everything carrying a tag from one place to another in one firing.

- type: object:move-tagged
  data: { tag: wet, from: cellar, to: bin }

flag:set { flag, value? }

Sets a flag, defaulting to true.

- type: flag:set
  data: { flag: door-unlocked }

response { text }

Says something to the player.

- type: response
  data: { text: "The door creaks open." }

game:end { journal? }

Ends the game, and says what it's remembered as.

- type: game:end
  data: { journal: "Escaped the house before dawn." }

game:save { name? }

Keeps the playthrough, once the turn is over.

- type: game:save
  data: { name: before-the-cellar }

measure:adjust { subject, measure, by? | to? }

Moves a measure, or sets it outright.

- type: measure:adjust
  data: { subject: player, measure: health, by: -1 }

achievement:unlock { id }

Unlocks a credit that outlives the playthrough.

- type: achievement:unlock
  data: { id: first-escape }

journal:log

Writes down that the player tried, even where whatever gated it turned them back.

- type: journal:log

character:obey

Only in an ordered action: the moment the character does what they were told, the way the player would. Triggers before it happen first, triggers after it afterwards. Once per list.

- type: character:obey

turn:pass { count }

Makes what the player did take that many turns, so everything that happens each turn happens that many times.

- type: turn:pass
  data: { count: 3 }

sound:play { id }

Plays one of the game's sounds, once, or until it's stopped if it loops. Games written before it was renamed call it audio:play, which still works.

- type: sound:play
  data: { id: rain }

sound:stop { id }

Stops a looping sound. Its older name, audio:stop, still works too.

- type: sound:stop
  data: { id: rain }

image:show { id }

Shows one of the game's images, until something hides it.

- type: image:show
  data: { id: cellar }

image:hide { id }

Hides a image.

- type: image:hide
  data: { id: cellar }

Conditions

A condition is a way to gate against a trigger happening, either game state or chance. The conditions field is a list of them, and every one must match before the subsequent Triggers will fire. The first to fail is the one whose failure path is activated.

Conditions are named the same way as triggers: the thing being asked about, then the question. chance, any-of and all-of keep their short names, because they don't ask about anything in particular.

inventory:has { object }

Matches when the object is in the inventory.

- type: inventory:has
  data: { object: brass-key }

object:here { object }

Matches when the object is carried or in the current scene.

- type: object:here
  data: { object: lantern }

character:here { character }

Matches when the character is in the current scene and can be seen there.

- type: character:here
  data: { character: groundskeeper }

character:obeying { character }

Matches when that character is doing this because they were told to. character: player matches whenever nobody else is carrying out an order, which is every ordinary turn. Handy for a button only the robot can reach.

- type: character:obeying
  data: { character: robot }

object:in { object, location }

Matches when the object is in location: a scene id, inventory, offstage, bin, held: somebody (held:player for the player), or in: something.

- type: object:in
  data: { object: lantern, location: cellar }

object:handled { object }

Matches when the player has had the object in their hands at some point, carried now or not.

- type: object:handled
  data: { object: letter }

flag:is { flag, value? }

Matches when the flag equals value, or is truthy when value is omitted.

- type: flag:is
  data: { flag: door-unlocked }

scene:visited { scene?, times? }

Matches when the scene has been entered at least times (default 1).

- type: scene:visited
  data: { scene: cellar, times: 2 }

scene:is { id? } or { tag? }

Matches when the player is standing in that room, or in any room declaring that word.

- type: scene:is
  data: { tag: outdoors }

action:performed { object?, action?, preposition?, indirect?, scene?, times? }

Matches when a matching action has fired at least times (default 1).

- type: action:performed
  data: { action: open, object: door }

measure:within { subject, measure, min?, max? }

Matches when the measure stands within the bounds given, each inclusive.

- type: measure:within
  data: { subject: player, measure: health, max: 2 }

inventory:within { of?, min?, max? }

Matches when what the player is holding stands within the bounds given.

- type: inventory:within
  data: { max: 3 }

object:tagged { tag, location }

Matches when something carrying the tag is at location - the same places object:in reads.

- type: object:tagged
  data: { tag: wet, location: cellar }

chance { name, of, index, no-repeat? }

Is luck with you? Each pass uses name, of and index, which can be either a number, an array of numbers or a spread ([1..5], one through five). If you want the condition to only fire once if there's a match, use no-repeat: true.

- type: chance
  data: { name: thunder, of: 6, index: 1 }

any-of { conditions }

Matches when at least one of the conditions matches.

- type: any-of
  data:
    conditions:
      - type: flag:is
        data: { flag: door-unlocked }
      - type: inventory:has
        data: { object: brass-key }

all-of { conditions }

Matches when every one of the conditions matches.

- type: all-of
  data:
    conditions:
      - type: flag:is
        data: { flag: door-unlocked }
      - type: inventory:has
        data: { object: brass-key }

Measures

A measure is a number that belongs to the player or to a thing and can move during play - health, how full a lamp's oil is, whatever your game needs to keep count of. It's bounded rather than open-ended: give it a min and a max, and a start to begin a playthrough at, and Stage keeps it inside those bounds for you.

id string, required

What the measure is called.

min number

The lowest it can fall to.

max number

The highest it can rise to.

start number

What it begins a playthrough at.

thresholds Threshold[]

Triggers that fire the moment it crosses into a new range.

Sound

A game can carry its own sounds: a creak when a door opens, rain while the player's outside. You list them once, in your game's sounds file, [id].game.sounds.yml, and play them by id with a sound:play trigger wherever something happens. Stage packs each file into the game, so it plays wherever the game's played.

- id: creak
  file: sounds/creak.wav

- id: rain
  file: sounds/rain.ogg
  loop: true

The file is the list of sounds itself, and each one takes these fields:

id string, required

What triggers call the sound.

file string, required

Where the file is, from the top of your game's folder: a WAV, Ogg or MP3.

loop boolean

Keeps it playing until a sound:stop ends it, and through a save and a load.

Images

A game can carry images too: a room as the player walks in, a face when somebody speaks. You list them once, in your game's images file, [id].game.images.yml, and show one by id with an image:show trigger. Every image needs an alt, which says what it shows for a player who can't see it. For an image that's only decoration, write alt: '', empty quotes, rather than leaving it out.

- id: garden
  file: images/garden.png
  alt: A walled garden, four beds of flowers either side of a pale path.

The file is the list of images itself, and each one takes these fields:

id string, required

What triggers call the image.

file string, required

Where the file is, from the top of your game's folder: a PNG, JPEG or WebP.

alt string, required

What the image shows, in words, or '' for decoration.

Actions

An action is a thing that can be done to an object, a character, a room, the player or the game itself - gates and then a list. It's usually the player doing it. conditions and once are the gates, each saying why it refused; triggers is what happens when every gate has let the player through, prose included.

id string, required

The id of the verb this answers to.

preposition string

The significant preposition this answers to, such as the under in "look under"; omit for the plain form.

indirect string

The id of the second object or topic required; omit to accept any.

conditions Condition[]

What must be true first - see Conditions

once block

Present to fire only the first time; its failure is what later attempts get.

triggers Trigger[]

Everything that happens when it works - see Triggers