Aiee! Index |
Variables |
Adventure File Syntax |
ADVENTURE |
ROUTINE |
TOKEN |
FUNCTION |
GRUE |
INTRO |
ITEM |
PLAYER |
ROOM |
How-To Guide |
Programmer's Text Editors |
Aiee! adventures are written in XML. The smallest possible adventure looks like this:
<adventure start="cell">
<intro>
<text>\tSanitarium
\n\nYour family finally committed you... But you'll show them! You'll escape
and have your revenge!
</text>
</intro>
<room id="cell" name="Padded Cell">
<look>
<text>You are trapped in a totally barren padded cell.</text>
</look>
<exit dir="u"><text>You thought you had found a way out, but you were
mistaken.</text></exit>
</room>
</adventure>
|
For a more useful example, open cloak.xml or dungeoncrawl.xml in a good text editor, and study them.
Aiee! uses global variables--any variable you define is available anywhere in the adventure. A variable can have any name you want, as long as it does not contain the characters ", ', (, or ). See ID below. Once defined, you can refer to a variable with $(ID).
There is no limit on the number of variables, or how large their contents can be.
System variables start with an underscore. You cannot modify any of these, they will be set automatically with every command.
"_who" holds the id of the current player or actor (during the actor's turn),
or "" for items and rooms.
"_this" holds the id of whatever room or item contains the command.
"_here" holds the id of whatever room the player or item is currently in.
"_plroom" holds the id of whatever room the player is currently in.
"_turn" holds the current game turn.
"_cmd" holds the current command.
"_arg1" holds the id of the first argument in a command, or empty string if
not identified.
"_name1" holds the name of the first argument in a command, or empty string
if not identified.
"_text1" holds the text the user typed for the first argument in a command.
"_prep" holds the preposition the user typed between the first and second
arguments.
"_arg2" holds the id of the second argument in a command, or empty string if
not identified.
"_name2" holds the name of the second argument in a command, or empty string
if not identified.
"_text2" holds the text the user typed for the second argument in a command.
"_score" holds the player's current score.
"_maxscore" holds the maximum score defined for the adventure.
"_grue" holds the grue chance of the current room.
The syntax below is given in a simple but programmer-ish form. A nice manual will be along eventually, but this and the sample adventures should be sufficient to make your own adventures. If you still have questions, email me and ask.
Additional tags will be available in future versions, but I do not expect to change any currently-defined tags; your adventures should work in all future versions of Aiee!.
Anything in all-uppercase below is a term defined elsewhere. Definitions are given in the form "TERM := DEFINE".
[TOKEN]...Routines execute each TOKEN in order until a <continue /> or <abort /> is encountered. If the routine runs out of tokens, it is treated as if there was a <continue /> at the end.
<abort />
<!-- Stops evaluating any further in a routine, and aborts the current
user command.
-->
<call function='REF' />
<!-- Invokes a function. There are no arguments, but you can use
variables to pass information in.
-->
<continue />
<!-- Stops evaluating any further in a routine, but allows the current
user command to continue normally.
-->
<damage [target='REF'] value="REF" [element='REF'] />
<!-- Causes damage on actor 'target'. If 'target' is undefined, it is
assumed to be the player.
'value' is the amount of damage done; it must be a valid integer.
If 'element' is defined, the weapon does damage of that type;
otherwise, it does damage type "p" (physical). Only one element can
be given for damage.
-->
<debug on='BOOL' />
<!-- Turns debugging mode on or off.
You should never use this command in the final version of an
adventure, but it's very helpful while designing one.
-->
<do [silent='BOOL']>TEXT</do>
<!-- Causes the player to perform command TEXT.
If 'silent' is defined and true, no text is printed out.
-->
<gameover [die='BOOL'] />
<!-- Displays the player's score and number of hints, and ends the
game.
If 'die' is defined and true, the player dies.
-->
<if>
[<test var='REF' [op='ID']>TEXT</test>]...
[<hasitem [owner='REF'] item='REF' [ready='BOOL] />]...
[<haslight [item='REF'] />]...
[<istype item='REF' type='ID' />]...
[<then>[TOKEN]...</then>]...
[<else>[TOKEN]...</else>]...
</if>
<!-- Evaluates the tags inside, one after another. <test>, <hasitem>,
and <haslight> set the "if flag" to true or false. <then> is
evaluated and ends the <if> block if the flag is true; <else> is
evaluated and ends the <if> block if the flag is false. Normally,
you will use it in the most basic form:
<if><test var='enteredcrypt'>1</test>
<then><text>You entered the crypt.</text></then>
<else><text>You have not entered the crypt yet.</text></else>
</if>
However, you can use it for longer chains of logic by repeating
<test> <then> <test> <then> with a final <else>. See the
example under <player><score>.
<test> compares a variable and the text by an operator 'op'; if
'op' is undefined, the default is "eq":
eq Equals
ne Not Equals
le Less Than or Equal To
lt Less Than
gt Greater Than
ge Greater Than or Equal To
eq, ne, le, lt, gt, and ge first check to see if
both 'var' and 'TEXT' are numbers, and if so will
do a numeric comparison; otherwise they do a
case-insensitive string comparison.
in Text In: does a case-insensitive match on two strings.
If 'TEXT' is contained in 'var', the result is
true, otherwise false.
<hasitem /> checks to see if 'item' is contained in 'owner'
(defaults to $(_this)), or in a container that is not closed;
if the owner is a room, it will not detect an item carried by
an actor or player, though.
If 'ready' is true, the item must also be readied.
<haslight /> checks to see if 'item' is lit; if 'item' is not
defined, the current room is used.
<istype /> checks to see if 'item' is of a given type. The types
are "room", "item", "player", and "actor".
-->
<img src='FILENAME' />
<!-- Displays an image. Images must be in .gif, .jpg, or .png format,
and included in the adventure zip file.
The image will be displayed until you move to a new room or another
image is displayed.
The space the image takes will be taken out of available space to
display text, so it should not be very tall--320 pixels is a good
height. It can be as wide as you like, but over 800 pixels wide
will be off the screen of some users.
Images will not be shown in the text-only client, but the filename
will be displayed.
-->
<light on='BOOL' [item='REF'] />
<!-- Lights or darkens a room or item.
If 'item' is not defined, the current room is used.
-->
<moveto [item='REF'] loc='REF' />
<!-- If 'item' is undefined, moves the player to room 'loc'.
Otherwise, moves a specific item to 'loc'.
The loc can be a room, item, or actor, or the reserved ID
'player', in which case an item is moved to the player's inventory,
or 'null', in which case it is moved to a "nowhere" room. It is
illegal to move the player to 'player' or 'null'.
-->
<output>TEXT</output>
<!-- Shows TEXT to the player, if _who is "player" (i.e., only on the
player's actions).
-->
<random var='REF' [n='N'] range='N' />
<!-- Sets a variable 'var' to the total of 'n' random numbers from 1 to
'range'. If 'n' is undefined, it defaults to 1. For example:
<random var='chance' range='100' />
Generates a random number from 1 to 100.
<random var='chance' n='2' range='6' />
Generates a random number from 2 to 12, averaging 7.
-->
<score value='N' />
<!-- Adds N to the player's score.
-->
<set var='REF' [op='ID']>TEXT</set>
<!-- Sets a variable 'var'. If operator 'op' is undefined, it is
assumed to be '='. The operators are:
= Sets 'var' equal to 'TEXT'.
+ Converts 'TEXT' to a number, and adds it to 'var'.
- As +, but subtracts 'TEXT' from 'var'.
* As +, but multiplies 'var' by 'TEXT'.
/ As +, but divides 'var' by 'TEXT' and leaves the
integer quotient.
% As +, but divides 'var' by 'TEXT' and leaves the
integer remainder.
app Appends 'TEXT' to 'var'.
pre Prepends 'TEXT' to 'var'.
name Sets 'var' equal to the name of stuff with id 'TEXT'.
For instance, with an actor with id "orc" and name
"Gruumsh the Orc", you'd use:
<set var='orcname' op='name'>orc</set>
aname As 'name', but includes the indefinite article (a/an).
thename As 'name', but includes the definite article (the).
input Prints TEXT as a prompt, waits for a line of input
from the player, and sets 'var' to that input.
While individual rooms, items, and actors do not have any
properties or flags, you can simulate that by using a variable name
in the form 'ID.someflag'.
-->
<sound src='FILENAME' [control='play|loop|stop'] />
<!-- Plays a sound file. Sounds must be in .au, .wav, or .mid format,
and included in the adventure zip file. mp3 will be supported in a
later version, but for now only primitive sound types work.
If 'control' is not defined, or is "play", the sound is played once
and stops. If 'control' is "loop", the sound repeats constantly
until stopped with "stop". If 'control' is "stop", the sound with
that filename is immediately stopped.
Sounds will not be shown in the text-only client, but the filename
will be displayed.
-->
<text>TEXT</text>
<!-- Shows TEXT to the player.
-->
<adventure start='ID' [maxscore='N']>
<!-- Creates a new adventure.
The player will start in room 'start'.
The maximum score possible is 'maxscore', for displaying at game end;
if not defined, the default is 100.
-->
[ACTOR]...
[FUNCTION]...
[GRUE]
[INTRO]
[ITEM]...
[PLAYER]
[ROOM]...
<!-- The child tags may be given in almost any order; if you set the
location of an item or actor, the location must be defined first. You
can't put something in a place that doesn't exist yet.
-->
</adventure>
<actor id='ID' [article='NAME'] name='NAME' [gender='ID'] [hits='N'] loc='ID'>
<!-- Creates a new actor with the given name, in room 'loc'.
The actor is friendly unless given a weapon item.
'article' contains the indefinite article (a, an) for the actor; if
not set, the item has a proper noun name. "The" will be used
as the definite article if 'article' is set.
'gender' must be "m" (male), "f" (female), or "n" (neuter); if
undefined, it defaults to "n".
'hits' is the amount of damage the actor can take before dying; if
undefined, it defaults to 10.
-->
[<alias name='NAME' />]...
<!-- Adds another name for the actor. You might have the
following:
<actor id='bluegoblin' name='blue goblin' loc='cave'>
<alias name='goblin' />
</actor>
You could now refer to that actor as 'goblin' or 'blue goblin'.
-->
[<ask [subject='NAME']>ROUTINE</ask>]
<!-- ROUTINE is evaluated when the actor is asked about a
given subject. Any text that contains the subject matches
(case-insensitive). The first defined ask which matches is
used.
If the subject is not defined, this ask routine is used for
any ask that does not match any other ask. The text will be
in $(_text2).
Creative use of <ask> would allow you to implement the
Eliza therapist program...
If a actor responds to a word or phrase, it's only fair to
have either the actor or someone else mention that word or
phrase first. Word-guessing games aren't fun.
"say SUBJECT to ACTOR" also calls ask, with the actor in _arg1
and the subject in _text2, just like "ask ACTOR about SUBJECT".
You can differentiate the two by checking _cmd.
-->
[<die>ROUTINE</die>]
<!-- ROUTINE is evaluated when the actor is killed.
A common replacement is to increase the player's score:
<die>
<text>The goblin vanishes in a puff of greasy black smoke!<text>
<score value='10' />
<moveto loc='null' />
</die>
If the routine is NOT aborted, the actor is moved to 'null' and
all contents are moved to the current room.
-->
[<give>ROUTINE</give>]
<!-- ROUTINE is evaluated when an item is given to the actor.
The item ID will be in _arg2.
If the routine is aborted, the give fails.
-->
[<guard dir='DIR'>ROUTINE</guard>]...
<!-- ROUTINE is evaluated when the player attempts to move in
direction 'dir'.
If the routine is aborted, the move fails.
-->
[<hurt>ROUTINE</hurt>]
<!-- ROUTINE is evaluated when the actor has been injured but not
killed.
-->
[<invisible />]
<!-- Makes the actor "invisible"--it will not be listed in the
room description.
-->
[<look>ROUTINE</look>]
<!-- ROUTINE is evaluated when the player types 'look CREATURE'.
If the routine does not exist, the standard "You see nothing
special about the whatsit." is shown.
-->
[<observe>ROUTINE</observe>]
<!-- ROUTINE is evaluated when the actor is in the same room as the
player, before the player performs an action. The player's
command is available in the system variables.
Actor <observe> is run after the player <command>,
and before the room <command>.
If the routine is aborted, the player's command fails.
-->
[<turn>ROUTINE</turn>]
<!-- ROUTINE is evaluated every turn.
<set var='$(_this).distance'>3</default>
[...]
<turn>
<if><hasitem owner='$(_plroom)' item='$(_this)' />
<else><abort /></else>
</if>
<if><test var='$(_this).distance'>0</test>
<then>
<text>The thing hits you with its ropy tentacles!</text>
<damage value='10' />
</then>
<else>
<text>The horrible thing slithers toward you...</text>
<set var='$(_this).distance' op='-'>1</set>
</else>
</if>
</turn>
-->
</actor>
<function id='ID'>ROUTINE</function>
<!-- Defines a function. Functions can be invoked with <call /> from
any routine.
-->
<grue>ROUTINE</grue>
<!-- ROUTINE is evaluated when the player moves from a dark room with a
"grue" value into a dark room or in an exitless direction. See
<room> for more information.
Example:
<grue>
<random var="roll" range="100"/>
<if><test var="roll" op="le">$(_grue)</test>
<then>
<text>You step forward boldly, and fall into a bottomless pit.</text>
<gameover die="true"/>
</then>
<else><text>You stumble in the dark, and nearly fall in a pit.</text></else>
</if>
</grue>
-->
<intro>ROUTINE</intro>
<!-- ROUTINE is evaluated when the game starts.
Use it to give the user an introduction to the game, instructions,
and set up any variables needed.
-->
<item id='ID' name='NAME' [loc='ID'] [light='BOOL'] [article='NAME']>
<!-- Creates a new item with the given name in room 'loc' (same meaning
as <moveto>), or room 'null' if not defined.
If 'light' is defined and true, the item is a light source.
'article' contains the indefinite article (a, an) for the item; if
not set, the item has a proper noun name. "The" will be used
as the definite article if 'article' is set.
-->
[<alias name='NAME' />]...
<!-- Adds another name for the item.
-->
[<close>ROUTINE</close>]
<!-- ROUTINE is evaluated when the player types 'close ITEM'.
If the routine is aborted, the close does not succeed.
-->
[<container [capacity='N'] [closed='BOOL'] [locked='ID'] />]
<!-- Makes the item a container.
If 'capacity' is defined, the item can hold "N" weight units;
otherwise, the item can hold 1000 weight units.
If 'closed' is defined, the item can be opened and closed; when
closed, the contents are not listed in your inventory, and
contained light sources don't shine. If undefined, the
container is always open and cannot be closed.
If 'locked' is defined, the container locks when closed, and
can only be opened if the player is carrying the item with the
given ID.
-->
[<drop>ROUTINE</drop>]
<!-- ROUTINE is evaluated when the player types 'drop ITEM'.
If the routine is aborted, the drop does not succeed.
-->
[<look>ROUTINE</look>]
<!-- ROUTINE is evaluated when the player types 'look ITEM'.
If the routine does not exist, the standard "You see nothing
special about the whatsit." is shown.
-->
[<invisible />]
<!-- Makes the item "invisible"--it will not be listed in the room
description.
-->
[<open>ROUTINE</open>]
<!-- ROUTINE is evaluated when the player types 'open ITEM'.
If the routine is aborted, the open does not succeed.
-->
[<points value='N' [loc='ID'] />]
<!-- Adds to the player's score if found in 'loc' when <gameover />
is hit. 'loc' defaults to 'player' if undefined.
-->
[<put>ROUTINE</put>]
<!-- ROUTINE is evaluated when the player puts this item into
something else.
If the routine is aborted, the put does not succeed.
-->
[<ready type='ID' [elements='ID'] [armor='N']>ROUTINE</ready>]
<!-- The item will be readied or removed when the player types
'ready ITEM'. If it is initially located on a player or actor,
it is readied at game start.
The player cannot have anything else of the same 'type' ready.
Items which can be readied, must be readied to be used.
If 'elements' is defined, the item is of all listed elements for
combat purposes; the default is "p" (physical). The other
elements are "a" (air), "e" (earth), "f" (fire), and "w"
(water). Multiple elements can be listed.
If 'armor' is defined, the item provides N defense against
attacks listed in 'elements', N/4 defense against other
elements.
ROUTINE is evaluated when the item is readied; it cannot be
readied if the routine ends with <abort />.
-->
[<remove>ROUTINE</remove>]
<!-- ROUTINE is evaluated when the item is removed; it cannot be
removed if the routine ends with <abort />.
-->
[<take>ROUTINE</take>]
<!-- ROUTINE is evaluated when the player types 'take ITEM'.
If the routine is aborted, the take does not succeed.
-->
[<turn>ROUTINE</turn>]
<!-- ROUTINE is evaluated every turn.
To make it only work if the player is in the same room, use:
<turn>
<if><hasitem owner='$(_plroom)' item='$(_this)' />
<else><abort /></else>
</if>
...whatever...
</turn>
-->
[<use>ROUTINE</use>]
<!-- ROUTINE is evaluated when the player types 'use ITEM [TARGET]'
or 'kill CREATURE ITEM'. If the routine is aborted, the use or
attack does not succeed.
When making a weapon, <use> should check that the target
exists, possibly use a random chance of the attack hitting, and
then apply damage to the target. This is a good operation to
put in a function:
<function id='attack'>
<if><test var='atktarget'></test>
<then><abort /></then>
</if>
<random var='roll' range='100' />
<if><test var='roll' op='le'>$(atkchance)</test>
<then>
<damage target='$(atktarget)' value='$(atkdamage)' element='$(atkelement)' />
</then>
<else>
<text>You miss!</text>
</else>
</if>
</function>
<use>
<set var='atktarget'>$(_arg2)</set>
<set var='atkchance'>50</set>
<set var='atkdamage'>6</set>
<set var='atkelement'>p</set>
<call function='attack' />
</use>
-->
[<weight value='N' />]
<!-- The 'weight' of the item. The player can carry up to 1000
weight units, total. Making the item weigh over 1000 is a good
way to make it uncarryable (9999 is the maximum weight, usually
used for "uncarryable" weight). If <weight> is not defined,
the item weighs 100 units.
-->
</item>
<player [hits='N']>
<!--
'hits' is the amount of damage the actor can take before dying; if
undefined, it defaults to 10.
-->
[<command>ROUTINE</command>]
<!-- ROUTINE is evaluated when the player types an "active"
command, before the command is performed. An <abort />
prevents the command from being evaluated, and does not take a
turn.
This routine is not called for 'look', defined exits, or
Special commands. It is called for undefined exits.
Player <command> is run before actor <observe>s and
room <command>.
This routine is evaluated before the room <command> routine.
-->
[<die>ROUTINE</die>]
<!-- ROUTINE is evaluated when the player is killed.
The default is to end the game.
-->
[<inv>ROUTINE</inv>]
<!-- ROUTINE is evaluated when the player types 'inv'.
If the routine is aborted, the inventory does not continue.
A common use of this routine is to display RPG stats stored in
variables.
-->
[<score>ROUTINE</score>]
<!-- ROUTINE is evaluated when the player's score is to be
displayed. If the routine is aborted, the score display does
not continue. You can use this to provide a more meaningful
score description:
<player>
<score>
<if>
<test var='_score' op='lt'>2</test>
<then><set var='rank'>Amateur</set></then>
<test var='_score' op='lt'>4</test>
<then><set var='rank'>Novice</set></then>
<test var='_score' op='lt'>6</test>
<then><set var='rank'>Adventurer</set></then>
<test var='_score' op='lt'>8</test>
<then><set var='rank'>Professional</set></then>
<else><set var='rank'>Master</set></else>
</if>
<text>Your score is $(_score), in $(_turn) turns.\n
This gives you a rank of $(rank).
</text>
<abort />
</score>
</player>
-->
[<turn>ROUTINE</turn>]
<!-- ROUTINE is evaluated every turn.
-->
</player>
<room id='ID' name='NAME' [light='BOOL'] [grue='N']> (one or more)
<!-- Creates a new room. The room name is shown in the title bar, and
is printed every time the player enters the room after the first
time.
If 'light' is defined and false, the room starts darkened;
otherwise, it is lit.
If 'grue' is defined and greater than 0, something will happen when
you try to move from a dark room to a dark room.
If there is a <grue> routine in the adventure, the system variable
"_grue" is set, and that routine is called; if not, the built-in
"grue" function is used, which uses the grue value as your
percentage chance of being eaten by a grue. Grues originated in
Infocom's _Zork_, but have since spread to adventures throughout
the universe.
-->
[<command>ROUTINE</command>]
<!-- ROUTINE is evaluated when the player types an "active" command
in the room, before the command is performed. An <abort />
prevents the command from being evaluated, and does not take a
turn.
This routine is not called for 'look', defined exits, or
Special commands. It is called for undefined exits.
Room <command> is run after the player <command> and
actor <observe>.
-->
[<dark>ROUTINE</dark>]
<!-- ROUTINE is evaluated when the player first enters the room or
types 'look', if the room is not lit. If not defined, the
player is told "It is dark."
-->
[<enter>ROUTINE</enter>]
<!-- ROUTINE is evaluated every time the player enters the room.
-->
[<exit dir='DIR' [visible='BOOL'] [room='ID']>ROUTINE</exit>]...
<!-- Creates an exit from the room.
If 'visible' is defined and true, the exit is listed after the
description; otherwise you must describe it or leave it to the
player to guess.
If 'room' is defined, and the routine did not abort, you move
there. 'room' is equivalent to having <moveto loc='ID' /> in
the routine.
-->
[<hint>TEXT</help>]
<!-- Hint text is displayed when the player types 'hint' in the
room. There may be multiple hints in a room, and they are
displayed in order, once for each hint request.
For example:
<hint>The dwarf seems to want something for his help.</hint>
<hint>Dwarves like precious metals.</hint>
<hint>Give him the gold statue.</hint>
The number of times you asked for hints is shown with your
score.
-->
[<look>ROUTINE</look>]
<!-- ROUTINE is evaluated when the player first enters the room or
types 'look', *if* the room is lit.
-->
[<points value='N' />]
<!-- Adds to the player's score when first entered.
-->
[<turn>ROUTINE</turn>]
<!-- ROUTINE is evaluated every turn.
To make it only work if the player is in the same room, use:
<turn>
<if><hasitem owner='$(_plroom)' item='$(_this)' />
<else><abort /></else>
</if>
...whatever...
</turn>
-->
</room>
While creating basic adventures in Aiee! is very easy, more advanced techniques can be a little more subtle...
However, you can add skills to an adventure by creating items, and then the player can "use SKILL on TARGET":
<item id='frotz' name='Frotz spell' loc='player'>
<look><text>A simple spell to create light on an object.</text></look>
<drop><text>Don't be ridiculous.</text><abort /></drop>
<put><text>Don't be ridiculous.</text><abort /></put>
<take><text>Don't be ridiculous.</text><abort /></take>
<use>
<if><test var='_arg2'></test>
<then><text>Frotz what?</text><abort /></then>
<istype item='$(_arg2)' type='item' />
<then><set var='frotzed' op='thename'>$(_arg2)</set>
<text>You cast frotz on $(frotzed), which begins glowing.</text>
<light on='true' item='$(_arg2)' />
<continue />
</then>
<else><text>You can only frotz inanimate objects.</text></else>
</if>
</use>
<weight value='0' />
</item>
If you don't have a good programmer's text editor with syntax highlighting for XML (no, not MS Notepad or MS Word), try one of these: