TADS: 'go to <location>', documentation (LONG)

3 views
Skip to first unread message

Lars Joedal

unread,
Nov 1, 1993, 2:54:28 AM11/1/93
to
'GO TO <LOCATION>' DOCUMENTATION


INTRODUCTION

What have we here? This is an implementation in TADS of a new verb
goToVerb. This verb allows the player to type commands like

>go to the machine room

and the system will try to find a way to the machine room, leading
the player all the way. This is not to be confused with
"teleportation" verbs (like the extra movement verbs in the original
Adventure). The 'go to <location>' command only allows the player to
travel to a location if it could be travelled to by use of the normal
travelling commands (N, S, etc.), and the journey takes just as much
of the player's time as a series of individual commands would. Doors
of the standard type (superclass doorway) will be opened on the way
if they aren't locked.
So, this verb doesn't allow the player to do anything that could
not be done with the normal commands. On the contrary, it has been
carefully implemented to minimize the risk of adding to the player's
capabilities (which might spoil some puzzles). But the verb allows
the player to quickly go to other locations, without having to spot
the best route on his/her map and without having to type all the
individual movements commands. In short, it relieves the player from
tedious backtracking.


WHAT IT LOOKS LIKE FROM THE PLAYER'S POINT OF VIEW

The player can type a command like
>go to the machine room
and the system will try to find a path from the player's current
location to the machine room.

If the system finds a path then the player will automatically walk to
the wanted location. Along the route, messages like
(Going east)
will be shown, and at each location the "sdesc" is shown, allowing
the player to see where s/he is walking. At the final location a
long description will be printed if VERBOSE mode is on, a short
description otherwise.
The system can find out how to open unlocked doors (if they are
implemented with the standard "doorway" class from adv.t). If a door
is closed it will be opened with a message to that effect:
(Going east)
(Opening the door first) Opened.
Each step counts as one turn, but opening doors is "free".

If the system can't find a path it says so and nothing happens. This
does not have to mean that there is no path at all. The walking can
only take place through locations that has been seen by the player
already. And if the player has no light source dark rooms can't be
passed. This is a bit more restrictive than for normal movement
commands, but ensures that the player can see where s/he is walking.
Neither can a locked door be passed, even if the player has the key;
doors must be unlocked manually. In some cases the player's
possessions may prohibit him/her from using a particular path. An
example is the narrow crawl in Ditch Day Drifter.

If the system does find a path but it is very long (default is longer
than 10 steps) the player is asked for confirmation. The limit of
how long a path can be without confirmation can be changed with the
verb "gotolimit". The command
>gotolimit <number>
sets the limit to <number>. A special case is that the number 0
turns off the questions. If no number is given the current limit is
printed.


HOW TO INCORPORATE THE VERB IN EXISTING GAMES

In short:
1. Your game must be compatible with TADS version 2.1 or higher.
2. #include the file with the implementation of the 'go to' verb.
3. Define vocabulary words for each location (room) in the game.
4. Mazes' rooms must have "lostroom" as superclass instead of "room".
5. Call the function initGoTo(true, nil, 10) (for example) in the
preinit function.
6. Use the flag "global.justTesting" in direction properties with
side effects.
Unless you have some very unusual coding or want to do some special
tests the above should be enough.

