% $Header: c:/tads/doc/RCS/tadsex.tex 1.1 92/11/13 16:46:02 mroberts Exp Locker: mroberts $
%
% TADS documentation:  Advanced Programming Examples
%
% Copyright (c) 1989 by Michael J. Roberts.  All Rights Reserved.
%

\chapterheader{Chapter Seven}{Advanced TADS Techniques}{7}

So far, we've concentrated on the basic aspects of writing a
game with TADS.  Once you get started on a game, you will probably
have lots of ideas for plot elements in your game.  Many parts of
an adventure game can be implemented very easily with TADS, simply
by piecing together the basic classes from {\tt adv.t} and filling
in descriptions, locations, and so forth.  However, some of the
things you will want to implement won't be that easy, and you
will need to do some programming.

This chapter is intended to help you realize your more complex
ideas by showing how to implement a number of sophisticated
plot elements.  We've tried to show examples of the most common
difficult game features, so there's a good chance that you'll
find an example in this chapter that's very similar to what
you're trying to do.  Even if you don't find a close
match to the feature you have in mind, reading this chapter
will show you many of the more subtle points of TADS programming,
which should give you a better idea of how to implement your
ideas.\bull


\chapterbody

\majorsection{Creating your own Verbs}

^^{verb}
It's always best to try to limit the number of verbs a player needs
to use to complete your game, because it helps prevent your
adventure from becoming a game of ``guess the verb.''  Choosing
the right sentence to express an idea for a command should never
be a challenge---if it is, your game will quickly become uninteresting.

However, that being said, you will sometimes want to include situations
in your games that call for verbs that aren't in the set defined in
{\tt adv.t}.  In addition, there's nothing wrong with making your
game understand obscure verbs and phrasings that are {\it alternatives}
to one of the more common verbs in {\tt adv.t}.  Furthermore, some
games include ``magic words'' that are effectively special verbs;
since the player learns these within the game, obscurity is not a
problem.

The way you add a verb depends on how the verb is used.  The simplest
kind of verb is used by itself---the player doesn't include any
objects in a command with the verb.  For example, ``north'' is
a verb that doesn't take any objects; magic words generally fall
into this category as well.  To implement a verb with no objects,
all you have to do is create a {\tt deepverb} object that includes
a {\tt verb} vocabulary property, an {\tt sdesc} property, and
an {\tt action(actor)} method.  (The {\tt deepverb} class is
defined in {\tt adv.t}; see Appendix A for details.)
The {\tt action} method is called
when the verb is used with no objects.  As an example, let's implement
a verb for a magic word; when the magic word is used, the player will
be magically transported from the cave to the shack, or the shack
to the cave.

{\example
\object{magicVerb: deepverb}
    verb = 'xyzzy'
    sdesc = "xyzzy"
    action(actor) =
    \lb
        if (actor.location = cave)
        \lb
            "Out of nowhere, a huge cloud of orange smoke
            fills the cave.  When it clears, you suddenly
            realize that you're in the shack!";
            actor.moveInto(shack);
        \rb
        else if (actor.location = shack)
        \lb
            "You suddenly feel very disoriented, and the
            room seems to be spinning all around you.  As you
            gradually regain your balance, you realize that
            you're now in the cave!";
            actor.moveInto(cave);
        \rb
        else
        \invislb
            "Nothing happens.";
        \invisrb
    \rb
\endobj
}

For a verb that takes a ^{direct object}, the verb object itself is
fairly simple; all you need to do is define {\tt verb} and
{\tt sdesc} properties, plus the special property {\tt doAction}.
The {\tt doAction} property defines a string that the system uses
to form the names of two new properties.  As an example, we'll
implement the verb ``smell,'' which takes as its direct object
the item to be smelled.

{\example
\object{smellVerb: deepverb}
    verb = 'smell'
    sdesc = "smell"
    doAction = 'Smell'
\endobj
}

When the system sees this definition, it creates two new properties:
{\tt verDoSmell} and {\tt doSmell}.  Now, for any object that the
player can smell, you add the {\tt verDoSmell} and {\tt doSmell}
methods.  For example, we'll implement a flower (notice that we
deftly bypass the opportunity for a cheap joke here at the expense
of good taste by choosing an object with a non-offensive odor):

{\example
\object{flower: item}
    noun = 'flower' 'rose'
    adjective = 'red'
    sdesc = "red rose"
    ldesc = "It's a red rose."
    verDoSmell(actor) =
    \lb
    \rb
    doSmell(actor) =
    \lb
        "A rose by any other name...";
    \rb
\endobj
}

^^{doAction}
If the {\tt verDoSmell} method isn't defined for an object,
trying to smell it will cause the system to display the message
``I don't know how to smell the {\it object}.''
Since ``smell'' is a verb that you may want to allow for every
object in your game, you may want to modify the definition of
{\tt thing} in {\tt adv.t} to provide a better default message
for this verb.  You could add these lines to the definition of
{\tt thing} in {\tt adv.t}:

{\example
verDoSmell(actor) =
\lb
\rb
doSmell(actor) =
\lb
    "It smells like any other <<self.sdesc>>.";
\rb
}

If you do this, all you need to do is override the {\tt doSmell}
method for any object with a non-default odor.

^^{ioAction}
Adding a verb that takes two objects (a direct object plus an
^{indirect object}) is similar.  Rather than defining a {\tt doAction}
property, you define an {\tt ioAction({\it preposition})} property.
For example, suppose you want to implement a verb ``pry {\it direct-object}
with {\it indirect-object}'':

{\example
\object{pryVerb: deepverb}
    verb = 'pry'
    sdesc = "pry"
    ioAction(withPrep) = 'PryWith'
    prepDefault = withPrep
\endobj
}

This causes the system to create the properties {\tt verIoPryWith},
{\tt verDoPryWith}, and {\tt ioPryWith}, which the system will
call in that order ({\tt verDoPryWith} is called for the direct
object, and the other two are called for the indirect object.)
Note that we've also defined a default preposition; while this
isn't required, it allows the system to be smarter by guessing
what the player meant when some words are left out.  For example,
if the only object present that can be used as an indirect object
in the ``pry'' command is a crow bar, the system can provide the
``with the crow bar'' phrase by default when the player simply
types ``pry the door.''

Whether or not you add verbs to the basic set in {\tt adv.t}, it's
a good idea to document your verbs.  In the instructions for your game,
you should provide a list of all of the verbs that are needed to complete
the game.  If players gets stuck, this list can be helpful in
convincing them that they're not fighting the parser.


\majorsection{Complex Room Interconnections}

^^{rooms}
The basic ^{travel} routines used by {\tt adv.t} make it easy to
implement a basic ^{map}:  all you have to do in your definitions
is to provide properties with names like {\tt north} and {\tt south},
and set them to point to rooms, simply by naming the room:

{\example
\object{kitchen: room}
    north = hallway
    east = porch
\endobj
}

This is fine for most situations, but frequently you'll want something
to happen during travel that's more complicated than simply moving
the player to the given room.  For example, if you have a very long
hallway, you may want a special message to be displayed when travelling
to indicate that the walk was especially long.  Or, you may want to
set off a trap when the player takes a certain exit.  Or, you may want
to prevent travel in a particular direction until a door has been
opened or some other obstacle has been removed.

The basic trick to implementing special travel effects like these
is to realize that the direction properties of a room aren't
limited to simple room pointers---you can write a method instead.
As long as you follow a few simple rules, you can do whatever you
want in this method.  Basically, if you want normal travel to
occur after the method is finished, the method should return a
room object which indicates the destination of the travel.  If
you don't want anything more to happen after the method is finished,
the method should return {\tt nil}.  The following sections show
how to use direction methods to implement a variety of special
travel effects.

\minorsection{Display a Message During Travel}

One of the simplest special effects is displaying a message during
travel.  For example, suppose you have a stairway leading down,
but the stairs collapse while the player is descending them,
dropping the player unceremoniously into the room below (with
no harm done).  All you really want to do here is to display
a message about the stairs collapsing, then complete the travel
as normal.

{\example
\object{livingRoom: room}
    sdesc = "Living Room"
    ldesc = "You're in the living room.  A dark stairway leads
             down."
    down =
    \lb
        "You start down the stairs, which creak and moan and
        wobble uncertainly beneath you.  You stop for a moment
        to steady yourself, then continue down, when the entire
        stairway suddenly breaks away from the wall and crashes
        to the floor below.  You land in a heap of splintered
        wood.  After a few moments, everything settles, and you
        manage to get up and brush yourself off, apparently
        uninjured.\char`\\b";
        \vskip12pt
        return(cellar);
    \rb
\endobj
}

The ``{\tt \char`\\b}'' is at the end of the message to separate the
message about the stairway's collapse from the descriptive
text that accompanies normal travel into a new room.  Since the
method just returns a room object, travel will proceed as normal
after the message has been displayed.

\minorsection{Simple Obstacles}

^^{obstacles}
The collapsing stairway of the previous example will need some
additional special consideration at the other end, because the
player clearly can't go back up the stairs once they've collapsed.
The simplest case is to disallow travel altogether, which is
simple:  just make an {\tt up} method that displays an appropriate
message, then returns {\tt nil} to indicate that no travel is
possible in this direction.

{\example
\object{cellar: room}
    sdesc = "Cellar"
    ldesc = "You're in the cellar.  A huge pile of broken pieces of
            wood that was once a stairway fills half the room. "
    up =
    \lb
        "The stairway is in no condition to be climbed. ";
        return(nil);
    \rb
\endobj
}

Since the method returns {\tt nil}, no further travel processing
takes place after the message is displayed.

Note that we should add an object for the broken stairway, just
for completeness, so that the player can mention the stairs in
a command.  We'll make it a {\tt fixeditem} since it can't be
moved or otherwise manipulated.

{\example
\object{brokenStairs: fixeditem}
    location = cellar
    noun = 'stairs' 'stairway' 'pile' 'wood'
    adjective = 'broken'
    sdesc = "pile of wood"
    ldesc = "It's a huge pile of wood that used to be a stairway.
            You won't have much luck trying to climb it. "
\endobj
}

