
'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:  joedal@dfi.aau.dk

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


Version 1.1 note:
Lars Joedal's email address is defunct as of 7/2002.
I have not tried his postal address.
Please contact me at the address listed on the http://www.umbar.com
Web site for any questions about my changes for version 1.1.
I have only changed this file by adding this note and changing this 
file's name from GOTO.DOC to GOTO.TXT, since it is a plain text file, 
not a Word document.

-- Andrew Pontious