With explanations:
First, since the implementation uses quite a lot of the newest
features of TADS, your game must be compatible with TADS version 2.1
or higher. It is ok to disable the "do" keyword (with the -1d
compiler switch), but other keywords must be enabled.
Second, include the implementation file by adding a line like
#include <goto.t>
to the game (e.g., right after the inclusion of adv.t). The code
includes both coding of the 'go to' verb itself and small
modifications to a few of the classes from adv.t. These changes are
automatically applied by use of the "replace" and "modify" keywords.
Third, every location that can be gone to must have a noun and
possibly adjectives. Nothing strange here - this is just like with
any other object in the game that can be referred to. It just
happens that in many games rooms can't be referred to and thus don't
have vocabulary words defined for them.
Fourth, if the game has any mazes you don't want the player to be
able to say anything like
>go to maze entrance
and be guided out of the maze. This is prevented by making any room
that is part of a maze of type "lostroom" instead of the normal
"room". The 'go to' command can't be used in "lostroom"s, and
neither can the player pass through such a room on the route to
somewhere else. Of course the player can still walk around as
normal, s/he just can't use the 'go to' verb.
Fifth, you should initilize the 'go to' variables by calling the
function initGoTo(useLists, extraList, askLimit). It's first
parameter tells if the vocabulary matching should use lists for this
verb (see your TADS manual). If useLists = true the verb is set up
so that the player can go to any "room" that's not a "lostroom"
or"nestedroom". The second parameter should be a list of objects you
also want the player to be able to 'go to'. Such as any
"nestedroom"s that was excluded from the list just described. If you
have no extra rooms the list can be nil. The third parameter sets a
limit of how long a path can be without the player being asked for
confirmation (see the above section on the "gotolimit" verb). If
this parameter is nil or 0 the player will never be asked.
Sixth, any direction property with side effects (change of the
value of a property, killing the player, etc.) must include a check
of the new property "global.justTesting". This is because the
direction properties are evaluated when the route is found, even
though the actor is not walking that way. For example, before the
'go to' verb is incorporated the definition of the top of a cliff
might look like this:
clifftop: room
ldesc = "You are standing on the top of a cliff, right at
the edge. 300 feet below you the sea stretches
out to the west. "
west =
{
"You jump off the cliff and fall down all 300 feet.
Unfortunately, the impact with the water knocks you
unconscious and you drown. ";
die();
}
;
The problem is that the property cliff.west may be evaluated by the
system when it seeks a path to somewhere else. Thus the player may
be killed without even being near the cliff, much less jumping off it.
How can the programmer know when the player is actually jumping
off the cliff and when it's just the system testing this direction?
The solution lies in the new property global.justTesting. When the
code associated with the 'go to' verb is finding the path this
property is set to true; at all other times it is set to nil. The
cliff from before should now look like this:
clifftop: room
ldesc = "You are standing on the top of a cliff, right at
the edge. 300 feet below you the sea stretches
out to the west. "
west =
{
"You jump off the cliff and fall down all 300 feet.
Unfortunately, the impact with the water knocks you
unconscious and you drown. ";
if (not global.justTesting)
die(); // Normal code goes here
else
return( nil ); // Test code. Just say "no exit"
}
;
Now the player is only killed if the walk in this direction is for
real. If it's just a test nil is returned, signalling no (useable)
exit in this direction. Notice that it is allowed to output text in
any case. Any output is suppressed during the search for a path, so
the player won't see it.
The flag global.justTesting only have to be used in code in
direction methods. This means the code attached to the properties
"north", "south", "east", "west", "ne", "nw", "se", "sw", "up",
"down" of "room"s and the property "destination" of "obstacle"s (such
as doors - "doorway" has "obstacle" as a superclass). And of these
methods most won't need any change. Only those that change anything
will have to check for the global.justTesting flag.
In the implementation an example can be found in the replacement
code for doorway.destination. Here a check of global.justTesting has
been put in to ensure that auto-opening doors don't open just because
it is tested to where they lead.


EXTRA OPTIONS

Sometimes you may want to know if a player is in the middle of a walk
or not. For example, if you have fuse that makes the player's lamp
burn out it may be fair to ask something like
"Your lamp has burned out. Do you really want to proceed?"
if the player was walking. Of course the question should only be
asked if the player _is_ walking.
Here you can use the method actor.isWalking. If the actor is
currently walking true is returned, nil otherwise. To stop the actor
from continuing a walk, call actor.stopWalking.

There are others more low-level methods that can help you customize
things, if you want to. These generally have the prefix "gt" (Go
To). Among these are
- room.gtRoomCheck(actor): Called for every room that is tried when
the path is found. If the room does not want to be part of a path,
this method should return nil, true otherwise.
- movableActor.gtHasLight: A flag that is true if the actor has a
light source, nil otherwise. This flag is ONLY guaranteed to be
correct during calls of "gtRoomCheck" methods!
- movableActor.gtMessage: The method is called when anything goes
wrong and it's supposed to print a message (but not to exit the
current command or something like that). As a parameter is a
number that tells what has gone wrong.
There are more "gt" methods and properties, but the above list should
exaust the ones you need, unless you choose the change the system
drastically.
If you have made any changes to the metod Me.travelTo from adv.t
you should take a look at movableActor.gtTravelInDir too. This
method is used during walks instead of the normal "travelTo" method.


ASSUMPTIONS

I have tried to make the 'go to' code as independent as possible of
the code in the rest of the game. Still, I have had to make some
assumptions. It is assumed that:
- Doors are implemented in a clean way. This means that
- the standard class "doorway" is used
- if the door can't just be opened, the verDoOpen method complains
(i.e., if verDoOpen does not output any text then the door can
be opened with doOpen without any problems)
- the property doorway.doordest tells where the door is leading
- Direction properties does not change the game's state without
checking the property global.justTesting (see the section on
incorporation in an existing game).
- If a room does not like the 'go to' verb (goToVerb) it complains
via the "gtRoomCheck" method, not with the normal "roomCheck"
method. Since you can use the class "lostroom" to create rooms
that does not allow use of 'go to' you will only seldom have to
worry about the "gtRoomCheck" method.
- The method Me.travelTo does not differ drastically from the
standard method. The new method movableActor.gtTravelInDir tries
to simulate the normal "travelTo" method (in its own way), but if
you have changed what is normal, movableActor.gtTravelInDir might
suprise you.
- If a room has been seen then room.isseen = true (as normal). Only
rooms with isseen = true can gone to or through.

