Gruescript
Gruescript
Robin Johnson
In the last decade or so, hobbyist and indie interactive fiction has seen an
explosion in form*, with systems like Twine, ChoiceScript and Inkle
making web and mobile IF more accessible and democratised for players
and authors alike. Most games made in these systems are story-centric
rather than puzzle-centric: that’s what web and mobile interfaces are best
for delivering, and it’s what the development systems are best for creating.
The development systems, the interfaces available, and the feel of the
games produced are intricately related.
* I want to make one thing extremely clear: this is awesome. All kinds of IF, from
all kinds of creator, are valid, are welcome, and enrich the genre.
1
For parser IF, the barrier to that sort of immediate accessibility is the
interface. I don’t just mean that mobile devices don’t have keyboards. The
golden age of parser games happened when the standard way of
interacting with a computer was by typing at a prompt. Command
prompts were intuitive to players at the time, just as links and buttons are
intuitive now.
So, how do we revive the puzzlebox feel for mobile-friendly games? If all
the player’s possible choices must be visible on the screen all the time,
how can we pose a puzzle without immediately giving the solution away?
I first tried to solve this problem in 2016, with a game called Draculaland.
Using JavaScript running in a simple, client-side web page, I created a
world model of interconnected rooms containing objects that can be
arbitrarily picked up and moved around – the basic structure that’s taken
for granted in parser games, but which choice systems don’t tend to
bother with. (Some authors, like Astrid Dalmady and Josh Labelle, have
implemented this sort of world model impressively in Twine games, but
Twine didn’t make it easy for them.) I added buttons for movement
between the rooms, and gave each object an associated list of ‘active
verbs’ that change depending on the game state. For example, any portable
item has the ‘take’ verb active when it’s lying on the floor, or ‘drop’ if it’s
being carried by the player. Special actions can then be made possible at
certain points in the game – for example, if the player is standing in the
graveyard while carrying the shovel, the verb ‘dig’ activates. This is a
simple example, but it’s essentially a parser-game puzzle: the player has to
make the mental connection between the shovel and the grave, then
navigate back to the right spot, through a generalised interface that
doesn’t give the secrets of the game away, to try out their idea.
The second game I made with that JavaScript engine, Detectiveland, won
that year’s Interactive Fiction Competition. It was the first non-parser
game to do so. I’ve made several games with the same engine since, and
had enquiries from authors asking whether they could use it to write their
own games. Usually I told them I planned to package it up neatly one day,
but for now they were free to grab the source and modify it. Nobody did. I
2
could have published the engine as a sort of library, which people would
use to write their own games in JavaScript – but judging by various
attempts to make parser game libraries in JavaScript and other general-
purpose languages over the years, those don’t really take off: if an author
is willing to use a general-purpose language, they’re probably the sort of
author who’ll just write their own engine.
But the game data itself didn’t have to be JavaScript. The components of
the games themselves were defined as relatively simple objects: ‘rooms’,
‘things’, ‘verbs’ and ‘rules’ – which could easily be imported from a text
file using a simple(ish) script syntax. I modelled the syntax on the
assembler-type language used in the Adventure International games,
available to modern developers as ‘ScottKit’, because the JavaScript games
were very similar in structure – the only extra information needed was a
way to tell the game which verbs to activate, for which things, under what
circumstances.
3
Blocks
The basic units of Gruescript code are blocks of one or more lines. The
first line of each block begins with one of the nine ‘block type’ keywords –
that’s how Gruescript knows where a new block starts. The block types
are:
4
verb A procedure that is run whenever the player performs
a particular verb. This can be declared for a particular
verb being applied to a particular thing (specific
verbs), or for a general definition of a verb (general
verbs). There can be several verb blocks for the same
verb, or the same pair of verb and thing.
5
The game block
Gruescript code should contain exactly one game block, normally at the
very beginning of the Gruescript file. The block must contain at least the
game title, and usually contains further metadata and any instructions
(usually say commands) to be run at the beginning of the game. Here’s an
example:
game Spaceships Versus Kitten
id SVK
author Brian Snodbury
version 1.0.0
person 2
examine on
wait on
say You were minding your own business and
playing with a ball of wool one day when an
alien spaceship crashlanded right next to your
cat tree!
say
# Released under the Foobar Licence
<http://foobar.madeupurl/licence-terms>
Let’s go through each of these lines in turn.
game Spaceships vs Kittens
The title of the game.
id SVK
An identity code for the game. This is used in local storage (cookies) to
identify saved games and options. If you host more than one Gruescript
game on one site, they must all have different ids.
6
The number after person can be 1 or 2, determining whether the game
will be in the first person (“I am in...”) or second person (“You are in...”)
This game will be in second person. This only affects the display and
default messages – your room descriptions and so on will have to be
written with the right person in mind. If there is no person line, the
game will default to second person.
examine on
If this line is present, every ‘thing’ in the game will be displayed as a
button which the player can click to examine that thing. examine is a
‘native verb’, meaning every Gruescript game will know the verb without
the author having to define it. If the game block contains examine off
instead (or no examine line at all), the ‘examine’ verb will still exist, but
will only be available as an ordinary verb button activated by a setverb.
wait on
This means that the native verb ‘wait’ will be available throughout the
game, shown as a button below the room contents. If this line is
“wait off”, the button will not be present (but the native verb will still
exist, and can be made available using an intransitive setverb – see the
chapter on verbs.) Unlike examine, Gruescript’s default behaviour if this
line is omitted is to consider wait ‘on’.
say You were minding your own business and
playing with a ball of wool one day when an
alien spaceship crashlanded right next to your
cat tree!
say
These are instructions that will be run when the game starts, before the
title and author are printed. Only one kind of instruction is used, the
command say, which prints the rest of the text on the line. The second
say instruction, as it has no text after it, will print a blank line.
7
Additionally, the game block may contain colour (or color) lines,
which configure the game’s colour palette. These are explained in
Appendix I.
Comments
The hash character (#) denotes comments; these are for authors to keep
notes and annotate code, and are completely ignored by Gruescript. The
hash can come at the beginning of a line, in which case the whole line is
ignored, or partway along a line, in which case anything after it is ignored
(If you want to output a ‘#’ character in your game, as part of a message,
room description, or anything else, use the HTML entity ‘#’.)
8
Rooms
The locations in your game world are called rooms; a game will not work
at all without at least one room. Of course, a room doesn’t have to
correspond to a literal room – it might be a corridor, a garden, or one end
of a large ballroom. Rooms are defined using room blocks. Here’s a simple
example. The first line begins the declaration and contains the room’s
name (which will be used internally by Gruescript) and description (which
will be seen by the player.) The rest of the lines can come in any order, and
define the room’s properties:
room by_fountain You're in a walled garden by a
fountain.
north summer_house
east potting_shed
tags outdoors garden
prop wall_colour red
This room’s internal name is by_fountain. This name can consist of
letters, numbers and underscores, is case sensitive (like everything else in
Gruescript), and must be unique – no other rooms or things can be named
by_fountain. The rest of the first line is the description of the room
that the player will see when they are in that room.
The second and third lines in our example are directions. These tell
Gruescript where the player can move to from the fountain room: north to
summer_house or east to potting_shed (both of which must be
defined in their own room blocks elsewhere.) The directions Gruescript
understands in this way are:
9
• the shipboard directions fore, aft, port and starboard
If you want to use unconventional directions that aren’t listed above, use
dir lines in your room block:
Room verbs
Rooms may have a verbs line:
room dance_floor
verbs tapdance robot limbo
The verbs listed on this line will become the room’s ‘permanent verbs’,
available to the player whenever they are in the room. They are listed in
an ‘Actions’ line just below the room description (but above the listed
room contents.) Attaching verbs to rooms is intended to be the exception
rather than the norm. Most verbs will be attached to things rather than
rooms (see the next chapter.)
Tags
Rooms may optionally have any number of arbitrary ‘tags’, added in the
block with lines beginning tags – there can be any number of tags lines
in a room block, and each tags line can contain any number of tags. A tag
can be whatever you like, as long as it is a string of letters, numbers and
underscores. Our fountain room has the tags outdoors and garden.
Tags are usually things that you make up yourself to use in the logic of
your game’s verbs and rules – for example, we might later write a rule that
gives the player messages about the weather if they are outside, or certain
verbs might have different effects in ‘garden’ rooms.
10
Some tags for rooms have special meanings in Gruescript:
• start means the player starts the game in this room. No more
than one room should have this tag. If there is none, the player
starts in the first room declared in your Gruescript.
• dark means the room is dark, and the player will not be able to
see the room description or objects in it unless a light source (i.e.
a thing with the tag lightsource) is present, either in the
room or carried by the player. (They will be able to see the
available directions. If you want to make an exit from a room that
a player can only use if they can see, either use a non-portable
thing such as a ladder with a climb verb, or use open and
close commands in rules – see the chapters on instructions and
rules.)
Properties
prop wall_colour red
Things may have any number of properties, set by the author. A property
is something like a tag, but instead of just being there or not there, it has a
value which can be a number or a string or one or more words. In this
case, the room’s property wall_colour has the value red. This could
be used for just about anything – perhaps there is a bull roaming the game
world whose behaviour depends on the colours of the walls around them.
The property value can be referred to from an instruction by something
like by_fountain.wall_colour, or $room.wall_colour – we’ll
explain more about this in the chapter on instructions.
11
Special properties
Only one property of rooms is special in Gruescript: desc, which
contains its description – usually the same text that is supplied after the
room name in the first line of its room block. This:
12
Things
A thing is any physical object in your game world – portable and non-
portable items, scenery, non-player characters, and anything else. They are
defined in thing blocks, like this:
13
(with a space between each line, but not a line break – if you want a line
break in your description, use the HTML ‘<br>’.)
loc bedroom
The location of the thing, specified by the (internal) name of a room. If
there is no loc line in the thing block, the thing will start ‘offstage’
(unless it is carried) until it is placed somewhere by a verb or rule.
Two of the most important lines in thing blocks, which were not in our
top hat example, are verbs and cverbs. These give the thing its usual
set of active verbs – verbs specifies the thing’s ‘permanent verbs’, which
will always be active for the thing (they cannot be de-activated, even by a
setverb), and cverbs specifies ‘carried verbs’ that will be active
whenever the thing is in the player’s inventory (they cannot be de-
activated while the thing is carried, but they can be activated by a setverb
while it is not carried.) Either of these, like tags, they can be declared over
multiple lines if necessary. Any thing block can contain verbs lines,
cverbs lines, or both, but it is more usual for portable things to have
14
carried verbs and non-portable things to have permanent verbs. For
example, it makes sense that a warning sign on the wall of a room
thing sign Warning sign on wall
verbs read
can be read whenever the player can see it, whereas a booklet
thing booklet
cverbs read
can only be read if the player is carrying it.
carried
it will start the game in the player’s inventory.
Special tags
The following tags have special meanings for things in Gruescript:
15
alive This thing is a living creature. This affects the
order that nouns are displayed in – alive things
are usually listed first in room contents.
16
mass_noun The name of the thing is a mass noun, like ‘salt’ or
‘mud’ – not plural, but not a discrete singular
item. By default, Gruescript will use ‘some’
instead of ‘a/a’ when referring to this thing in the
indefinite case. The indefinite form is based on the
thing’s short description, not its screen name, so
thing sugar bag of sugar should not
have this tag (“a bag of sugar”), but thing
grass tall grass should have it (“some tall
grass”).
quiet Things with this tag will not be listed at all in the
scrolling window, regardless of the display
options; this does not affect their listing in the
room window. It’s intended to be used for things
that are duplicated in the room description, so the
player won’t see something like “You are
next to a church. You can also see
a church.”
Special properties
The following properties of things have special meaning to Gruescript.
17
name The screen name of the thing (same as beginning
a line in the thing block with name, as described
above)
18
Note that some of these, such as name and desc, overlap in purpose with
lines in the thing block that have already been explained. This is mainly
for convenience – many or most things will have screen names and
descriptions, so you need not use prop every time. name giraffe in a
thing block does exactly the same as prop name giraffe. The fact
that they also exist as properties allows them to be read and modified
during gameplay – for example, you might want to change the giraffe’s
screen name to Jeffrey after it introduces itself to the player, with
“assign giraffe.name Jeffrey” (don’t forget to change its
display property too.) There will be more on this in the chapter on
instructions.
19
Instructions
With your knowledge of rooms and things, you are now able to create a
rudimentary game world: interconnected rooms, containing things, some
of which are portable and the player can pick up, carry around, and drop
in other rooms.
While I’ve no doubt that some people would be able to make impressive
games without using any other features of Gruescript, any complex
interactions or puzzles will need to be added using verbs, setverbs,
procedures and rules. Before we can get to those, we need to introduce
something they all depend on: instructions.
If an assertion inside a block fails, that block itself – the verb, setverb, rule
or conditional exit – is also considered to have failed (there can be
20
exceptions to this if the block contains iterators, but we’ll get to those
later.) Conversely, if all the instructions in the block run without any failed
assertions, the block is considered to have succeeded. A block with no
instructions, or only commands, will always succeed. This will become
important in the later chapters on verbs, procedures and rules.
Most instructions have one or more ‘arguments’ – input values that are
sent to the instruction when it is called. These can be numbers (integers
only), names of things or rooms (use the internal names), variable names,
or strings (of words). An instruction’s arguments are supplied after the
instruction name itself, separated by spaces. Strings always come last, so
there is no need for quotemarks or other delimeters. (Whitespace in
strings is always normalised – that is, any leading or trailing spaces are
removed, and any contiguous spaces are reduced to a single space – there
is no distinction between “foo bar” and “ foo bar ”.)
Commands
A simple command line might look like:
bring football: A football smashes through the
ceiling and lands at your feet!
This starts with the keyword for the command that is being used (bring),
then supplies that command with its argument (in this case, a single
‘thing’, the football). Optionally, after a colon (:), comes a printable
message.
bring thing Move a thing to the room that the player is in.
If the player is carrying or holding the thing,
it will be removed from their inventory and
placed ‘on the floor’
21
hide thing Set a thing’s ‘location’ to nothing, so that it is
effectively removed from the game, regardless
of where it is now (if anywhere), or whether it
is carried
22
putnear thing Move the first thing to the location of the
thing second thing. If the first thing is being held or
carried, it will be removed from the player’s
inventory. If the second thing is being held or
carried, the first thing will be put into the
room that the player is in.
tagroom tag Add a tag to the tag list of the room the player
is in. If the room already had this tag, there is
no effect.
untagroom tag Remove a tag from the tag list of the room the
player is in. If the room did not already have
this tag, there is no effect.
clear tag Remove this tag from all things and rooms
that have it
23
write variable Set a variable to a string, which can be any
string number of words and may include braced
expressions (see below)
24
die string End the game, printing the string between
triple-asterisks *** like this ***. If
the string is omitted, the message will be “I
am dead” or “You are dead”, depending
on whether the game is in the first or second
person. (This doesn’t have to represent the
actual death of the player character in the
game universe; for a notable counterexample,
this will usually be the command you use to
tell the player when they win the game.)
25
status string Sets the contents of the status bar at the top of
the game window. If this contains a vertical
line character (‘|’), the content to the left of
the vertical line will be displayed on the left of
the status bar, and the content to the right of
the bar (if any) will be displayed on the right
of the status bar. If there is no vertical bar, the
whole text will be displayed on the right. The
string may include braced expressions, in
which case the status bar will keep updating
to reflect any relevant variable changes. (If
you want to print a vertical line in the status
bar, use the HTML entity ‘|’.)
count variable Count the values in the list and assign that
lister numeric value to the variable
26
log words... Print the words (there can be any number of
them) to the Gruescript console. This is
intended as a debugging aid and has no help
during gameplay. In an exported game, the
words will be logged to the JavaScript
browser console. Note that the argument is
not a string, so it cannot contain braced
expressions, but any or all of the words may
be variable or property references (see below.)
If, after a command on the same line, there is a colon (‘:’) followed by a
message, that message will be printed after the command is run. (You
cannot use a ‘raw’ colon in a string supplied to a command, such as say –
if you want Gruescript to output a colon, use the HTML entity
‘:’.) The message is always printed, regardless of whether the
command had any effect. Like other strings, it can contain arbitrary HTML
(avoiding colons!) and braced expressions, which we will get to later in
this chapter.
Listers
The listers are:
27
in room Every thing in the specified room
Assertions
The Gruescript assertions, with their arguments, are:
28
held thing The player is holding the thing
inscope thing The thing is ‘in scope’ – that is, either in the
same room as the player, or the player is
carrying it
29
cansee The player can see; that is, the value of the
special variable cantsee is zero. (Remember
that this is the same as that variable having
never been set.) This is not related to whether
the room the player is in is dark.
contains value The list includes the value. This uses the same
lister listers, with the same arguments, as the pick
and count commands (above).
eq value value The two values are equal. This assertion, like
its friends gt and lt, is only useful if one of
the values is supplied by referencing a
variable with $ (see below).
30
lt number The first number is less than the second
number number
31
variables and properties, so we’ll come back to iterators later in this
chapter.
Referencing variables
Any argument given to an instruction may be a reference to a variable
(except string arguments; to supply variables as strings you need to use a
braced expression – see below.) In this case the value of that variable is
supplied as the argument. This is done by using the variable name,
prefixed with a dollar sign (‘$’). So
Note that instructions with arguments that are variables, like assign or
is, take the name of the variable without the $. If you try something like
A dollar sign is not used to reference a property, but the word(s) on either
or both sides of the dot can be variable references themselves, notated as
usual with the dollar sign prefix (‘$’). So if the variable vehicle contains
the word bicycle, then $vehicle.model will again evaluate to
32
Pedersen. With the appropriate variables set up, you could also use
bicycle.$info or even $vehicle.$info.
Braced expressions
In any string argument, such as the arguments of the commands say,
write and status, variables can be embedded using braced
expressions – expressions contained between curly braces (‘{’, ‘}’).
There can be any number of these in a string. They cannot be nested
directly, but they can contain references to variables or properties whose
values include braced expressions of their own. If you want to output
curly braces, use the HTML entities ‘{’ and ‘}’.
Braced expressions consist of one or two words between the braces. The
‘principal word’ of the braced expression can be any literal word, or (more
usefully) a reference to a variable or property. The principal word appears
either on its own, or with another word before it, which must be an article
33
(a, an, the or your) or a pronoun reference (nom, obj, pos, ind or
ref).
34
Reference Case Pronouns (neuter, male, female, nb, plural)
nom nominative it, he, she, they, they
obj objective it, him, her, them, them
pos possessive its, his, her, their, their
ind independent its, his, hers, theirs, theirs
possessive
ref reflexive itself, himself, herself, themself*, themselves
* I ran a thoroughly non-rigorous twitter straw poll asking nonbinary folk what
they preferred for this; most chose ‘themself’, but ‘themselves’ was a
significant minority. To give a nonbinary character ‘themselves’, you can use
plural (maybe temporarily), or give everyone a custom ref_pronoun
property and reference that, or otherwise hack around the issue.
35
output (either by say or by being displayed on a button or the status line).
If you want to keep the string as it is now, use the freeze command:
verb eat
write foo The last meal you ate was a lovely
{$this}.
freeze foo
hide $this
(note that freeze’s first argument is a variable, so it’s freeze foo,
not $foo!) will evaluate the braced expression in foo now and rewrite that
into the same variable, so foo will always display the same from now on,
regardless of how often the value of this changes.
Capitalisation in references
If a variable or property has a lowercase name, such as foo, it is possible
to refer to that variable with a different capitalisation – $Foo or $FOO –
to get the contents of that variable in Capitalised or UPPERCASE form
(unless another variable with the capitalised version of the name exists.) If
you do this with a property, you should capitalise the property name, not
the thing or room before it, e.g. banana.Ripeness or
$fruit.COLOUR. If the reference happens inside a braced expression,
and the contents of the variable or property are the internal name of a
thing (and that thing’s internal name is lowercase), the result will be a
capitalised or uppercase version of the display name.
36
and
say {THE $fruit} is {$fruit.Colour}.
(or the same line with $Fruit or $FRUIT) will print
Iterators
Iterators are commands that change the ‘flow’ of a block of instructions.
When Gruescript finds an iterator, it will consider the rest of the
instructions in the current block (that is, the game, verb, setverb,
proc or rule block that is currently being executed) to belong to this
assertion. It will then ‘iterate’ those instructions, running through them
one or more times. The iterated instructions can be commands, assertions
or even more iterators (in which case that iterator will, in turn, cause the
code after it to be iterated once or more, every time that iterator is called
by the previous iterator, and so on.)
The instructions below the iterator are treated like a ‘nested’ block of
instructions: upon each iteration, they will be run in turn until they are all
complete, in which case that iteration succeeds, or until an assertion fails,
in which case that iteration fails. Iterators are a special kind of assertion,
as they are considered to succeed or fail themselves after they are done
iterating. Note that a particular iteration failing does not necessarily mean
the iterator running it will fail.
37
Gruescript has three iterators. An iterator is always followed by a lister,
which creates its iteration list of things, rooms or values. The listers are the
same as the ones used by pick and count commands, with the same
arguments. The iterators are:
During each iteration, the special variable this will contain the current
item in the iteration list. (If this already had a value, it will be reset to its
previous value when the iterator completes, making it safe to ‘nest’
iterators or to call procedures that contain their own iterators.)
For example, the following iterator looks through all the things in the
room meadow, and causes a hungry to eat all of them, without condition:
all in meadow
say The hungry goat eats {the $this}!
hide $this
38
If we wanted to change the goat’s behaviour so that it is a little more
discerning, and only eats things with the tag edible, we can simply add
a condition to the block:
all in meadow
has $this edible # if this assertion fails,
proceed to next iteration. Otherwise...
say The hungry goat eats {the $this}!
Or, if we want it to eat a maximum of one thing, we could use select:
select in meadow
has $this edible # otherwise fail this iteration
say The hungry goat eats {the $this}!
# the iteration succeeds, so the sequence stops
and succeeds
On the other hand, if we want the goat to eat everything available for as
long as it can, until it attempts to eat something that is not edible, we
could use a sequence:
sequence in meadow
say The hungry goat eats {the $this}!
has $this edible: Oh dear, it looks ill. # if
this assertion fails, so does the sequence.
# Otherwise, the sequence is still going!
say It licks its lips and looks for more food.
As an iterator itself an assertion, the block it belongs to will fail if the
iterator does. It can be prefixed with ‘!’ to make it the opposite assertion
(this does not affect how the iteration itself or the instructions below it
behave), and it can be postfixed with ‘:’ followed by a string that will be
printed if the assertion fails. This will appear on its own line, after any
output from the iterated instructions.
39
The three orderers are:
40
Verbs
Verbs are the basic unit of interaction in Gruescript games. The player can
always see, in the ‘room’ window, the directions and verbs available to
them.
In addition to any user-defined verbs, there are six native verbs, which
always succeed: take, drop, examine, wear, remove, and wait.
Here’s an example of a thing, and a specific verb that works with it:
thing guitar
has portable
cverbs play
thing piano
has instrument
verbs play
41
verb play
say You extract a strange sound from {the
$this}!
Now the guitar and the piano both have the verb play (in the guitar’s
case, only when it is carried) but, because there is no specific verb for
either play guitar or play piano, The general verb play is used.
While a verb’s instructions are being executed, the special variable this
always contains the object of the verb – that is, the noun that the player
has clicked a button next to. So the say instruction in the general
verb play block will refer to either the guitar or the piano, whichever
is being played.
If Gruescript finds multiple verb blocks that match the player’s command,
it will run through them all (specific first, then general) until one succeeds,
then stop.
verb play
say It makes a noise like “{noise}”!
42
execution of the verb stops. So, to block an exit, you need to create a verb
block that succeeds:
verb go
direction north
at parlour
here gorilla
The gorilla stands in the way of the door.
If you want to allow the travel, use continue to make the block fail:
verb go west
at ocean_beach
say You dive into the sea and swim west...
assign swimming 1
continue
Or you can mess with the action entirely:
verb go
eq $lost 1
taghere outside
say You wander around the city at random...
random lostwalk 3
continue
verb go
eq $lost 1
eq $lostwalk 1
goto park
verb go
eq $lost 1
eq $lostwalk 2
goto graveyard
verb go
eq $lost 1
eq $lostwalk 3
goto abandoned_building
43
Setverbs
Setverbs are crucial to Gruescript’s keyboardless interface – they tell
Gruescript when to activate or de-activate particular verbs for particular
things. They look very similar to verbs – in fact, apart from beginning
with setverb instead of verb, their syntax is identical. However,
setverbs should not contain commands, only assertions, and they
should not emit messages. Using a command in a setverb, or printing a
message after a failed assertion using ‘:’, is not actually an error, but it can
have highly odd results, since it is not easy to predict when these
instructions will be run. Gruescript will check setverbs at least once every
turn, and also every time the player changes the inventory item they are
‘holding’ – but on most of these occasions, not all setverb blocks will
actually be run. This is because once any successful setverb block has
determined that a certain verb is to be active for a certain thing, no other
setverbs will be checked for that thing-verb pair.
44
Instead of a noun, a setverb may have the keyword intransitive. This
means that (if the setverb is successful) the verb will be active, but not
attached to any noun: it will show in the ‘Actions’ line in the room
descriptions window, along with the permanent verbs of the current room:
setverb fly intransitive
has cape worn
will make the ‘fly’ verb available anywhere.
Blocks of similar setverbs, like verbs, run until one of them succeeds – that
is, if any setverb succeeds for a given verb and a given thing (regardless of
whether it is a specific or general setverb), no further setverbs will
consider that verb and thing together. So if our Gruescript contains the
setverb example above, it will never be possible to deactivate the cook
verb for any thing with the food and raw tags if the oven is present.
Any exceptions must be accounted for in that same setverb block, or dealt
with another way. It can be helpful to use rules (see the next chapter) to
change a variable, and write a single setverb block that depends on the
value of that variable.
To keep a verb active for a thing, the relevant setverb(s) must pass
continually. Whenever setverbs are checked, all verbs are deactivated for
all things unless a setverb turns them on. The only exceptions are the
native verbs take, drop, wear and remove, which are set depending on
context, and the native verb examine, which is always considered active
for all things (although it is performed by clicking the noun, rather than
the verb) if your game has examine on.
45
Conversation
Everything in this chapter only applies if your game uses Gruescript’s
conversation system – that is, if your game block contains the line:
conversation on
If this line is not present, or is conversation off, you can ignore this
chapter and are free to implement dialogue however you wish, or not at
all.
For the purpose of the conversation system, an NPC is any thing with the
tag conversation. It is likely (but not necessary) that you will also
want to give it the tag alive, and one of male, female or nonbinary
to set the character’s pronouns. An example:
thing mrs_ft Mrs Fothertonhayes-Cranstanley
name mrs fothertonhayes-thomas
loc pta_meeting
tags alive female conversation
prop start_conversation "How may I help you?" \
asks Mrs Fothertonhayes-Cranstanley, looking \
down her nose at you.
prop end_conversation \
Mrs Fothertonhayes-Cranstanley gives you \
a curt nod.
46
The special properties start_conversation and
end_conversation contain strings that will be printed when the
player starts or ends a conversation with this NPC. If they are not present,
default messages are used: “You start talking to (the NPC).” or “You stop
talking to (the NPC).” (or their first-person equivalents, in a game with
person 1.)
[end conversation]
The ‘end conversation’ verb will hide the conversation panel. It will also
disappear if the NPC that the player was talking to is no longer in scope,
either because the player walked to another room or the NPC was moved
somewhere else, or if the player begin another conversation by talking to a
different NPC. If the conversation ends by any means other than the
player clicking ‘end conversation’, the NPC’s end_conversation
message will not be printed.
During the conversation, the room description and inventory are visible as
normal, and the player can continue to use whatever other verbs are
available in the room. The special variable conversation will contain
the internal name of the NPC. If you want to force the conversation to end
without removing the NPC,
assign conversation 0
will do so.
47
You are free to treat talk as an ordinary verb, including ‘overriding’ it
with specific or general verb blocks (the object is the NPC), or adding it
with a setverb to other things, such as simple NPCs that you don’t want to
make a complex conversation tree for (just leave out the conversation
tag.)
Asks, tells and says are defined by setverb and verb blocks, the same as
ordinary verbs. The ‘object’ of ask, tell or say is considered to be the
conversation topic, which can be a thing name or any arbitrary value. You
can change the display of these ‘verbs’ with display and prompt, and
do anything else that you can do in ordinary verb and setverb blocks.
Regardless of setverbs, ‘ask’, ‘say’ and ‘tell’ will only be considered for
activation when a conversation is open. They are not tied to particular
things. So,
setverb ask alibi
will mean you can “ask about alibi” when talking to any NPC, and you do
not need to worry about the verb becoming active for other things.
To make asks, tells or says that are specific to a particular NPC, use verbs
that begin with ask_, tell_ or say_, followed immediately (no space)
by the internal name of the NPC. So:
setverb ask_mrs_ft hat
will make ‘hat’ an available ‘ask’ topic for Mrs Fothertonhayes-
Cranstanley, and nobody else, whether or not the hat is actually a thing.
As usual, the setverb block can contain instructions:
setverb ask_mrs_ft hat
has mrs_ft wearing_hat
will succeed, and activate the topic, only if she has the wearing_hat
tag.
48
Verb blocks can be made the same way. Any ask_, tell_ or say_ verb
blocks with an NPC will be considered before any ask, tell or say
blocks without, with execution stopping after a success as usual, so
verb ask_mrs_ft hat
say "I inherited it from a particularly dreadful
great-aunt,"
she says. "Elegant, isn't it?"
verb ask_fish_expert
has $this fish
will activate “ask fish expert about” for every thing with the fish tag,
and
verb ask
has $this hot_topic
will make any thing with the hot_topic tag available as an ask for
everyone with conversation. Note that in these cases, only ‘things’
will be considered. If you want to do this other topics, create ‘dummy’
things.
49
Procedures and rules
Procedures
Procedures consist of a name and a group of instructions. A simple
example:
proc do_cat
bring cat
say The cat appears and meows at you.
This can then be ‘called’ by its name, either with the command
run do_cat
or the assertion
try do_cat
This can be anywhere that instructions are allowed – a verb, setverb, rule,
the game block, or another procedure. A procedure can even call itselff
(although if it does, you should take care to avoid infinite loops.)
The difference between run and try is that run is a command and try
is an assertion. run will always just run the procedure, and then the next
instruction after the run (if any) will be executed. try will succeed or fail
depending on whether the procedure succeeds or fails; like any assertion,
if try fails, the block it is in will also stop running and fail.
There can be several proc blocks with the same name. In this case, when
that name is called by run or try, the proc blocks will be run one after
the other, in the order that they appear in your Gruescript source, until
one of them fails. (Even when called from run, a procedure block can fail
and stop this execution; the only difference is that run itself is not an
assertion and cannot fail.) try will succeed if all the procedure blocks of
the specified name succeed, or fail as soon as any one of those blocks fails.
50
Arguments
Procedure blocks can have arguments. These are variables that are ‘local’
to the procedure block, which can be given specific values when calling
the procedure with run or try. For example, given a procedure
You do not have to specify all (or any) of the arguments when calling a
procedure. If an argument is not specified by a try or run instruction,
procedure blocks with that argument will treat its value as zero (even if it
had a 'global' value already.) If the try or run instruction contains more
values than a procedure block has arguments, the block will ignore the
extraneous values.
Note that strictly speaking, arguments are ‘local’ to a procedure block, not
a whole procedure! It is possible, but probably not wise, to have multiple
blocks for the same procedure with completely different arguments; each
block that is executed will be passed the same values in the same order,
but it is the first line of the block that decides which values go into which
arguments. Also note that changing the value of an ‘argument’ variable
51
within the procedure will not affect the value being given to other blocks.
So, given the multi-block procedure
proc counting num
say {$num}...
add num 1
say ...{$num}...
continue
1...
...2...
...1!
not ...3! on the last line.
Variables that are not named as arguments in a procedure block are treated
as ‘global’, identical to the same variables in all other contexts. Changing
their value in the procedure block will change them everywhere.
Rules
Rules are procedures that are run every turn, beginning at the end of the
player’s first turn. They are like procedures, but have no name or
arguments; their first line consists of the word rule on its own. Every
52
rule runs its instructions until they run out or an assertion fails; whether a
rule block fails or not makes no difference, except to where its instructions
stop. Rules run in the order that they are declared in the Gruescript source.
53
Special variables
Gruescript keeps track of the following special variables. You should only
use them for reading and not writing – writing is not illegal, but will have
unpredictable results. Generally there is a right way to change things; for
example, rather than assign room laboratory, you probably want
goto laboratory.
54
cantsee If this value is zero, the player can see. If it is 1 (or
anything else) they cannot. Remember that a
value of zero is the same as having never been set.
The player being innately unable to see is not the
same as the room being dark – these two concepts
are independent of each other. This variable
should be used for things like being temporarily
dazzled or wearing a blindfold. Unlike the other
special variables, it is safe to write to cantsee –
in fact, the correct way to change its value is to do
so. (The assertion cansee exists as shorthand for
“eq $cantsee 0”)
Visible tags
Tags can be made ‘visible’ by using a tagdesc block. This is a single line
consisting of the identifier tagdesc, followed by the name of the tag
and then a string that will be printed, in parentheses (like this),
after the thing when it appears in a room description or the player’s
inventory. If the string is omitted, the name of the tag is used, with
underscores replaced by spaces. This can be done with special tags like
lightsource; this adds the printed message but does not affect the
behaviour of the tag. Examples:
tagdesc bargain labelled "half price"
tagdesc lightsource providing light
tagdesc broken
55
Tags for rooms are never visible (change the room’s desc property
instead.)
56
Appendix I: Visual configuration
The game block may contain colour lines which determine the colours
used by the game. You can spell it color if you like. These lines take the
form
statusbar_background conversation_text
statusbar_text conversation_button
main_background conversation_button_text
main_text examine_button
main_prompt examine_button_text
main_text_old inventory_background
main_prompt_old inventory_text
instructions_button inventory_button
instructions_button_text inventory_button_text
room_background inventory_inactive_text
room_text save_background
room_button save_button
room_button_text save_button_text
conversation_background page_background
Most of these are (hopefully) self-explanatory. Note that the save...
colour-areas apply to the part of the page containing the ‘save’, ‘restore’,
‘restart’ and ‘undo’ buttons, and all those buttons – with the default
colours, its background is the same as page_background – and
inventory_inactive_text usually applies only to the button in
your inventory that corresponds to the thing currently held by the player.
Messages in the scroller appear in the main_text colour initially, then
change to main_text_old (usually a dimmer colour) on subsequent
turns. main_prompt and main_prompt_old apply to the mock
‘command prompts’ echoed in the scroller.
57
HEXCODE is a three- or six-digit colour code. It is not preceded by ‘#’ –
that would make it a comment! It can also be a css colour name like red.
For advanced visual configuration, you can apply whatever CSS you want
to your game’s HTML page once you have downloaded it. You can also
change the layout of the page itself, as long as it keeps the div elements
that Gruescript uses: topbar_left, topbar_right, scroller
(containing scroller_content), room_description, holding,
inventory, save_prompt, restart_prompt, save_load, and
saved_games and options.
58
Appendix II: Cloak of Darkness
Roger Firth’s Cloak of Darkness is a small game that has become a sort of
reverse Rosetta stone for interactive fiction authoring systems: it is
traditional to rewrite Cloak of Darkness in any new authoring system, in
order to give an overview of how that system deals with common game
elements.
The specification of the game is deliberately simple: the player begins the
game in the foyer of an opera house, wearing a cloak. There are two other
rooms, a cloakroom and a bar. Entering the bar, the player finds that the
room is dark; if they remove their cloak and (optionally) hang it on a hook
in the cloakroom and then go back to the bar, they will find the bar is now
lit. If they did not spend too long in the bar while it was dark, they will
now see a message written in the dust telling them they have won the
game; otherwise, they have scuffed out the message, and lose the game.
59
# a portable item, the cloak
thing cloak pitch-black opera cloak
carried # starts in inventory
tags portable wearable worn
# These are all special engine-recognised tags:
# rooms
room foyer You're in the foyer of the Opera House.
tags start # the player starts here
# directions to other rooms
south bar
west cloakroom
north foyer
# ‘north foyer’ is a dummy direction, so that the
# button appears: attempting to go north will be
# caught by the following verb block:
verb go north
at foyer
say But you've only just arrived!
# This verb block always ‘succeeds’, effectively
# blocking the action.
60
# The bar should be dark unless the cloak is
# elsewhere. Because a rule block will fail when
# an assertion inside it fails, the easiest way to
# do this in a single rule is to make the bar
# dark, then “un-make it dark” if the conditions
# are met.
rule
tag bar dark
!thingat cloak bar # includes carried/worn
# The ‘!’ prefix negates the assertion, so if the
# cloak is NOT at the bar, the rule will continue:
untag bar dark
# a non-portable item
thing hook brass hook
loc cloakroom
61
setverb hang hook # when to activate 'hang' verb
for the hook
!eq $held 0 # check we’re holding *something*
# (the contextual variable ‘held’ contains the
# item being held, or 0 if none.)
has $held wearable
# *any* wearable garment(s) can be hung on the
# hook. (There just happens to be only one in the
# game.)
verb hang
display hang {$held}
prompt hang {$held} on {$this}
# we’ll use an author-defined tag, ‘hooked’, to
# make something show if it’s currently hanging
# on the hook. It doesn’t change any other
# behaviour (you can still pick it back up
# normally with ‘take’), so this is
# straightforward.
tag $held hooked
say You hang {the $held} on the {the $this}.
put $held $this.loc
62
rule
at bar
!has bar dark
lt $mess 3
say A message is scrawled in the dust! It
reads:
die You win # end the game
rule
at bar
!has bar dark
# There is no need to check $mess here – if it is
# less than three, the assertion in the above rule
# would already have passed, and the game ended.
say A message is scrawled in the dust!
say What a pity, it's too scuffed to read.
die You lose
63