Game engine
The game engine interprets PLAYER commands in the context of the WORLD
described by the current GAME FILE. Every time the PLAYER issues a
command, the engine looks up the corresponding action or
movedef defined in the GAME FILE (or recognizes a special
command), executes any tests specified by the action's
condstatement, and when it finds a test that matches the
current GAME STATE it executes the associated WDL statement
directives. Those directives may change the state of the world
(e.g., by changing variable values), may move the
PLAYER, may print messages to the screen, may issue WARNINGs
or ERRORs, or may end the game. By chaining together
sequences of such tests and directives, surprisingly complex game
scenarios can be achieved.
The specific semantics of the WDL, as implemented by the
Zurk game engine are:
- NAMEs
- ROOMs, ACTIONs,
and OBJECTs all are assigned unique
NAME strings. Each NAME MUST be unique in a single
GAME FILE - no two ROOMs can share the same NAME,
nor can a ROOM and an OBJECT share the same name.
Each NAME can be declared only once (via a
Room, Action, etc. command), though it may be
referenced more than once (via a RoomRef, etc. command). It is an UNRECOVERABLE ERROR to reference a NAME
that has not been declared, though the reference is allowed to precede
the declaration in the GAME FILE. That is, a NAME, say
ObjA, can be referenced (ObjRef(ObjA)) on lines 93,
112, 153, and 198 of a GAME FILE, but be declared on line 138.
The exceptions to the uniqueness rule are the NAMEs
associated with VARIABLEs and MOVEs. The same VARIABLE NAME
or MOVE NAME may be reused in different contexts. These cases
will be described in detail below, under Variable and Move.
In addition to the NAMEs that are declared in the GAME FILE,
there are a small number of built-in, special-purpose NAMEs:
- Player
- The NAME of the data structure
corresponding to the PLAYER. This special NAME allows WDL
statements to reference characteristics of the PLAYER such as
Player.location.
- Inventory
- The special LOCATION associated with the
PLAYER. The INVENTORY is a list of OBJECTs carried by the
PLAYER. An OBJECT's location can be assigned to be
Inventory or can be tested against Inventory.
While Inventory is the name of a LOCATION, the PLAYER cannot
occupy the INVENTORY location (that would be just silly).
- location
- The NAME of a special VARIABLE
associated with every OBJECT and with the PLAYER. The
location variable stores a reference to the LOCATION
(ROOM or Inventory) of the object.
- Motility
- The NAME of a special VARIABLE
associated with all OBJECTs other than the PLAYER. The meaning of
this VARIABLE will be described further below, under Object.
- Room and RoomRef
- These statements
declare and reference a ROOM (i.e., a LOCATION at which
the PLAYER can be at some time during the game), respectively. A
ROOM has the following properties:
- NAME
- See NAMEs, above.
- description
- String giving the description of the
ROOM. The description is printed whenever the PLAYER enters the ROOM
or when the PLAYER gives the look command with zero
arguments.
- objset
- The set of OBJECTs currently in the ROOM.
OBJECTs may be added to or removed from the ROOM as a result of
PLAYER actions. Note that if the LOCATION of an OBJECT is updated,
the objsets for all affected ROOMs MUST also be updated.
E.g., if the PLAYER picks up a rose OBJECT from the
garden room, then rose.location would be set to
Inventory, while the rose would removed from
garden's objset.
- moveset
- The set of MOVE commands that are available
to the PLAYER in this ROOM. The moveset effectively defines
the interconnectivity of the LOCATIONs in the world, by describing
the neighbors of a ROOM.
- Variable
- This statement declares a VARIABLE. Unlike
ROOMs, OBJECTs, and ACTIONs, a VARIABLE may be declared more than once
in a GAME FILE. Specifically, each VARIABLE is attached to an OBJECT,
and the fully qualified name of the VARIABLE is
objname.varname. The fully qualified name of a VARIABLE MUST
be unique. E.g., OBJECTs ObjA and ObjB can both
have instances of the VARIABLE VarA, but their fully
qualified names are ObjA.VarA and ObjB.VarA,
respectively. No OBJECT can contain two instances of the same
VARIABLE.
- Object and ObjRef
- These statements declare
and reference an OBJECT, respectively. An OBJECT is a thing in the
environment and can either be MOBILE (can be picked up and moved by
the PLAYER) or SESSILE (cannot be picked up or moved). An OBJECT has
the following properties:
- NAME
- See NAMEs, above.
- description
- String giving the description of the
OBJECT. The description is printed whenever the PLAYER picks up the
OBJECT (adds it to the PLAYER INVENTORY) or gives the look
command with this OBJECT as an argument.
- Motility
- Special variable possessed by all OBJECTs.
An OBJECT can be either Motile (meaning that it can be added
to the INVENTORY or dropped in an arbitrary ROOM) or Sessile
(meaning that it is immobile and cannot be picked up or relocated).
- varset
- (optional) An OBJECT can contain a set of
VARIABLEs, allowing state to be associated with the OBJECT. In WDL,
the state of a VARIABLE v associated with OBJECT o
can be tested or set via o.v. If o does not have a
variable named v, it is a RECOVERABLE ERROR to query or
attempt to set that variable. If the attempt to access o.v
is the result of a PLAYER command, then a WARNING should be issued to
the PLAYER and the current command halted. If the access to
o.v is detected at GAME FILE load time, then an UNRECOVERABLE
ERROR should be issued to the PLAYER indicating that the GAME FILE is
corrupted. If the access is detected at GAME FILE load time in the
ParserTest module, then an UNRECOVERABLE ERROR should be
issue and the load abandoned.
- Action
- An ACTION is a command that the PLAYER can
execute, along with a set of conditions and outcomes for each
condition. The notion is that the same ACTION can have different
results depending on the GAME STATE when it is executed. E.g., an
attempt to leave a ROOM through a door might fail when that door is
closed (door.closed=true), or an ACTION might only be
applicable when taken from a certain ROOM
(Player.location=garden). Each ACTION is characterized by
the following properties:
- NAME
- See NAMEs, above. The ACTION
NAME is specified by the first argument to the ACTION
declaration statement.
- arity
- The arity of an ACTION is the number of
arguments that that action takes. E.g., an ACTION with an arity of 3
takes three arguments when the PLAYER types it in: aName argA
argB argC. Each argument is mapped to a numerical argument ID in
the body of the action: the first argument becomes $1, the
second becomes $2, etc. The ACTION arity is specified by
the second argument to the ACTION declaration statement.
- condstatement
- The condstatement determines
the possible outcomes of the ACTION when executed in different GAME
STATEs. A condstatement is essentially an arbitrarily long
list of boolean expressions, each of which is associated with a list
of outcome statements. When the ACTION is invoked, each of the
boolean expressions are evaluated in turn. Testing stops the
first time an expression is found to evaluate to
true; at that point, the corresponding list of outcome
statements is executed, in order. The boolean expressions and the
outcome statements can test and set the current GAME STATE by
referring to variables either by fully qualified name (e.g.,
objA.varC) or by argument reference (e.g.,
$2.varC). The statements can also refer to the special
OBJECT Player, the special variables location or
Motility, or the special value Inventory.
- Move
- The Move statement declares a move
that is possible from some ROOM. Every MOVE must be associated with
a ROOM, and different ROOMs can have MOVEs with the same name (but
different conditions and outcomes). The outcome of a move describes
how the GAME STATE changes when the PLAYER executes that MOVE. Like
ACTIONs, MOVEs support conditional execution and outcomes. Unlike
ACTIONs, MOVEs do not have arity - every MOVE is of arity 0 and
accepts no arguments. It is a RECOVERABLE ERROR and should result in
a WARNING for the PLAYER to provide an argument to a MOVE command.
MOVEs possess the following properties:
- NAME
- See NAME, above. Note that different
ROOMs can declare MOVEs with the same NAME, so MOVE
NAMEs are non-unique.
- condstatement
- The outcome(s) of a specific MOVE are
described by its condstatement in exactly the same way as
outcomes are handled for ACTIONs. See condstatement under
Action, above, for the precise semantics.
- Special WDL commands
- The WDL language also provides the
following directives which control the game engine itself or cause
output to be displayed.
- print
- Displays a message to the PLAYER by printing
its string argument to STDOUT. This string output MUST be wrapped at
word boundaries at the 75 column mark. (I.e., the text string is
wrapped at the last word boundary before the 75 column mark.)
Otherwise, the string argument to the print command MUST be
printed literally.
- error
- Displays an ERROR message to the PLAYER, by
printing its string argument to STDERR, and then terminates the game
and exits the Zurk engine. This statement is to be used by
a GAME FILE designer to indicate an UNRECOVERABLE ERROR. Any text
printed in addition to the string argument to the error
statement MUST be human-consumable. The exit from the Zurk
game MUST be clean and not cause the corruption of any GAME FILEs or
other durable state.
- warn
- Displays a WARNING message to the PLAYER by
printing its string argument to STDERR. Unlike error,
however, warn indicates a RECOVERABLE ERROR and game play
MUST continue uninterrupted. This statement is used to indicate that
the PLAYER has executed an illegal command, provided the wrong number
of arguments to a command, etc. The warn statement MUST NOT
cause the corruption of any GAME STATE, any GAME FILEs, or any other
durable state.
- end
- Terminates the game. The game termination MUST
be clean and MUST NOT corrupt any GAME FILEs or other durable state.
Terran Lane
2005-02-28