Even if these assumptions are not fulfilled the new verb should not
allow the player to do anything s/he could not do already. Each step
is simulated as a normal movement:
actor.roomCheck is called (the normal "roomCheck", not the
new "gtRoomCheck")
actor.actorAction is called
actor.location.roomAction is called
One step is taken (unless something blocks the way)
Daemons are run
Fuses are run
This is exactly the checks that are normally done by the system.
This ensures that the player is not allowed to do anything not
normally allowed.


ALGORITHM

The algorithm used for the search is relatively simple. The possible
routes are explored one step at a time. At first, all possible exits
of the player's current location is tried. Each newly room found is
marked as visited, and put in a list. Then the exits of the rooms in
the list is tried, and a new list of rooms is built. This goes on
until either the searched location is found or there are no more
rooms to try.
At each step, when a room is found it is marked as visited, and
properties (used as variables) are used to hold the location that
lead to this room (i.e. the previous room) and the direction used.
The marking of a room as seen prevents any cycles in the search, and
the storing of the previous room and exit direction makes it possible
to find the path used by backtracking.
In pseudo-code the algorithm can be described as:

roomList := [ actor.location ]
WHILE there is rooms left in roomList DO
newRoomList := [ ]
FOR each room in roomList DO
FOR each exit of the room DO
IF the exit leads to a room that is ok THEN
mark that new room as visited
store previous room (in a property of this new room)
store exit used from previous room ( " )
IF this new room is the searched room THEN
break the loop
ENDIF
add this new room to newRoomList
ENDIF
ENDFOR
ENDFOR
roomList := newRoomList
ENDWHILE
IF the destination room was found THEN
backtrack the path from the destination room
walk the player along the route
ELSE
tell the player that the room could not be found
END

Here the check "room is ok" means a variety of checks: The room must
not be marked (or we would proceed in cycles), the player must have
seen the room before, dark rooms are not allowed, etc.
The path found is guaranteed to be the shortest possible, because
after the n'th iteration of the outer loop the list called roomList
contains all the farthest rooms reachable in n steps. If the room
had been reachable in fewer than n steps it would have been found
during an earlier iteration.


TIME CONSUMPTION

I have tried to code everything fairly efficient, but still it takes
some time to search for a path to a given room. The algorithm is
linear in the number of rooms that must be tried, which is about the
best one can hope for, I guess. If the room to be found is nearby,
i.e. with a short path, the search will not be long, but if the room
is far away or it can't be found at all a lot of rooms has to be
searched. Still, it is guaranteed to be linear in the total number
of rooms. Rooms in mazes should not be counted, since they aren't
searched.
In practise I don't think the problem of delays is serious.
First, these delays only apply to the 'go to' command, where the
alternative is to manually find a route and walk along it. Second,
they aren't *that* long. On my Atari ST (8 MHz, MC68000 processor)
the 'go to' command takes an initial delay of about 1 second and each
room visited on the search takes 0.2 seconds, excluding any time
needed to load objects from disk. In Ditch Day Drifter a walk from
one end of the game to the other takes about 12 seconds.
If memory isn't scanty there will not be much loading from disk
during the search since the search only proceeds with rooms that has
been seen and thus loaded. And today 8 MHz is slow, so on your
favourite computer the above benchmark may be way too pessimistic.


LEGAL MATTERS

Both the code and the documentation as well as the sample game is
genuine public domain (it's neither shareware nor freeware, nor is it
copylefted). This means I give up *all* rights, and you can do
whatever you want with it. Such as stripping off my name, putting it
in your commercial game, patenting it, or selling it to the
government. I don't care (unless you sue me for infringement when
you are granted the patent :-) ).
Well, of course any kind of donation (such as letting me have a
registered copy of your shareware adventure game) is welcomed, but
it's completely volountary. As stated above, I have given up all
rights.
It's probably best to also have a disclaimer. Here it goes: I make
no promises of any kind about this code & documentation or about its
fitness for a particular purpose, and I don't take any responsibility
for direct or inderect consequenses of its use.


BUG REPORTS ETC.

We all hope there are no bugs in our code, and we all have to face
that users find some anyway. Please let me know of any bugs or
inconveniences you find. If you have any other comments, I'd also
like to here them.


Enjoy!

Lars


internet email: joe...@dfi.aau.dk

post mail: Lars Joedal
Rypevej 24
DK-8270 Hoejbjerg
Denmark


+------------------------------------------------------------------------+
| Lars J|dal | Q: What's the difference between a quantum |
| email: joe...@dfi.aau.dk | mechanic and an auto mechanic? |
| Physics student at the | A: A quantum mechanic can get his car into |
| University of Aarhus | the garage without opening the door. |
| Denmark | -- David Kra |
+------------------------------------------------------------------------+

Reply all
Reply to author
Forward
0 new messages