\majorsection{Adaptive Room Descriptions}

The stairway examples shown above could be enhanced to make it
possible to enter the cellar by some other passage, which would
mean that the stairway could be seen before it collapses.  If
this were done, it would be necessary for the cellar's room
description, as well as the {\tt up} method, to be sensitive to
whether the stairway had collapsed yet.

Making a room description that adapts to the state of the room
is done by turning {\tt ldesc} into a method.  Just as special
travel effects can be achieved by making a direction property
into a method, special room descriptions can be programmed by
making {\tt ldesc} a method.

Since the stairway can be seen before it's collapsed, we'll
introduce a new object for the working stairs.  Note that
this object should be climbable, so we'll define {\tt verDoClimb}
and {\tt doClimb} methods for it.  Note that the method
{\tt actor.travelTo({\it location})} is the exact same
method that is called when the player travels normally by
typing a direction command (such as ``up'').   Also note that the
{\tt location} property of the {\tt brokenStairs} object should
initially not be set (which will make it {\tt nil}), since the
broken stairway is not part of the game initially in the new
configuration.  

{\example
\object{workingStairs: fixeditem}
    location = cellar
    noun = 'stairs' 'stairway'
    sdesc = "stairs"
    ldesc = "The stairway leads up.  It looks extremely
            rickety. "
    verDoClimb(actor) = \char`\{\char`\}
    doClimb(actor) =
    \lb
	actor.travelTo(livingRoom);
    \rb
\endobj
}

Now we'll change the {\tt cellar} object's {\tt up} method, as
well as its {\tt ldesc} property, to reflect the current status
of the stairs.  We'll detect whether the stairs have collapsed
yet by checking the location of the {\tt workingStairs} object:
if it's in the cellar, we'll know the stairs haven't collapsed
yet, and if it's not anywhere (that is, if it's location is
{\tt nil}), we'll know that the stairs have collapsed.  (This
object switch will be implemented below, in the {\tt livingRoom}
object's {\tt down} method.)  We'll use this detection technique
to make both the {\tt ldesc} and the {\tt up} method sensitive
to the status of the stairway.

{\example
\object{cellar: room}
    sdesc = "Cellar"
    ldesc =
    \lb
        "You're in the cellar. ";
        if (workingStairs.location = nil)
        \invislb
            "A huge pile of broken pieces of wood that was
            once a stairway fills half the room. ";
        \invisrb
        else
        \invislb
            "A suspicious-looking stairway leads up. ";
        \invisrb
    \rb
    up =
    \lb
        if (workingStairs.location = nil)
        \lb
            "The stairway is in no condition to be climbed. ";
            return(nil);
        \rb
        else
        \lb
            "The stairs creak and moan and sway, but you
            somehow manage to climb them safely.\char`\\b";
            return(livingRoom);
        \rb
    \rb
\endobj
}

We'll also have to make a small change to the {\tt down} method
in the {\tt livingRoom} object, to replace the {\tt workingStairs}
object with the {\tt brokenStairs} object.  Here's the new
{\tt down} method, which will
remove the {\tt workingStairs} object from the game by moving it
to {\tt nil}, and put the {\tt brokenStairs} object in its place.

{\example
\object{livingRoom: room}
    sdesc = "Living Room"
    ldesc = "You're in the living room.  A dark stairway leads
             down."
    down =
    \lb
        if (workingStairs.location = nil)
        \lb
            "You quickly notice that the stairs have
            collapsed, so there's no way down. ";
            return(nil);
        \rb
        else
        \lb
            "You start down the stairs, which creak and moan and
            wobble uncertainly beneath you.  You stop for a moment
            to steady yourself, then continue down, when the entire
            stairway suddenly breaks away from the wall and crashes
            to the floor below.  You land in a heap of splintered
            wood.  After a few moments, everything settles, and you
            manage to get up and brush yourself off, apparently
            uninjured.\char`\\b";
            \vskip12pt
            workingStairs.moveInto(nil); \hfill /* replace workingStairs... */
            brokenStairs.moveInto(cellar);  \hfill /* ... with brokenStairs */
            return(cellar);
        \rb
    \rb
\endobj
}

\majorsection{Doors}

^^{doors}
One common type of obstacle is a door.  Doors can be implemented
using exactly the same mechanisms shown above for the stairway:
in the directional properties for the rooms involving the door,
check the status of the door object (generally the {\tt isopen}
property), and either return a room object if the door is open,
or display a message and return {\tt nil} if the door is closed.
This type of mechanism is used in {\tt DITCH.T} for its doors.

However, {\tt adv.t} provides a set of classes that makes it
easier to implement doors.  The classes are based on a general
class called {\tt obstacle} (which you may wish to extend into
your own specialized classes to implement different types of
obstacles).  The basic class is {\tt doorway}, and an additional
class called {\tt lockableDoorway} makes it easy to implement a
door that can be locked and unlocked with a particular key object.

By using the {\tt doorway} and {\tt lockedDoorway} classes, you
can implement doors with no programming, because the basic travel
routines will do the necessary work based on properties defined
in your doorway objects.

The first thing to note about doors is that they have an unusual
property:  a door exists in two rooms, because it connects the
two rooms.  Unfortunately, TADS doesn't have any good way of
putting a single object in multiple locations.  To work around
this problem, you should make {\it two} objects for each actual
door in your game:  one for each side.  Fortunately, the {\tt doorway}
class makes it easy to keep the states of the two objects
synchronized:  for example, when you open one side of the door, the
other side is automatically opened.

Start by implementing the two sides of the door.   Make a {\tt doorway}
object for each, filling in the normal properties for any object
({\tt location}, {\tt sdesc}, {\tt noun}).  Put one {\tt doorway}
object in each room to be connected.  Now add the special properties
for a doorway.  Set {\tt otherside} to the other door object making
up the pair of doors; this is how the door's internal routines know
what other door to keep synchronized.  Set {\tt doordest} to the
room object that's reached by travelling through the door.

Then, all you need to do for the rooms containing the doors is
to set the direction properties to the doors themselves, rather
than the rooms reached through the doors.  Here's an example,
showing the two room objects and the two door objects.

{\example
\object{porch: room}
    sdesc = "Porch"
    ldesc = "You're on a porch outside a huge run-down wooden house
            that was probably painted white in the distant past,
            but which is gray and faded now.  A door (which is
            << porchDoor.isopen ? "open" : "closed" >>) leads west."
    west = porchDoor                   \hfill /* use door for the direction */
\endobj

\vskip12pt
\object{frontHall: room}
    sdesc = "Front Hall"
    ldesc = "You're in the spacious front hallway of the old house.
            The paint on the walls is all peeling, and a thick veil
            of spiderwebs hangs down from the ceiling.  A door
            (which is <<hallDoor.isopen ? "open" : "closed">>)
            leads east."
    east = hallDoor
\endobj

\vskip12pt
\object{porchDoor: doorway}
    location = porch
    noun = 'door'
    sdesc = "door"
    otherside = hallDoor
    doordest = frontHall
\endobj

\vskip12pt
\object{hallDoor: doorway}
    location = frontHall
    noun = 'door'
    sdesc = "door"
    otherside = porchDoor
    doordest = porch
\endobj
}

Note that there's no need to define {\tt ldesc} for a doorway, unless
you want to.  The default {\tt ldesc} defined in the {\tt doorway}
object displays a message saying simply ``It's open'' or ``It's closed''
or ``It's closed and locked,'' depending on the door's state.  If you
want to override {\tt ldesc}, you can use the {\tt isopen}
and {\tt islocked} properties to determine the state of the door
to be displayed in your custom message.

Some more properties of the {\tt doorway} class are worth mentioning.
If the door is not locked, the door will be opened {\it automatically}
when the player tries to travel through it (such as by going west from
the porch in the example above).  This makes the game more convenient,
because it's not necessary to type ``open the door'' when that's the
obvious thing to do.  However, in cases where you don't want a door
to be automatically opened, you can simply add {\tt noAutoOpen = true}
to the {\tt doorway} object's properties; this requires the player
to explicitly open the door, even when it's not locked.

^^{keys}
^^{locked doors}
If you want to require a key to lock and unlock the door, use
the {\tt lockableDoorway} class instead of {\tt doorway}.  Then,
add the {\tt mykey} property, setting it to the object which
serves as the key (this object should be of class {\tt keyItem}).
If you don't set the {\tt mykey} property, the door can be locked
and unlocked without any key; that is, the player simply types
``lock door'' and ``unlock door'' to lock and unlock it.

