The TADS Alternate Library
Version 2.0
Floating Objects and Destinations
Copyright 2000 by Kevin Forchione.
This is part of the TADS Alternate Library Authors Manual.
Introduction and Table of Contents
Floating
Class
Floating is a basic mix-in
class that allows an object to assign its location relative to the actor’s
location. Floating class objects have only one location at any given moment,
however their location is dependent upon two factors: the list returned by their
foundIn property and the actor’s location.
A Floating object’s
foundIn property can either be a list
or code that returns a list. The list must either be an empty list or a
list of game objects in which the Floating object is to be found. This list is
then compared with the actor’s location, working outward recursively from the
actor’s location to the actor’s location’s location, etc. This recursive search
through an actor’s nested locations is independent of sense.
In this way a Floating
object will be placed in the closest location to the actor appearing in the
foundIn list; otherwise it will be assigned a location of nil.
Alt Floating Objects appear as close to the actor’s nested location as possible
Once a location has been
determined the Floating object is moved into this location, which means that if
the location is not nil then it appears in the contents list of the location.
Floating object locations
are determined by the moveFloating() function. This function makes its
determination of all Floating object locations at least once per command
execution cycle. To ensure that Floating objects are where they are expected to
be Alt’s moveFloating() function is run during:
·
preinit() to set up
Floating object’s initial location
·
validDoList() and
validIoList()
·
preCommand()
·
Animate.moveInto()
Suppose our game defines a
mist that appears in several locations. The mist is constant from one location
to another, and simply provides an interesting smell.
mist: Floating, Decoration
foundIn =
[forestClearing, stoneChamber]
noun =
'mist'
adjective
= 'low-lying'
sDesc =
"low-lying mist"
initial =
"A low-lying mist hugs the ground about your feet."
smellDesc
= "The mist smells of boiled cabbage."
doSmell(actor) = {self.smellDesc;}
;
Now whenever we enter the
forestClearing or the stoneChamber the mist is there, waiting to be smelled.
Note that we can use initial to
describe the mist because it’s a Decoration and therefore can’t be taken. Since
the object is a decoration, however, we need to define doSmell() as well as
smelldesc in order to be able to smell the mist. This is because Decorations
use dobjGen() and iobjGen() to generate default messages for any actions not
defined directly in the object by verification or action methods. The smelldesc
property isn’t an action method, it’s merely called by doSmell(), so we need to
define doSmell() in this object.
Alt’s Destination class
serves functionally the same purpose as ADV.T’s obstacle class. Destination
class provides a destination() method that determines what happens when an
actor attempts to enter this object. This class generally serves as a bridge
between two locations, with conditions that must be met in order to negotiate
the transfer.
Destination class does not
provide boarding methods, but does provide noExit handling. Most
Destinations are accessed through travel directions properties, such as north,
south, east, and west. Boarding can also be attempted for most Destination
class objects through the <<enter obj>> command. Entering the
object invokes its destination() method, which either transfers the actor to
the location indicated by the leadsTo property or displays an
appropriate message and prevents transfer.
When defining a Destination object it is important to
override the class’ leadsTo property,
which should be assigned a location to which the
Destination leads.
Alt defines the following
classes deriving from Destination
Venture contains a
Passageway in the form of stoneSteps in the forestClearing leading down to a
stoneChamber. The advantage to using a Destination, instead of simply
connecting the forestClearing to the stoneChamber directly by travel direction
properties is that we add depth to the passage from one location to another. In
this case the stoneSteps carry information about the milieu we are in.
stoneSteps: Passageway
location
= forestClearing
noun =
'steps'
adjective
= 'stone'
isThem =
true
sDesc =
"stone steps"
lDesc =
"Ancient steps extend down into shadows. Perhaps these steps
have
have lain untrodden for a thousand years."
leadsTo =
stoneChamber
;
Each instance of Destination
class inherits a hasObstacle() method that determines the conditions under
which the Destination can be successfully negotiated. This method should return
true if passage through the destination is impeded in some way; otherwise it should
return nil.
Use hasObstacle() to indicate whether the actor can successfully negotiate the Destination.
This method should return nil to indicate that progress is unimpeded; otherwise it should display
an appropriate message explaining why passage to the destination failed and return true.
The following is the hasObstructed()
method for Doorways:
hasObstacle = {
if
(self.isOpen)
return nil;
else
if (!self.isLocked && !self.noAutoOpen) {
self.setIsOpen(true);
"(Opening << self.theDesc >>)\n";
return nil;
}
else {
"%You%'ll have to open << self.theDesc >> first.
";
setit(self);
return true;
}
}
Passageways offer no
obstacle to passage, while Doorways must be open, or automatically openable.
Gateways, too, must be raised or automatically raisable. If these conditions
aren’t met then hasObstacle displays a message explaining why passage is denied
and returns true to indicate that the passage is being obstructed.
The stoneSteps define
location as forestClearing and leadsTo as stoneChamber. This makes them
one-direction only. To join and synchronise two Destination class objects you
need to code their otherSide properties to point to each other. For example,
the following two Doorways are synchronised with each other, behaving like a
single door.
hallDoor: Doorway
location
= hallway
noun =
'door'
adjective
= 'hall'
sDesc =
"hall door"
leadsTo =
kitchen
otherSide
= kitchenDoor
;
kitchenDoor: Doorway
location
= kitchen
noun =
'door'
adjective
= 'kitchen'
sDesc =
"kitchen door"
leadsTo =
hallway
otherSide
= hallDoor
;
Each Doorway has a
location property explicitly coded, each has a leadsTo property assigned the
location to which the Doorway leads and each Doorway has an otherSide pointing
to the opposite Doorway. This strategy works very well when you want to define
doors that behave differently on each side, for instance, one side being
lockable, the other not.
Each member of a paired
set of Doorways must define an otherSide attribute that points to the opposite
Doorway.
Like a Floating object, a
FloatingDestination object occupies only one location at any given time, and determines
its location based on the actor’s nested location and its own foundIn property.
It uses information provided by its
foundIn property and its leadsTo property to determine the destination of the
object for any given location it currently occupies.
FloatingDestination class
provides a superclass for Passage, Door, Gate, and Connector classes. These
objects behave much like their Destination counterparts, except that they “float”
with the actor, moving to their locations according to the rules for Floating
objects.
This class allows an
author to define a Door, an object that simulates the behaviour of paired
Doorway objects. For example, the Doorways above could be more compactly
defined using a single Door object:
hallDoor: Door
foundIn =
[ hallway, kitchen ]
sDesc =
"hall door"
noun =
'door'
adjective
= 'hall'
;
With FloatingDestination
class we don’t need to code an otherSide. The object automatically keeps its
other side(s) in synch using the foundIn and leadsTo properties. Coding leadsTo
is also unnecessary in our example. When the Door is found in the hallway, it
leads to the kitchen; when found in the kitchen, it leads to the hallway.
Whenever an object returns only two locations in its foundIn property and these
locations lead to each other then the leadsTo property doesn’t need to be
coded.
FloatingDestination objects are Floating objects
that determine
their destinations using their foundIn and leadsTo
properties
If, however, our Door was
found in both the hallway and the kitchen, but its kitchen location lead to the
entryway then we would need to code the leadsTo property, like this:
/*
* When the actor is is the hallway the door leads to the kitchen,
* but from the kitchen the door leads to the entryway!
*/
hallDoor: Door
foundIn =
[ hallway, kitchen ]
leadsTo =
[ kitchen, entryway ]
sDesc =
"hall door"
noun =
'door'
adjective
= 'hall'
;
Also, using this technique
the Door object could be defined for multiple locations:
multiDoor: Door
foundIn =
[ entryway, hallway, kitchen ]
leadsTo =
[ hallway, kitchen, entryway ]
sDesc =
"multi door"
noun =
'door'
adjective
= 'multi'
;
This door appears in the
entryway, hallway, and kitchen when the actor occupies each of these Rooms. From
the entryway it leads to the hallway; from the hallway it leads to the kitchen;
and from the kitchen it leads to the entryway.
The next step, of course,
is to code up the Room directions to point to the multiDoor object. Notice that
we cannot assign the multiDoor to go from the hallway into either the
entryway or the kitchen depending upon the travel direction properties of the
Rooms. This leads to an important rule:
A FloatingDestination provides
only one destination for any given location
Hence you will need to
have one door object for each separate destination from any given location.