Note that there's no requirement that the two sides of a door
be identical.  You can take advantage of this in certain
situations.  For example, if you want to have a door that needs
a key from the outside, but doesn't need any key from the inside
(like most doors you'd find in a house), make both sides
{\tt lockableDoorway} objects, but only set the {\tt mykey}
property in the object that serves as the outer side of the door.


\majorsection{Vehicles}

^^{vehicles}
Vehicles are rooms that move.  Basically, a vehicle is an extension
to the idea of complex room interconnections; rather than having a
room interconnection that merely displays a message, or sometimes
disallows travel, a vehicle has a room interconnection that moves
around.  Usually, a vehicle will also have some other behavior,
such as controls that operate the vehicle, or a viewscreen that
shows the area outside the vehicle.

The first type of vehicle we'll address is the fully-enclosed
variety, such as a subway train or an elevator.  We'll address
vehicles that are also objects in their own right, such as rubber
rafts, in a later section (``Nested Room Vehicles'' below).  A
fully-enclosed vehicle really just amounts to a room that has
a direction property that changes, depending on where the vehicle
is supposed to be.  The vehicle doesn't have a {\tt location}
property, because it's just an ordinary room---it's not an object
in other rooms.

As an example, let's implement an elevator that travels between
two floors.  The elevator will have ``up'' and ``down'' buttons
to activate it, doors, and a display indicating which level it's
on.

To make the elevator more realistic, we'll arrange for it to spend
a couple of turns in transit as it moves between floors.  To
do this, we'll use a ``notifier'' function that's set when a
button is used to start the elevator on its way.  We'll also
use a flag to indicate when the elevator is in transit, so that
pushing the buttons while the elevator is moving will have no effect.

The doors will be simpler than normal doors, since the user can't
directly manipulate them.  They'll open and close automatically.

{\example
\object{elevDoors: fixeditem}
    location = elevRoom
    noun = 'door' 'doors'
    adjective = 'elevator' 'sliding'
    sdesc = "elevator doors"
    ldesc = "They're a standard pair of sliding elevator doors.
       They're currently <<self.isopen ? "open" : "closed">>. "
    isopen = true
    verDoOpen(actor) =
    \lb
        "The doors are automatic; you can't open them yourself. ";
    \rb
    verDoClose(actor) =
    \lb
        "The doors are automatic; you can't close them yourself. ";
    \rb
\endobj
}

The ^{buttons} are fairly simple:  they use the basic {\tt buttonitem}
class.  We'll set a property indicating the destination of the
elevator when that button is pushed---the ``up'' button will
take the elevator to the upper floor, and the ``down'' button
will take the elevator to the lower floor.
The {\tt doPush} method checks to see if the elevator is
already moving, and then to see if the elevator is already at
the destination of this button.

To make less work for ourselves, we'll define a class for the
two elevator buttons, then make two objects based on the class.

{\example
\object{class elevButton: buttonitem}
    location = elevRoom
    doPush(actor) =
    \lb
        /* ignore if already in motion or already at destination */
        if (elevRoom.isActive or elevRoom.curfloor = self.destination)
        \invislb
            "\char`\\"Click.\char`\\"";
        \invisrb
        else
        \lb
            "The doors close, and the elevator starts to move.";
            elevRoom.isActive := true;
            elevDoors.isopen := nil;
            elevRoom.counter := 0;
            elevRoom.curfloor := self.destination;
            notify(elevRoom, \&moveDaemon, 0);
        \rb
    \rb
\endobj

\vskip12pt
\object{elevUpButton: elevButton}
    sdesc = "up button"
    adjective = 'up'
    destination = floor2 \hfill /* where to go when button is pushed */
\endobj

\object{elevDownButton: elevButton}
    sdesc = "down button"
    adjective = 'down'
    destination = floor1
\endobj
}

^^{notify}
^^{daemons}
You'll recall that {\tt notify} is a built-in function that sets up
a ``daemon,'' which is a routine that you want the system to call
after each turn.  The call to {\tt notify} in the example above
tells the TADS run-time system to call the method {\tt moveDaemon}
in the object {\tt elevRoom} after every turn (the third argument
is {\tt 0}, which means that the method should be called after
each turn; if it had been non-zero, it would have specified a
number of turns to wait before calling the method once).

The elevator object needs to be sensitive to its current location
with its {\tt exit} and {\tt north} parameter.  It also needs to be
sensitive to the state of the doors.  The daemon (that is started with
the {\tt notify} call in the buttons) controls the travel of the
elevator.

{\example
\object{elevRoom: room}
    sdesc = "Elevator"
    ldesc = "You're inside a small elevator.  Sliding doors (which are
       currently <<elevDoors.isopen ? "open" : "closed">>) lead north."
    out = (self.north)
    north =
    \lb
        if (elevDoors.isopen) return(self.curfloor);
        else
        \lb
            "The doors are closed.";
            return(nil);
        \rb
    \rb
    curfloor = floor1 \hfill /* start off on lower floor */
    moveDaemon =
    \lb
        self.counter++;
        switch(self.counter)
        \lb
        \hskip-1em case 1:
        \hskip-1em case 2:
        \hskip-1em case 3:
            "\char`\\bThe elevator continues to travel slowly.";
            break;

        \vskip12pt
        \hskip-1em case 4:
	    "\char`\\bThe elevator stops, you hear a bell make a
            \char`\\"ding\char`\\" sound, and the doors slide open. ";
	    elevDoors.isopen := true;
	    self.isActive := nil;
	    unnotify(elevRoom, \&moveDaemon);
	    break;
        \rb
    \rb
\endobj
}

The ``{\tt \char`\\b}'' sequences before the messages in the
{\tt moveDaemon} method are there because the routine runs as
a daemon.  This means it's hard to predict exactly what will have
been displayed just prior to the method's execution, so it's safest
to put in a blank line to make sure that the daemon's messages
are set apart from the previous message.  You should generally
put in a blank line before the first message displayed by any
daemon.

Note that you'll have to do a small amount of extra work to
finish this example.  First, you'll have to define the
rooms {\tt floor1} and {\tt floor2}.  These are extremely simple
if the only way to reach them is through the elevator, because
you don't have to worry about whether the elevator is present or
not.  If you want to be more sophisticated, and make the floors
reachable without taking the elevator (for example, with a stairway),
you'll have to do two things:  first, you'll have to put some
doors on each floor, and make sure they're closed while the
elevator isn't on that floor; and second, you'll probably want
to make some way to call the elevator to the current floor.

Here's a general class for making the doors on each floor.  We'll
make the door's {\tt isopen} property simply check to see if
the inner doors are open, and if so, whether the elevator is
on the current floor (which is the door's location).  We'll do
the same thing for the {\tt verDoOpen} and {\tt verDoClose}
methods that we did with the {\tt elevDoors} object.

{\example
\object{class outerElevDoor: fixeditem}
    sdesc = "elevator door"
    ldesc = "They're <<self.isopen ? "open" : "closed">>. "
    isopen = (elevDoors.isopen and elevRoom.curfloor = self.location)
    noun = 'door' 'doors'
    adjective = 'elevator'
    verDoOpen(actor) =
    \lb
        "The doors are automatic; you can't open them yourself. ";
    \rb
    verDoClose(actor) =
    \lb
        "The doors are automatic; you can't close them yourself. ";
    \rb
\endobj
}

Now you just need to add an object of this class for each floor; the
only thing you need to set in each object is its {\tt location}
property.  Then, on each floor, make the {\tt south} property sensitive
to the state of the doors, in the same way as the {\tt north} property in
the {\tt elevRoom} object is sensitive to the state of the inner doors.

The other addition you'll want to make is to add a call button
on each floor.  This is probably accomplished most easily by simply
using the existing daemon that runs the elevator, and adding new
buttons on the floors, outside the elevator, that activate the
elevator.  Here's a basic class that can be used to make these
buttons.

{\example
\object{class outerButton: buttonitem}
    sdesc = "elevator call button"
    adjective = 'elevator' 'call'
    doPush(actor) =
    \lb
    	/* ignore if elevator is already here */
        if (elevRoom.curfloor = self.location and elevDoors.isopen)
        \invislb
            "Your incredible powers of observation suddenly inform
            you that the elevator is already here. ";
        \invisrb
        else
        \lb
            "\char`\\"Click.\char`\\"";
            if (not elevRoom.isActive) notify(elevRoom, \&moveDaemon, 0);
            elevRoom.isActive := true;
            elevDoors.isopen := nil;
            elevRoom.counter := 0;
            elevRoom.curfloor := self.location;
        \rb
    \rb
}

You'll notice if you try to run this example that there's a minor
problem:  the daemon that moves the elevator around displays messages
as though the player were on the elevator.  By using the same daemon
to move the elevator around with the call buttons, we'll have to
change the messages so that they're sensitive to whether the
player is on the elevator or not.  For motion messages, the
player should only get the messages if the player is on the
elevator.  Messages concerning the doors opening, on the other
hand, should be seen if the player is either on the elevator or
at the location at which the elevator is arriving.  The new
{\tt moveDaemon} method (for the {\tt elevRoom} object) is shown
below (the rest of the {\tt elevRoom} object stays the same).

{\example
    moveDaemon =
    \lb
        self.counter++;
        switch(self.counter)
        \lb
        \hskip-1em case 1:
        \hskip-1em case 2:
        \hskip-1em case 3:
            if (Me.location = self)
            \invislb
                "\char`\\bThe elevator continues to travel slowly.";
            \invisrb
            break;

        \vskip12pt
        \hskip-1em case 4:
            if (Me.location = self)
            \invislb
 	        "\char`\\bThe elevator stops, you hear a bell make a
                \char`\\"ding\char`\\" sound, and the doors slide open. ";
            \invisrb
            else if (Me.location = self.curfloor)
            \invislb
                "\char`\\bYou hear a bell make a \char`\\"ding\char`\\" sound,
                and the elevator doors slide open. ";
            \invisrb
	    elevDoors.isopen := true;
	    self.isActive := nil;
	    unnotify(elevRoom, \&moveDaemon);
	    break;
        \rb
    \rb
}

There are many more enhancements that you could make to the elevator
control daemon to make the elevator more realistic.  First, it would
be fairly easy to add more floors, simply by adding more buttons.
Second, it would be nice to provide some sort of indication of
where the elevator is currently.  In addition, real elevators
use a somewhat different control algorithm.  A real elevator usually
has a current direction; it travels in that direction, stopping at
each floor that's been selected with a call button at the floor or
with the matching button in the elevator.  When it gets to the top
or bottom floor, all of the selected buttons inside the elevator are
cleared (i.e., de-selected).  The elevator then reverses direction
if necessary, servicing called floors.  This type of algorithm would
be a little more work to implement, and is left as an exercise to
the reader.


\majorsection{Chairs and other Nested Rooms}

A ``^{nested room}'' is a room inside another room.  In most cases,
nested rooms are also objects in their own right.  The principal
feature of a nested room is that it isn't enclosed.  Typical
examples of nested rooms are chairs and beds:  these are objects
within a room, but they're also ``rooms'' in the sense that the
player can be located in them.

Implementing a nested room in TADS is fairly simple, because
{\tt adv.t} defines a class called {\tt nestedroom} that does
most of the work.  Furthermore, the classes {\tt chairitem}
and {\tt beditem} let you implement the most common nested
room objects with very little work.

One feature of nested rooms that is worth mentioning is that
the player can not necessarily reach all of the objects in
the enclosing room from a nested room.  By default, {\it none}
of the objects in the enclosing room are reachable from a
nested room.  However, you can easily make any objects
reachable from the nested room by setting its {\tt reachable}
property to a list containing all the reachable objects in
the enclosing room.  If you want to set it to everything in
the enclosing room, simply set it as follows:

{\example
reachable = (self.location.contents)
}

This says that everything contained in the enclosing room is
reachable.


\majorsection{Nested Room Vehicles}

^^{vehicles}
Sometimes, you'll want to make a nested room that's also a vehicle.
For example, you might want to implement a rubber raft that the
player can carry around, but also inflate and use as a vehicle.
Fortunately, there's an {\tt adv.t} class called {\tt vehicle}
that serves this function.  The {\tt vehicle} class is a carryable
object that can be boarded as a vehicle.

Let's implement an inflatable rubber raft.  The first thing we
need to do is build a standard {\tt vehicle} object, but we'll
customize it so that it can only be boarded when it's inflated.
We'll furthermore make it so that it can only be inflated when
it's not being carried, and we'll make it impossible to take
when it's inflated.

{\example
\object{raft: vehicle}
    location = startroom
    sdesc = "inflatable rubber raft"
    noun = 'raft'
    adjective = 'inflatable' 'rubber'
    isinflated = nil
    ldesc = "It's an inflatable rubber raft.  Currently,
             it's <<self.isinflated ? "inflated" : "not inflated">>. "
    verDoTake(actor) =
    \lb
        if (self.isinflated) "You'll have to deflate it first. ";
        else pass doTake;
    \rb
    verDoInflateWith(actor, iobj) =
    \lb
        if (self.isinflated) "It's already inflated!";
    \rb
    doInflateWith(actor, iobj) =
    \lb
        if (self.isIn(actor)) "You'll have to drop it first. ";
        else
        \lb
            "With some work, you manage to inflate the raft. ";
            self.isinflated := true;
        \rb
    \rb
    verDoDeflate(actor) =
    \lb
        if (not self.isinflated) "It's as deflated as it can get.";
    \rb
    doDeflate(actor) =
    \lb
        "You let the air out, and the raft collapses to a
        compact pile of rubber.";
        self.isinflated := nil;
    \rb
    doBoard(actor) =
    \lb
        if (self.isinflated) pass doBoard;
        else "You'll have to inflate it first.";
    \rb
\endobj
}

Note that we'll have to define a couple of verbs, and it would
be convenient to add an object for the pump as well.

{\example
\object{inflateVerb: deepverb}
    sdesc = "inflate" 
    verb = 'inflate' 'blow up'
    ioAction(withPrep) = 'InflateWith'
    prepDefault = withPrep
\endobj

\vskip12pt
\object{deflateVerb: deepverb}
    sdesc = "deflate"
    verb = 'deflate'
    doAction = 'Deflate'
\endobj

\vskip12pt
\object{pump: item}
    sdesc = "pump"
    location = startroom
    noun = 'pump'
    verIoInflateWith(actor) = \char`\{ \char`\}
    ioInflateWith(actor, dobj) =
    \lb
        dobj.doInflateWith(actor, self);
    \rb
\endobj
}

Once all of this is done, making the raft move around is very
similar to making a fully-enclosed vehicle move around.  The only
real difference is that the raft will always have a location.
As with most vehicles of this type, you'll want to make it
impossible for the player to get out of the raft while it's
in motion.  To do this, you'll need to override the
{\tt doUnboard} and {\tt out} methods.  For this example,
let's assume that the river (where the raft will travel) will
be composed of a series of rooms, each of which is of class
{\tt riverRoom}.  So, whenever the raft is in a {\tt riverRoom},
we'll prevent the player from getting out.  To do this, we'll
add the methods below to the {\tt raft} object.  Note that
these methods will inherit the corresponding {\tt vehicle} methods
when the raft is not floating down the river.

{\example
doUnboard(actor) =
\lb
    if (isclass(self.location, riverRoom))
    \invislb
        "Please keep your hands and arms inside the raft
         at all times while the raft is in motion.";
    \invisrb
    else pass doUnboard;
\rb
out =
\lb
    if (isclass(self.location, riverRoom))
    \invislb
        "You can't get out until you've landed the raft.";
        return(nil);
    \invisrb
    else pass out;
\rb
}

The only thing left is to move the raft around.  For this example,
we'll set things up as follows:  each (non-river) room which
borders on a river will have a property, {\tt toRiver}, set to
point to the bordering river room.  Each river room which has a
landing will have a property, {\tt toLand}, set to point to the
bordering non-river room.  Each river room will also have a
property, {\tt downRiver}, set to the next river room downstream
from the current room.  We'll introduce two new verbs:  ``launch''
and ``land.''  When the player is in the raft, ``launch raft''
will set the raft in motion down the river (if it's on land),
and ``land raft'' will land (if there's a landing nearby).
The new verbs are below.

{\example
\object{launchVerb: deepverb}
    sdesc = "launch"
    verb = 'launch'
    doAction = 'Lanuch'
\endobj

\vskip12pt
\object{landVerb: deepverb}
    sdesc = "land"
    verb = 'land'
    doAction = 'Land'
\endobj
}

Now we'll add some more methods to the {\tt raft} object:  the
verb handling methods for the new verbs, and a daemon that causes
the raft to drift downstream while it's in the river.

{\example
verDoLaunch(actor) = \char`\{ \char`\}
doLaunch(actor) =
\lb
    if (isclass(self.location, riverRoom))
    \invislb
        "You're already afloat, if you didn't notice.";
    \invisrb
    else if (self.location.toRiver = nil)
    \invislb
        "There doesn't appear to be a suitable waterway here.";
    \invisrb
    else if (Me.location != self)
    \invislb
        "You'll have to get in the raft first.";
    \invisrb
    else
    \lb
        "The raft drifts gently out into the river.";
        notify(self, \&moveDaemon, 0);
        self.counter := 0;
        self.moveInto(self.location.toRiver);
    \rb
\rb
verDoLand(actor) = \char`\{ \char`\}
doLand(actor) =
\lb
    if (not isclass(self.location, riverRoom))
    \invislb
        "You're already fully landed.";
    \invisrb
    else if (self.location.toLand = nil)
    \invislb
        "There's no suitable landing here.";
    \invisrb
    else
    \lb
        "You steer the raft up onto the shore.";
        unnotify(self, \&moveDaemon);
        self.moveInto(self.location.toLand);
    \rb
\rb
moveDaemon =
\lb
    "\char`\\bThe raft continues to float downstream.";
    self.counter++;
    if (self.counter > 1)
    \lb
        self.counter := 0;
        if (self.location.downRiver = nil)
        \lb
             "The raft comes to the end of the river, and lands.";
             self.moveInfo(self.location.toLand);
             unnotify(self, \&moveDaemon);
        \rb
        else
        \lb
            self.moveInto(self.location.downRiver);
            self.location.riverDesc;
        \rb
    \rb
\rb
}

Note that we'll expect each river room to have a property,
{\tt riverDesc}, which displays a message when the raft drifts
into that room.  The {\tt moveDaemon} method will keep the raft
in each river room for two turns, then move the raft to the
next river room, calling {\tt riverDesc} to note the entry.
When the raft comes to the end of the river, the method will
automatically land the raft; this means that the last river
room must have a non-nil {\tt toLand} property.  You could
alternatively put in a waterfall or other special effect
when reaching the end of the river.

To build a river, all you have to do is define a series
of rooms of class {\tt riverRoom}, and point the {\tt downRiver}
property in each to the next room downriver.  Landings are
built by setting the {\tt toRiver} and {\tt toLand} properties
of the landing room and corresponding river room, respectively,
to point to each other.


\majorsection{Hiding Objects}

Hiding objects is fairly simple, thanks to a set of classes defined
in {\tt adv.t} that make certain kinds of hiding automatic.

The {\tt adv.t} classes make it possible to hide objects under
or behind other objects, and to set up an object so that its
contents are found only when the object is searched.  The
basic hider classes are {\tt underHider}, {\tt behindHider}, and
{\tt searchHider}.  These classes are for the objects doing
the hiding; the hidden objects are hidden inside these objects.
For the objects being hidden, instead of setting their {\tt location}
properties, set the properties {\tt underLoc}, {\tt behindLoc},
or {\tt searchLoc} to point to the respective classes.  All
hidden objects must be of class {\tt hiddenItem}.

For example, to hide a key under a bed, make the bed an
{\tt underHider} object, and set the {\tt underLoc} property
of the key to point to the bed.

{\example
\object{bed: beditem, underHider}
    noun = 'bed'
    location = startroom
    sdesc = "bed"
\endobj

\vskip12pt
\object{key: item, hiddenItem}
    noun = 'key'
    sdesc = "key"
    underLoc = bed
\endobj
}

The {\tt behindHider} and {\tt searchHider} objects work the
same way, but you should use the {\tt behindLoc} and {\tt searchLoc}
properties, respectively, instead of {\tt underLoc}.

Note that the {\tt initSearch} function, defined in {\tt adv.t},
must be called during initialization (usually, by your
{\tt preinit} function) to set up the hidden objects.  This
routine sets up special contents lists for the various {\tt hider}
objects.  This routine only considers hidden objects when they're
of class {\tt hiddenItem}, which is why you must use this class
for all of your hidden objects.

Note that the ease with which you can hide objects shouldn't
be taken as a license to hide objects with wild abandon.  From
a game design viewpoint, hidden objects are almost always poor
puzzles.  If you do hide an object, make sure that you provide
some sort of clue that something is hidden there.  Otherwise,
the player must tediously look behind and under every object
in the game.  As with most puzzles, it's easy to make a really
hard game---it's harder to make a fun game.


\majorsection{Collections of Objects}

^^{collections of object}
^^{object collections}
Each item that a player can manipulate in a TADS game generally
corresponds to an object defined in the game program.  To allow
the player to refer to each object individually, your game program
must define a unique set of vocabulary words for each object.
For example, if you have two books in your game, the books must
have at least one distinct adjective each, so that the player can
specify in a command which book he wants to manipulate.

This style of game construction, in which each object must have
a unique set of vocabulary, imposes certain limitations when you
design your game.  Sometimes you will find that you want to have
a large collection of similar objects that are not distinguished
from one another.  Fortunately, there are a number of ways to implement
object collections; the particular mechanism you choose will vary,
and depends on the type of situation you want to create.  This section
provides examples of several different effects you can create with
collections of objects.

\minorsection{Object Collections as Decorations}

Suppose you are creating a book store.
You will want to stock the store with a large number of books---but
you won't want to create a huge number of individual book objects,
not only because it would take too much time and effort, but also
because most of the books won't be relevant to the game.  There
will usually be one or two important books, and many unimportant ones.

In this type of situation, one way to work within the limitations
of TADS is to create a single object representing the collection
of unimportant books.  Since most of the books are not important,
they can be grouped together into this single object, which is set
up so that it tells the player about its lack of importance whenever
the player tries to manipulate it.  Furthermore, it can serve as
a pointer to any important similar objects.

As an example, we'll implement a collection of books in a book store.
A single object will represent the collection of unimportant books.
When the player looks at the collection of books, though, it will
point out the presence of a couple of important books.

{\example
\object{manybooks: fixeditem}
    sdesc = "books"
    adesc = "a number of books"
    noun = 'book' 'books'
    location = bookstore
    ldesc =
    \lb
        "The bookstore has a huge stock of books, ranging from
        scientific titles to the latest collection of
        \char`\\"Wytcome and Wyse\char`\\" cartoons. ";
        if (not self.isseen)
        \lb
            "One title in particular catches your attention:
            \char`\\"Liquid Nitrogen:  Our Frigid Friend\char`\\". ";
            ln2book.moveInto(bookstore);
            self.isseen := true;
        \rb
    \rb
    verDoRead(actor) =
    \lb
        "Although you'd really like to sit down and start reading,
        you know from painful experience that the neo-fascist staff
        have been cracking down on browsing lately. ";
    \rb
    verDoTake(actor) =
    \lb
        "Unfortunately, you know you could never afford all of these
        books. ";
    \rb
\endobj
}

Note that we set the {\tt adesc} property to a non-default value.  This
is because the default {\tt adesc}, which would display ``a'' followed
by the object's {\tt sdesc}, would result in the strange message
``a books'' in this case.

To help the player avoid wasting a lot of time trying to manipulate
the books, we put in some special messages that let the player know
that the books can't be manipulated.  When you create a decoration
object, it's always a good idea to think of all of the things that
the player might want to do with it, and provide messages that make
it clear that the object isn't important to the game.  In this case,
we've made it clear that the collection of books can't be read or
taken.

The most important definition, though, is the {\tt ldesc} property.
This property not only provides a general description of the collection
of books, but also points out the single important book to the player
when the collection of books is examined for the first time.  (The
{\tt isseen} property is used to determine if the {\tt ldesc} has
been displayed before.  The first time the {\tt ldesc} property is
executed, {\tt isseen} is set to {\tt true}.)  In addition to displaying
a message for the player about the special book, it moves the special
book into the bookstore.

\minorsection{Selecting from an Object Collection}

One way you may wish to extend this basic idea is to provide a
collection of books from which the player can take a single book,
seemingly at random.  The first time a player takes a book from
the collection, he gets one book; the next time, he gets another
book; and so forth.  This is easy to implement by overriding the
{\tt doTake} method of the collection of books:  rather than moving
the collection itself into the player's inventory, the method will
choose a book from a list of individual book objects, and move that
book into the player's inventory.

{\example
\object{manybooks2: fixeditem}
    sdesc = "books"
    adesc = "a number of books"
    noun = 'book' 'books'
    location = bookstore
    ldesc =
    \lb
        "The bookstore has a huge stock of books, ranging from
        scientific titles to the latest collection of
        \char`\\"Wytcome and Wyse\char`\\" cartoons.  You
        may want to just try picking up a book. ";
    \rb
    verDoTake(actor) =
    \lb
        if (length(self.booklist) = 0)
        \invislb
            "You don't see anything else you're interested in. ";
        \invisrb
    \rb
    doTake(actor) =
    \lb
        local selection;

        \vskip12pt
        selection = self.booklist[1];
        self.booklist := cdr(self.booklist);
        selection.moveInto(actor);
   
        \vskip12pt
        "You look through the books, and choose << selection.thedesc >>. ";
    \rb
    booklist = [ln2book chembook cartoonbook]
\endobj
}

The property {\tt booklist} contains a list of books that we have
pre-defined.  Each time the player takes a book, the {\tt doTake}
method will take the first element out of the {\tt booklist} property,
and act as though the player had picked up that object.  This way, the player
can just type ``take book,'' and the game will seem to select a
book for the player.  The {\tt verDoTake} property makes sure
there's something left in the list, and displays an appropriate
message if not.  Note that we've written the {\tt ldesc}
message so that it's clear to the player that he should attempt
to take a book.

\minorsection{Selecting from Indistinguishable Objects}

Another variation on this theme involves a collection of
indistinguishable objects.  For example, suppose we want to
implement a matchbook which contains a number of matches.  The
player should be able to take a match out of the matchbook in
order to light it.

Since TADS requires every object to have unique vocabulary,
though, we'll have to impose some limits on what the player can
do.  In particular, we'll only allow the player to have a single
match at any given time---all of the other matches must be in
the matchbook.

In terms of implementation, this means that we'll only need a single
object to represent a match.  This object will have two possible
states:  existent or non-existent.  We'll use the {\tt location}
property to indicate the state; if the {\tt location} is {\tt nil},
the object is non-existent, otherwise it's in the game.  When the
match is non-existent, the player can take a match out of the
matchbook---which brings the match into existence by moving it
into the player's inventory.  As long as the match is in the game,
the player cannot take another match out of the matchbook.  Once
the match is burned away, though, it will be removed from the
game, and the player can take another match.

The matchbook will have a count of available matches.  When
the player attempts to take a match, we'll first check to see
if there are any matches left; if so, we'll check to make sure
that the match object doesn't already exist in the game.

{\example
\object{matchbook: item}
    location = bookstore
    noun = 'matchbook'
    sdesc = "matchbook"
    matchcount = 4  \hfill /* number of matches left in matchbook */
    ldesc =
    \lb
        if (self.matchcount > 0)
        \invislb
            "The matchbook contains << self.matchcount >> match<<
             self.matchcount = 1 ? "." : "es. " >>";
        \invisrb
        else
        \invislb
            "The matchbook is empty.";
        \invisrb
    \rb
\endobj

\vskip12pt
\object{match: item}
    noun = 'match'
    adjective = 'single'
    sdesc = "single match"
    ldesc =
    \lb
        if (self.isBurning)
        \invislb
            "It's currently lit.";
        \invisrb
        else
        \invislb
            "It's an ordinary safety match.";
        \invisrb
    \rb
    verDoLight(actor) =
    \lb
        if (self.isBurning) "It's already lit!";
    \rb
    burnFuse =
    \lb
        "\char`\\bThe match burns down.  You drop it, and it disappears
        in a cloud of ash.";
        self.isBurning := nil;
        self.moveInto(nil); \hfill /* match is now non-existent */
    \rb
    doLight(actor) =
    \lb
        "The match starts burning.";
        self.isBurning := true;
        notify(self, \&burnFuse, 2);
    \rb
\endobj

\vskip12pt
\object{fakematch: fixeditem}
    noun = 'match'
    adjective = 'bound'
    sdesc = "bound match"
    location = matchbook
    verDoTake(actor) =
    \lb
        if (matchbook.matchcount = 0)
        \invislb
            "The matchbook is empty.";
        \invisrb
        else if (match.location <> nil)
        \invislb
            "You'll have to use the one you already took first.";
        \invisrb
    \rb
    verDoLight(actor) =
    \lb
        "You'll have to remove it from the matchbook first.";
    \rb
    doTake(actor) =
    \lb
        "You tear a match out of the matchbook.";
        match.moveInto(actor);  \hfill /* move a match into inventory */
        matchbook.matchcount--; \hfill /* one less match */
    \rb
\endobj
}

The {\tt fakematch} object lets the player refer to the match in
a command, even when the {\tt match} object doesn't exist in the
game (i.e., its {\tt location} is {\tt nil}).  The {\tt fakematch}
has {\tt verDoTake} and {\tt doTake} methods that let the player
take a match out of the matchbook.

Note one minor flaw in this implementation:  the player might not
have the match, but still can't take another until the first is
destroyed.  This could be confusing.  For example, the player
may take a match, then later drop it because his hands are full,
then move to a different room.  As far as the player is concerned,
no match is present---he left the match in another room, and may
not even remember where.  However, he won't be able to take another
match, because the first match is still in the game.  There's no
easy way to fix this.  You could arbitrarily move the match into
the player's inventory, even though it is in some other room; this
might be less confusing for some players, but it may be more confusing
for others who are aware that there is a match in another room---this
``other'' match would be gone after the single match object is moved
into the player's inventory.

\minorsection{Money}

Money does not fit very naturally into a TADS game for a number
of reasons.  If you do want to use money in your game, it can
generally be implemented using techniques similar
to those we used for the matches in the matchbook.  The only changes
for money are that some new verbs will probably be added, and you will
probably want to be able to add more money to the player's holdings.

The necessary new verbs will depend on your game.  For example, you may
want ``pay'' and ``buy'' verbs for purchasing items.  The main reason
you'll want to use these new verbs is that there's no easy way
for the player to refer to an amount of money.  So, you'll have
to arrange a protocol for purchasing objects.  For example, make
a location that serves as a store, and make a shopkeeper actor.
To purchase an object, the player tells the shopkeeper to give
him an object.  The shopkeeper responds by telling the player the
price of the object.  The player responds with ``pay shopkeeper'';
this deducts the price of the object, and gives the player the
object.

If your game contains a non-obvious protocol like this, it is
very important that you document it for the player.  You can
provide an explanation in the instructions for your game, but
since most people will start playing your game immediately
without reading any accompanying instructions, it would be
far better to put the explanation into the game itself.  There
are a number of ways you could do this; you could, for example,
simply display an explanation of the necessary commands when
the player first enters the shop.  Alternatively, you could
provide instructions as error messages; when the player attempts
to take an object in the shop, you could display a message:
``Please direct your inquiries to the shopkeeper; for example,
type `shopkeeper, give me the axe.'\thinspace ''

An even better approach would be to avoid situations like this
altogether.  If you can't make it fit easily and naturally into
your game without resorting to special sequences of commands,
you should probably find another way to accomplish the same
thing.  Remember, absolute realism is not important in a text
adventure game---the only important thing is to make the game
fun to play.


\majorsection{Non-Player Characters}

^^{actors}
^^{characters}
^^{non-player characters}
One of the most effective techniques for giving a game depth and
making it involving is to include non-player characters.  Early
adventure games often seemed empty and static; the player just
wandered alone through a vast maze of caves.  More modern games
use non-player characters (which we'll refer to as ``actors''
from now on), who move through the game, interacting with the
player and doing things on their own.  When done well, actors add
a whole new dimension to a game, and make the setting seem alive
and real.

Actors are one of the most complicated parts of a TADS game,
because they operate on more levels than other objects.  First,
there's player interaction:  an actor normally can interact with
the player, by talking (the player can tell an actor to do things,
and can ask an actor questions), and by direct action (attacking,
for example, or exchanging objects).  There are a great many
ways a player and an actor can interact, and TADS lets you
implement as much or as little interactive capability into an
actor as you wish.  Second, actors often do things on their own.
Sometimes, this means that the actor carries out a pre-scripted
set of actions.  Other times, the actor just reacts to the player;
for example, an actor might spend most of the game following the
player around, making amusing comments from time to time.  Most
actors, though, are combinations of these, reacting to the player
most of the time, and carrying out pre-scripted actions in response
to certain events.  Third, some actors have a ``memory,'' and
have knowledge of various parts of the game.  Certain things
an actor does may depend on what a player has done previously.

Fortunately, all of these basic elements of an actor are
implemented pretty easily.  You can write the code for a simple
actor without too much difficulty.  Even better, once you have
a simple actor working, fleshing out the actor is just a
matter of adding to the basic framework of the actor.

To implement an actor, you need to write two main pieces of
code that aren't associated with most other types of objects.
First is the {\tt actorAction} method.  This method handles
most of the actor's interaction with the player; it essentially
defines the limits of the actor's ability to interact.  Second is
the actor's ``daemon.''  You'll recall from previous chapters that
a daemon is a function or method that's called after every turn
(that is, the run-time system calls a daemon each time it finishes
processing a player command).  The actor daemon is what allows the
actor to go about its business; since it is called after each turn,
the actor can perform an action of its own on each turn.

If you've played {\it Ditch Day Drifter,} you'll recall a couple
of actors in that game of differing degrees of interaction and
activity.  One fairly limited actor is the guard in front of the
steam tunnels:  the guard does very little apart from blocking your
way into the tunnel.  The guard's interaction consists mostly of
accepting objects; you can attempt to bribe the guard with money,
for example, or give him something to drink.  The guard's daemon
also does very little; it just displays a randomly-chosen message
(from a set of several pre-determined messages) on each turn.
A more versatile actor is Lloyd, the insurance robot.  Lloyd
can accept objects (you can buy the insurance policy), and you
can also talk to him to a limited extent (you can ask about the
policy).  Like the guard, Lloyd displays a randomly-chosen message
on most turns.  In addition, Lloyd follows the player from room
to room.  Lloyd even reacts to certain events and places, and has
a memory of sorts:  once Lloyd has paid out an insurance claim,
he will remember not to pay out the claim again.

Let's look at an example of implementing an actor.  We'll start
with a simple ``puzzle actor,'' similar to the guard in {\it Ditch.}
This is a very common element of adventure games:  a person that is
blocking the way to some goal.  In this case, we'll implement a
receptionist who's blocking the way to an office you want to enter.
As long as the receptionist is awake, you won't be allowed to enter
the office.

{\example
\object{receptionist: Actor}
    noun = 'receptionist'
    sdesc = "receptionist"
    isawake = true
    ldesc =
    \lb
        "The receptionist is a very, shall we say, sturdy-looking
        woman, with a tall beehive hair-do and thick glasses.  She
        reminds you of your third-grade teacher, who was overly
        concerned with discipline.";
        if (self.isawake)
        \invislb
            "She eyes you impatiently as she goes through some papers.";
        \invisrb
        else
        \invislb
            "She's slumped over the desk, fast asleep.";
        \invisrb
    \rb
    location = lobby
    actorDesc =
    \lb
        if (self.isawake)
        \invislb
            "A receptionist is sitting at the desk next to the
            door, watching you suspiciously.";
        \invisrb
        else
        \invislb
            "A receptionist is slumped over the desk next to
            the door.";
        \invisrb
    \rb
    actorDaemon =
    \lb
        if (self.location <> Me.location or not self.isawake) return;
        switch(rand(5))
        \lb
        \hskip-1em case 1:
            "The receptionist sharpens some pencils.";
            break;
        \hskip-1em case 2:
            "The receptionist goes through some personal mail, holding
            each letter up to the light and attempting to read the
            contents.";
            break;
        \hskip-1em case 3:
            "The receptionist looks through the personnel files.";
            break;
        \hskip-1em case 4:
            "The receptionist answers the phone and immediately
            puts the caller on hold, cackling to herself fiendishly.";
	    break;
        \hskip-1em case 5:
            "The receptionist shuffles some papers.";
            break;
	\rb
    \rb
\endobj
\vskip12pt
\object{lobby: room}
    enterRoom(actor) =
    \lb
        if (not self.isseen)
        \invislb
            notify(receptionist, \&actorDaemon, 0);
        \invisrb
        pass enterRoom;
    \rb
    sdesc = "Lobby"
    ldesc = "You're in a large lobby, decorated with expensive-looking
             abstract pastel oil paintings and lots of gleaming chrome.
             A large receptionist's desk sits next to a door leading
             into an office to the east.  The exit is to the west."
    west = outsideBuilding
    out = outsideBuilding
    east =
    \lb
        if (receptionist.isawake)
        \lb
            "You start to stroll past the receptionist nonchalantly,
             trying to ignore her as though you actually belong here,
             but she's wise to that trick:  she leaps up, and with
             surprising force throws you back from the doorway.
             Satisfied that you're not going through, she sits back
             down and returns to her work.";
             return(nil);
        \rb
        else
        \invislb
            return(office);
        \invisrb
    \rb
\endobj
}

Notice that the implementation of this puzzle looks very much like
that of a door, or any other obstacle, except that the obstacle itself
is somewhat more complicated.  The actual work of preventing the player
from moving past the receptionist, though, is exactly the same as for
any other obstacle:  in the direction method, we check to see if the
receptionist is still awake, and if so, we refuse to let the player
past by returning {\tt nil} (after displaying an appropriate message,
of course).

The main features that are special for an actor are the {\tt actorDesc}
and {\tt actorDaemon} methods.  The {\tt actorDesc} is a special
property that you should define for any actor; this method is called
by the general {\tt room} code that displays the long description
of the room.  After the description of the room and its contents, the
{\tt room} long description routine will call the {\tt actorDesc}
method of each actor in the room (except the player actor, {\tt Me}).
The {\tt actorDesc} method should simply display an appopriate
message to the effect that the actor is present; it's similar to
the {\tt ldesc} method for the actor, but it usually contains less
detail, since it's more to inform the player that the actor is
present than to provide a detailed desription of the actor.  It
should mention that the actor is present and what the actor is
doing.

The {\tt actorDaemon} is nothing special---you can call this
method whatever you want.  Most actors will have something like
the {\tt actorDaemon}, though.  This is the daemon that makes the
actor seem alive by doing something after every turn.  For our
simple receptionist actor, which doesn't really do anything of
its own accord, this method simply displays a random message
each turn.  Note that the method returns immediately if the
player (the {\tt Me} object) is not present in the same room
as the receptionist; it clearly wouldn't make any sense to
display a message about what the receptionist is doing when
the receptionist is not present.

Note that {\tt actorDaemon} is {\it not} started automatically
for an actor.  Instead, you must explicitly activate it.  In this
case, we've chosen to activate the {\tt actorDaemon} routine by
calling {\tt notify()} the first time the player enters the
lobby.  The {\tt enterRoom} method of the {\tt lobby} object
checks to see if the room has been entered before (by checking
to see if the {\tt isseen} property of the current room has
been set---this is always automatically set by the {\tt room}
code after the room has been seen for the first time); if the
room has never been entered, we call {\tt notify()} to start
{\tt actorDaemon} running.  Note that {\tt enterRoom} finishes
by using {\tt pass} to run the {\tt enterRoom} method that would
normally be inherited from the {\tt room} class.

\minorsection{Following the Player}

There are several main types of wandering actors.  The first
type follows the player around, like Lloyd the insurance
robot in {\it Ditch Day Drifter}.
This first type is fairly simple, because all you have to do is
set up a daemon that checks to see if the actor and the player are
in the same room, and if not, move the actor to the player's location.

The big change between a stationary actor, such as the receptionist
in the earlier example, and a wandering actor is in the actor's
daemon.  Instead of just displaying a random message, as did the
receptionist, the actor daemon will actually move the actor around.

Here's a basic {\tt actorDaemon} method that will make the actor
follow the player.

{\example
actorDaemon =
\lb
    if (self.location = Me.location)
    \lb
        switch(rand(5))
        \lb
        \hskip-1em case 1:
             "Lloyd hums one of his favorite insurance songs.";
             break;
        \hskip-1em /* etc. with other random messages... */
        \rb
    \rb
    else
    \lb
        self.moveInto(Me.location);
        "Lloyd rolls into the room, and checks to make sure
        the area is safe.";
    \rb
\rb
}

Note that this routine must be set up to run every turn with
the {\tt notify()} function, as explained in the receptionist example.

This new version of the method still displays a random message if
the actor is in the same room with the player, but now it has some
new code that is executed when the player is somewhere else.  The
new code moves the actor to the player's location, and displays a
message informing the player that the actor has entered the room.

In a real game, you'll probably further extend this type of daemon
to make the actor do special things in certain locations.  For
example, in {\it Ditch Day Drifter}, there's some extra code that
displays special messages when Lloyd enters certain
improbable locations, such as squeezing through the north-south
crawl or climbing the rope.

\minorsection{Moving on a ``Track''}

The second type of wandering actor moves around on a fixed path,
or ``track.''  This type of actor is not much harder to implement
than an actor which follows the player; rather than using the player's
location in the actor motion daemon, you make a list of locations
that the actor visits in sequence, and the actor motion daemon
takes locations out of this list.

One new factor you'll have to keep in mind in implementing this
type of motion daemon is that the actor could be entering the
actor's location, leaving the actor's location, or neither.  The
motion message will have to check for each of these situations.

For an example, we'll show how to implement a robot similar to
the vacuuming robot in {\it Deep Space Drifter} that moves around
between several rooms in a fixed pattern.

{\example
\object{vacRobot: Actor}
sdesc = "domestic robot"
noun = 'robot'
adjective = 'domestic'
location = stationMain
tracklist = ['southeast' bedroomEast 'west' bathroom 'west'
           \hskip2em bedroomWest 'northeast' stationMain 'north'
           \hskip2em stationKitchen 'south' stationMain]
trackpos = 1
moveCounter = 0
actorDaemon =
\lb
    if (not self.isActive) return;  \hfill /* do nothing if turned off */
    self.moveCounter++;
    if (self.moveCounter = 3)  \hfill /* move after 3 turns in one location */
    \lb
        self.moveCounter := 0;
        if (self.location = Me.location)
        \invislb
            "The robot moves off to the <<self.tracklist[self.trackpos]>>.";
        \invisrb
        self.moveInto(self.tracklist[self.trackpos + 1]);
        if (self.location = Me.location)
        \invislb
            "A domestic robot rolls into the room and starts noisily
             moving around the room vacuuming and dusting.";
        \invisrb

        \vskip12pt
        /* move to next position on track; loop back to start if necessary */
        self.trackpos += 2;
        if (self.trackpos > length(self.tracklist)) self.trackpos := 1;
    \rb
    else
    \lb
         /* we're not moving this turn, so display activity message */
         if (self.location = Me.location)
         \invislb
             "The domestic robot continues darting around the room
              vacuuming.";
         \invisrb
    \rb
\rb
\endobj}

The list of rooms that the robot visits is specified in the
{\tt tracklist} property.  This is a strange-looking list, because
it contains both objects and (single-quoted) strings.  The way this
works is that the list elements are always inspected in pairs:  the
first item in a pair is a string that will be displayed to indicate
the direction that the robot will leave the current room; the second
item in the pair is the room to be visited next after the current
room.  So, the first pair contains the string {\tt 'southeast'} and
the object {\tt bedroomEast}:  the robot will leave to the southeast,
and move to the east bedroom.

The {\tt actorDaemon} still does all the work, but it's a little more
complicated than in the previous examples.  First, it checks to see
if the robot is active; if not, it doesn't go any further.  Next,
it increments a counter that tracks how long the robot has been in
the current room; when it gets to three turns, it's time to move on,
otherwise the robot stays where it is.

If the robot is moving, the daemon first resets the move counter to
zero.  Next, it checks to see if the player is in the current location;
if so, the daemon displays a message telling the player that the robot
is leaving, and indicates which direction (the direction is determined
by looking at the current element of the {\tt tracklist} list, as
described above).  Next, the robot is actually moved to the new
location (again, determined by looking in the {\tt tracklist} list).
Next, if the player is in the {\it new} location, the daemon displays
a message saying that the robot has entered the room.  Finally, we
increment the track position counter property {\tt trackpos} (by 2,
since the list entries are in pairs).  If the position counter has
moved beyond the end of the list, we reset it to 1.

If the robot is not moving during this turn, the daemon will check
to see if the actor is in the same location as the player, and display
a message if so.  Note that the single fixed message in the example
could easily be replaced by a randomly-chosen message, as we did
with the earlier examples.

\minorsection{Random Wandering}

The third type of wandering actor moves through the game at random.
This type of actor wandering is somewhat more complex than the earlier
types.

The main trick in implementing a randomly-wandering actor is that we
must avoid evaluating direction properties when they don't contain
a fixed object.  This is because we could accidentally display spurious
messages, and possibly trigger side effects (such as killing the player
or making other changes to the game state) if we were to evaluate a
direction property that was actually a method.  For example, think
back to the earlier example with the stairway that collapses when
used---you probably wouldn't want to display the collapse message
and destroy the stairs when one of your wandering actors encountered
the room.

The best way to avoid such unintended side effects is to avoid
using any direction property that isn't a simple object.  TADS
provides a special built-in function called {\tt proptype} that
lets you determine the type of a property without actually
evaluating it.  The {\tt proptype} function will tell you whether
a property is a simple object or a method.

Other than the mechanism for choosing a new room, randomly-wandering
actors are essentially the same as actors on a track.  You generate
messages in the same manner, checking to see if the actor is leaving
or entering a room occupied by the player.

{\example
actorDaemon =
\lb
    local dir, dirnum;
    local tries;
    local newloc;

    for (tries := 1 ; tries < 50 ; tries++)
    \lb
        dirnum := rand(6);
        dir := [\&north \&south \&east \&west \&up \&down][dirnum];
        if (proptype(self.location, dir) = 2 /* object */)
        \lb
            newloc := self.location.(dir);
            if (not isclass(newloc, room)) continue;
            if (self.location = Me.location)
            \invislb
                "The robot leaves to the <<
                 ['north' 'south' 'east' 'west' 'up' 'down'][dirnum]>>.";
            \invisrb
            self.moveInto(newloc);
            if (self.location = Me.location)
            \invislb
                 "A domestic robot enters the room and starts vacuuming
                  noisily.";
            \invisrb
        \rb
    \rb
\rb
}

The line that sets the {\tt dir} local variable probably needs a
little explanation, because it's a tricky bit of TADS coding.  The
first thing is a list of property addresses---these aren't properties
of any object, but rather just references to properties that you can
later use to get an actual property of an object.  We take this list,
which consists of six elements, then choose a single element at
random by indexing into the list with a randomly-chosen number
from 1 to 6.  This leaves the {\tt dir} local variable with a
pointer to a property that we can use later.  Note that you could
include northeast, southeast, northwest, and southwest in the
list if you want; we left them out to make the example a little
bit easier to read.

Next, we use the {\tt proptype()} built-in function to determine
if the selected property (contained in {\tt dir}) of the actor's current
location contains an object.  If it does not, we continue looping.

If the selected property of the current location does in fact
contain an object, we first get that location into the local
variable {\tt newloc}.  Then, we make one more check on the new
location:  we ensure that {\tt newloc} actually contains an
object of class {\tt room}.  This is because it is possible
for a room direction connection property to contain an object
of class {\tt obstacle}, such as a door (see the section on
implementing doors earlier in this chapter).  If it's a door
or other obstacle, we want to treat it the same as though the
direction property didn't contain an object at all---so we'll
just continue with the loop, ignoring this choice of a direction.

Once we have an object of class {\tt room} in {\tt newloc},
we proceed to move the actor the
same way we moved the actor on a track.  Note that we use the
same indexing trick to display the direction that the actor is
exiting.

In case you're wondering why the example uses a ``tries'' counter
and stops after 50 times through the loop:  this is simply a bit
of bullet-proofing to keep the daemon from going into an infinite
loop.  It's quite possible, especially while you're developing
your game, that the robot may encounter some rooms from which
there is no escape.  Remember that the robot can only use exits
which are explicitly set to objects; if you create a room with
nothing but exits that are methods, the robot will be unable to
find a suitable exit.  If we didn't include the loop counter and
give up after a while, the loop would run forever in such rooms,
leaving the game stuck.

In a real game, you may want to add some special-case code
that allows a randomly-wandering actor to use certain exits that
would normally be off-limits because they're methods.  One way
of accomplishing this is to add a special check to the daemon
to test if the actor is in such a special location, and if so,
to make it go to a particular destination.  Another way is to
put additional properties in some rooms (such as {\tt robotEast})
that specify directions accessible only to the robot; in the
daemon, you'd check for the presence of these special robot
directions first, falling back on the normal direction list
only if the special directions are not present.

One modification you may wish to make to this type of totally random
wandering is to restrict the actor to a particular set of rooms.  There
are a number of ways to accomplish this.  The simplest is to create a
class of rooms that the actor is allowed to occupy, and always check
upon choosing a new room that the room is a member of the class.

If you want to change the allowed rooms dynamically as the game runs,
use a condition rather than a class.  Instead of checking to see if the
room is a member of a class, check to see if the condition is true.  For
example, if you want to create an actor that only enters dark rooms, you
would check upon choosing a room to make sure the room is not lit.

\minorsection{More Complex Actor Scripts}

For some games, you may wish your actors to exhibit even more complex
autonomous behavior than we've shown so far.  One type of common
extension is to make the actor perform some action when in a certain
room or when a certain set of conditions is met.  For example, you
may wish to implement a thief that steals any treasure left lying
around in a maze room.  To implement this, we'd first need to create
a class of rooms for mazes; let's call this new class {\tt mazeroom}:

{\example
\object{mazeroom: room}
\endobj
}

Similarly, we'll create a class for any object that counts as
a treasure:

{\example
\object{treasure: item}
\endobj
}

Note that the only purpose of these two classes is to serve as a flag,
so we can determine if an object is considered a treasure or if a room
is part of a maze.  These classes don't actually implement any new
behavior of their own.

Next, we'd add a check in the thief's daemon that looks for treasure
objects when the thief is in a room in a maze.  We'll add this code
to the end of the same daemon we used for the randomly wandering
actor above in the previous section; we won't reproduce that entire
routine here.  This code is added after the {\tt for} loop that
comprised the bulk of that daemon.

{\example
if (isclass(self.location, mazeroom))
\lb
    /* check contents of current room */
    local cont := self.location.contents;
    local len  := length(cont);
    local found := nil;
    local i;

    for (i := 1 ; i <= len ; ++i)
    \lb
        if (isclass(cont[i], treasure))
        \lb
	    found := true;
	    cont[i].moveInto(self);
        \rb
    \rb
    if (found and Me.location = self.location)
    \invislb
        "You notice the thief has helped himself to some items.";
    \invisrb
\rb
}

This is a fairly complicated example of making your actors do something
special when certain conditions are met.  A simpler example is Lloyd's
attempt to sell insurance to the sleeping guard in {\it Ditch Day
Drifter}, which is simply a message that's displayed by Lloyd's
movement daemon when Lloyd enters the room with the guard.

You may want some actors to have very complicated scripts that do
more than just move the actors around.  You may want to implement an
actor which goes about his business, doing things in some of the
locations he visits.  The best way to do this is probably by making
your actor a ``state machine.''  One easy way to implement a state
machine is to use a number to represent the current state; on each
turn, you increment the state number, and use a {\tt switch} statement
to take the appropriate action depending on the new state.

For example, suppose you want to implement an actor that enters
a building and walks to the study.  If someone else is in the study,
he'll just wait impatiently for the other person to leave; once he's
alone in the study, he'll produce a key and open a safe, conveniently
dropping the key near the safe.  He'll then take something out of the
safe and leave the building.

We'll represent the set of states with numbers.  State 1 will be
outside the building; when in state 1, we'll enter the building.
State 2 will be inside the front hall, and we'll move to the study.
In state 3 we'll check to see if we're alone; if not, we'll just
stay in state 3 and act impatient.  Once we're alone, we'll
produce the key and open the safe.  In state 4, we'll take the
object out of the safe and exit the room, moving back to the front
hall.  In state 5, we'll leave the building.

The following methods can be used to accomplish this.

{\example
actorMessage(msg) =
\lb
    if (Me.location = self.location)
    \lb
        "\char`\\b";
        say(msg);
    \rb
\rb
actorMove(newloc, todir, fromdir) =
\lb
    if (Me.location = self.location)
    \invislb
        "\char`\^<<self.thedesc>> leaves to the <<self.todir>>. ";
    \invisrb
    self.moveInto(newloc);
    if (Me.location = self.location)
    \invislb
        "\char`\^<<self.thedesc>> enters the room from the <<self.fromdir>>. ";
    \invisrb
\rb
actorState = 1
actorDaemon =
\lb
    switch(self.actorState++)
    \lb
    \hskip-1em case 1:
        self.actorMove(frontHall, 'north', 'south');
    	break;
    \hskip-1em case 2:
        self.actorMove(study, 'east', 'west');
    	break;
    \hskip-1em case 3:
        if (self.location = Me.location and not Me.ishidden)
	\lb
	    "Jack crosses his arms and looks at his watch.";
	    self.actorState := 3;         \hfill /* remain in state 3 */
	\rb
	else
	\lb
	    actorMessage('Jack looks around, and is satisfied that
              he is alone.  He searches his pockets, and produces a
              key, which he inserts into the safe and opens it.  He
              nervously removes something from the safe; he doesn\char`\\'t
	      seem to notice when the key drops to the ground.');
            safeKey.moveInto(self.location);
	\rb
    	break;
    \hskip-1em case 4:
        self.actorMove(frontHall, 'west', 'east');
    	break;
    \hskip-1em case 5:
        self.actorMove(outsideBuilding, 'south', 'north');
    	break;
    \rb
\rb
}

Note that the {\tt actorMove} and {\tt actorMessage} methods that
we implemented provide a convenient mechanism for displaying
messages conditionally if the player is present.

Naturally, you'd want to extend the end of this script to give Jack
something to do (or somewhere to disappear to) at the end of his
appearance.  Using this basic technique, you can implement essentially
arbitrarily complicated actors.  Note that it's easy to jump to a
different state rather than progress to the next state---just assign
the state variable ({\tt actorState} in this case) to the new state
you want to enter on the next turn.  This allows your actor to behave
intelligently:  rather than just following a script without reference
to what's going on in the game, the actor can take different actions
depending on the conditions around him.  In our simple example above,
the only sensitivity to game conditions is that the actor waits to
open the safe until he's alone (or thinks he is); you can easily
test for much more complex conditions, though.

In deciding how to implement your actors, you should consider the
things you actor does most frequently.  If your actor mostly moves
around on a track, and occasionally does something special in a
particular location or when a particular set of conditions is
true, then implementing your actor using the techniques for
moving on a track is easiest; just check for your special
conditions before or after moving.  On the other hand, if your
actor does something special in many locations and on most turns,
using a state machine is easiest.

\minorsection{Talking to Actors}

^^{asking questions (of actors)}
^^{questions (for actors)}
^^{commanding actors}
^^{talking (to actors)}
One of the main areas of interaction with actors in adventure games
is asking the actors questions.  TADS is a bit limited when it comes
to artificial intelligence; unfortunately, TADS doesn't make it possible
to ask actors general questions such as ``what do you think about the
role of the media in the upcoming election?'' or ``why is the sky
blue?''  Instead, questions addressed to actors in a TADS game are
limited to asking about particular objects in the game.  Furthermore,
all the player can do is ask about an object in general---the player
can't ask ``why is the fuse outside the tram?'', but is limited to
``ask lloyd about the fuse.''

Another type of interaction is to tell an actor something.  As with
questions, TADS is too limited to allow a player to tell an actor anything
very specific.  All that a player can say is something like ``tell lloyd
about the scroll.''

As a game designer, these limitations make your job fairly straightforward.
Implementing the ``ask'' and ``tell'' routines is essentially the same
as implementing any other verb method.  Here's an example of a
{\tt doAskAbout} method for an actor.

{\example
verDoAskAbout(actor, iobj) =
\lb
\rb
doAskAbout(actor, iobj) =
\lb
    switch(iobj) \hfill /* indirect object is what is being asked about */
    \lb
    \hskip-1em case insurancePolicy:
        if (insurancePolicy.isbought)
        \invislb
            "\char`\\"It's very complicated, but let me assure you,
            it's a very good policy.\char`\\"";
        \invisrb
        else
        \invislb
            "\char`\\"It's a great policy for only a dollar!\char`\\"";
        \invisrb
        break;
    \hskip-1em default:
        "\char`\\"I don't know much about that.\char`\\"";
    \rb
\rb
}

You could add more cases as needed to add to Lloyd's knowledge.

Note one strange twist here:  the {\tt actor} parameter to the methods
is the actor that asked the question---that is, the player, or {\tt Me}.
The actor who's being asked is {\tt self}, since the actor is the
direct object of the ``ask'' verb.  Note also that we had to implement
an empty {\tt verDoAskAbout} method so that the actor can be asked
questions at all; by default, all objects inherit a generic error
message for {\tt verDoAskAbout}.

You can implement behavior for telling an actor about an object in
much the same way, by writing {\tt verDoTellAbout} and {\tt doTellAbout}
methods for the actor.  Likewise, you can make it possible for the player
to give objects to the actor by implementing {\tt verIoGiveTo}
and {\tt ioGiveTo} methods for the actor.  Note that the {\tt ioGiveTo}
method should move the direct object into the actor's inventory
(by executing {\tt dobj.moveInto(self)}) if the object is accepted.

\minorsection{Taking Objects from Actors}

By default, actors don't let the player take objects that the actor
is carrying.  This is because all of the routines that try to move
an object will call the {\tt verGrab(actor)} method of all of the
containers of the object.  By default, an actor's {\tt verGrab}
method will display a message indicating that the actor won't give
up the object so easily; since {\tt verGrab} is called during the
validation phase of parsing, displaying a message is sufficient
to prevent the command from proceeding.

If you want to allow the player to take objects from an actor,
override the actor's {\tt verGrab} method.  You could make
the method do nothing at all, in which case the actor will allow
any object to be taken; or it can display a message only on certain
objects, which will prevent only those objects from being taken.

\minorsection{Commanding an Actor}

The player can issue a command to an actor by typing something
like ``lloyd, go north.''  When this happens, the TADS parser
processes the command in almost exactly the same way as a normal
command, except that the actor of the command is changed from
the default {\tt Me} (the player's actor) to the actor indicated
in the command.  (In fact, it really is exactly the same
processing; when no actor is typed, it's as though the player
had typed ``me, go north.'')

Since most all processing occurs relative to the {\tt actor} parameter
to the verb-handling methods (such as {\tt verDoTake(actor)} and
{\tt doDrop(actor)}), you don't need to make very many special
provisions for commands directed to actors.  This type of processing
is done almost entirely automatically by the system.

However, in most cases, you don't actually {\it want} the player
to be able to boss your actors around.  Most of your actors will
have a mind of their own, and many will even be presenting obstacles
to the player.

To make it possible for you to control whether the player can
control your actors, and to what extent, the TADS parser will always
call the {\tt actorAction} method of the selected actor prior to
allowing any further processing to proceed.  If this routine
executes an {\tt exit} statement, the command will go
no further, preventing the player from controlling your actor.
Since the method receives all of the information on the current
command (the verb, direct object, preposition, and indirect object)
as parameters, it can choose to allow or disallow particular
commands.  For allowed commands, do nothing; for disallowed
commands, display an appropriate message and then execute an
{\tt exit} statement.

%
% Ideas for additional sections in this chapter...
%
% \majorsection{Implementing a View Screen}
%

\endchapter

At this very moment, we have the necessary techniques, both
material and psychological, to create a full and satisfying
life for everyone.
\author BURRHUS FREDERIC SKINNER, {\sl Walden Two\/} (1948)

\eject

