Newsgroups: rec.arts.int-fiction
Path: gmd.de!xlink.net!howland.reston.ans.net!pipex!sunic!uts!news.daimi.aau.dk!aau!joedal
From: joedal@dfi.aau.dk (Lars Joedal)
Subject: TADS: 'go to <location>', implementation (LONG)
Message-ID: <joedal.752140383@dfi.aau.dk>
Sender: news@aau.dk
Nntp-Posting-Host: dfi.aau.dk
Organization: Aarhus University, Denmark
Date: Mon, 1 Nov 1993 07:53:03 GMT
Lines: 790

/* ************************************************************ */
/*  TADS implementation of "go to <location>", version 1.0	*/
/*  This code is public domain. 				*/
/*  Please report any bugs to					*/
/*	joedal@dfi.aau.dk					*/
/*  Comments are also welcome.					*/
/* ************************************************************ */

/* Along with this code you should have received a file with
 * documentation.
 * The following comment is NOT the (full) documentation, it's
 * just a terse list of the relevant methods, properties, verbs,
 * and classes.
 */

/* Changed methods & properties:
 *    doorway.destination
 *    chairitem.roomAction
 * New methods & properties you should know of:
 *    global.justTesting - flag 
 *    movableActor.isWalking - method
 *    movableActor.stopWalking - method
 * New methods & properties you may want to know of:
 *    room.gtRoomCheck( actor ) - method
 *    movableActor.gtHasLight - flag
 *    movableActor.gtMessage( number ) - method
 * Other new methods & properties (which you don't have to think of):
 *    Lots of them, all with the prefix 'gt'.  If you have changed the
 *    method Me.travelTo you should take a look at the method
 *    moveableActor.travelInDir( dirNo, verboseDesc ).
 *
 * New verbs:
 *   goToVerb
 *   gtLimitVerb
 *
 * New classes:
 *   lostroom - a room that can't be gone to, from, or through.
 */

initGoTo: function;		     // Initializes some variables and lists.
findExitList: function; 		// Finds exits from current location.
findRoomFromObstacle: function;  // Finds the room that an obstacle leads to.
findPath: function;		    // Finds a way to to a room, if possible.
nowGoToRoom: function;	  // Leads an actor along a path, one step at a time.


/* New verbs: 
 *    goToVerb:  The verb to 'go to' a location.
 *    gtLimitVerb:  A verb to control how long the path can be without the
 *		    player being asked for confirmation first.
 */

goToVerb: deepverb
    verb = 'go to'
    sdesc = "go to"
    doAction = 'GoTo'
    isTravelVerb = true
    dirNames = [ 'north' 'northeast' 'east' 'southeast' 'south'
		 'southwest' 'west' 'northwest' 'up' 'down' ]
    dirProps = [ &north &ne &east &se &south &sw &west &nw &up &down ]
    dirVerbs = [ nVerb neVerb eVerb seVerb sVerb swVerb wVerb nwVerb
		 uVerb dVerb ]
    validDo( actor, obj, seqno ) = {
	if ( obj = nil )
	    return( nil );
	else if ( self.validDoList <> nil )
	    /* If the goToVerb.validDoList has been initialized (normally
	     * by the function initGoTo) it is assumed that it only contains
	     * legal objects.
	     */
	    return( obj.isseen );
	else
	    /* If the list is nil the default behaviour is used:  Allow only
	     * real rooms (not "nestedroom"s) and only if they have been
	     * seen.
	     */
	    return( obj.isroom and obj.isseen and not obj.isnestedroom );
    }
    validDoList = nil	// Not to be confused with the empty list.  If the
			// list is nil the system does not restrict the
			// search to the list.
    gtAskLimit = 10  // If the walk will take more than this number of
		     // moves the player will be asked for confirmation.
		     // If this property is set to nil the player will
		     // never be asked.  Change it with a call of the
		     // function initGoTo if you want another value.
;

gtLimitVerb: deepverb
    sdesc = "gotolimit"
    verb = 'gotolimit'
    action( actor ) = {
	if (goToVerb.gtAskLimit = nil)
	    "Currently there is no limit on the number of moves without
	     confirmation. ";
	else {
	    "Current limit of moves without confirmation is ";
	    say( goToVerb.gtAskLimit );
	    ". ";
	}
    }
    doAction = 'GtLimit'
;

/* New class:  lostroom is a type of room that can't be gone to or from
 * with the 'go to' verb.  Generally, to be used for mazes - there is
 * no fun of a maze if the player can just type 'go to maze exit' or
 * something like that.
 */
class lostroom: room
    roomCheck( v ) = {
	if ( v = goToVerb ) {
	    // This method is called by the normal system when the player
	    // gives the command.  Meaning that the player stands in this
	    // room and is trying to walk out.
	    "Find %your% own way! ";
	    return( nil );
	}
	else
	    pass roomCheck;
    }
    verDoGoTo( actor ) = {
	// The player is GOing TO this room.
	"%You%'ll have to find << self.thedesc >> on %your% own. ";
    }
    gtRoomCheck( actor ) = nil	// Do not allow this room to be used on
				// 'go to' walks.
;

/* Changes to existing classes and objects: The classes movableActor and
 * room have been changed and extended to accomodate the new goToVerb.
 * The classes darkroom, nestedroom, doorway, and chairitem have also
 * been changed, but only slightly.  And basicNumObj has been extended
 * to recognize gtLimitVerb.
 */

modify movableActor
    gtTravelInDir( dirNo, verboseDesc ) = {
	/* The method gtTravelInDir is a kind of new "travelTo" method,
	 * tailored to the 'go to' verb.  The actor takes a step in the
	 * direction specified.  If successful, true is returned, nil
	 * otherwise.  Suitable messages are shown.  If verboseDesc is
	 * true a full lookaround is done, otherwise only a terse one.
	 */
	local	nextLoc, dirVerb;

	"\b(Going ";
	say( goToVerb.dirNames[dirNo] );
	")\n";
	nextLoc := self.location.( goToVerb.dirProps[dirNo] );
	if (nextLoc <> nil and nextLoc.isobstacle)
	    nextLoc := findRoomFromObstacle(self, nextLoc);

	/* Now nextLoc contains the next location (unless something unusual
	 * has happened).
	 */
	if (nextLoc = nil) {
	    /* For some reason or another the actor is NOT able to go by
	     * the laid-out route (e.g., a non-player character could have
	     * locked a door).  Give the player a message to the effect
	     * and stop walking.
	     */
	    self.gtMessage(4);
	    exit;
	}
	/* Normal move.
	 * Since a lot of these moves are done in a single command TADS
	 * doesn't get a chance to do all the normal checks for each move.
	 * Instead the checks are done here explicitly.
	 * To make the whole thing fit smoothly into the rest of the game
	 * each move is simulated as a normal movement command.
	 */
	dirVerb := goToVerb.dirVerbs[dirNo];
	if ( not self.roomCheck( dirVerb ) )
	    exit;
	self.actorAction( dirVerb, nil, nil, nil );
	self.location.roomAction( self, dirVerb, nil, nil, nil );
	if ( not (self.location.islit or nextLoc.islit) ) {
	    darkTravel();
	    return(nil);
	}
	/* All the checks are done.  Now the actor can be moved.  This
	 * is done in a way very similar to the Me.travelTo method.
	 * There are differences, though:
	 * - the descriptions on the route are short, even if the player
	 *   is playing in VERBOSE mode.  Long descriptions all along would
	 *   just be confusing.  Only at the final location is the long
	 *   description shown.
	 * - there is no reason to set the room.isseen flag since only
	 *   already seen rooms can be part of the path
	 */
	self.location.leaveRoom(self);
	self.moveInto(nextLoc);
	self.location.lookAround(verboseDesc);
	return(true);
    }
    gtMessage( number ) = {
	/* This method is called when:
	 * - the path is found (to allow for a message to that effect)
	 * - the path could not be found (report the error)
	 * - the found path turned out to be unusable (see gtTravelInDir)
	 */
	switch( number ) {
	    case 0: // No error.  No message
		    break;
	    case 1: // Just not found.
		    "Sorry, I couldn't find that location from here.\n";
		    break;
	    case 2: // No exits from here.
		    self.location.noexit;
		    break;
	    case 3: // The starting location is dark.  Being more hard on
		    // this for 'go to' travelling than normal travelling
		    // (see darkroom.gtRoomCheck) we count this as an error.
		    darkTravel();
		    break;
	    case 4: // Panic!  The actor could not walk along the specified
		    // path!
		    "Something blocks %your% way!\n";
		    break;
	    default: // We should never end here, but just in case...
		     "Sorry, something went wrong.\n";
		     break;
	}
    }
    gtWalkTurn = -1	     // Holds current turn count if actor is walking.
    gtAllowDifference = nil	// "true" allows gtWalkTurn to be off by one.
    gtStopWalking = nil  // "true" means the current walking should stop NOW.
    isWalking = {
	/* Returns true if the actor is currently walking, nil otherwise.
	 * "Currently walking" means that a 'go to' command is active and
	 * that the last step has not been taken.  Thus if this method is
	 * called when the fuses and daemons are run for the last time (after
	 * the last step) nil is returned.
	 */
	local countDif;

	countDif := self.gtWalkTurn - global.turnsofar;
	if (self.gtAllowDifference) countDif++;
	return( countDif >= 0 );
    }
    stopWalking = {
	/* Stops the current walk.
	 * Example:  A fuse decides that the player's lamp is now running
	 * out.  So it tells the player this and turns off the lamp.  Since
	 * this will in general hinder walking ('go to' walking as well as
	 * normal movement commands) the fuse calls Me.isWalking to see if
	 * the player happens to be in the middle of a walk.  It finds out
	 * that the player is indeed walking, and calls Me.stopWalking.
	 * The fuse does NOT end with an exit or abort statement, which is
	 * quite normal as other fuses should have the chance to burn down,
	 * too.
	 * Summary:  Calling this method is the proper way to stop the
	 * player from walking, as opposed to using exit or abort.
	 */
	self.gtStopWalking := true;  // This stops the main 'go to' loop (in
				     // the function nowGoToRoom).
    }
;

modify class room
    /* Expand the definition of a room, so it responds to the goToVerb. */
    verDoGoTo( actor ) = {
	if (self = actor.location)
	    "But %you're% here already!\n";
    }
    doGoTo( actor ) = {
	local	pathList;
	local	rem, cur, tot, i;

	/* To speed up operations with "darkroom"s we here find out whether
	 * the actor has a light source, so it will not have to be considered
	 * each time a darkroom.gtRoomCheck is tested.
	 */
	rem := global.lamplist;
	tot := length( rem );
	actor.gtHasLight := nil; // This property holds the information of
				 // whether the actor has a lamp or not.
				 // It is only to be used in routines special
				 // for the 'go to' verb!
	for( i := 1; i <= tot; i++ ) {
	    cur := rem[i];
	    if ( cur.islit and cur.isIn(actor) ) {
		actor.gtHasLight := true;
		break;
	    }
	}

	/* Now find the path. */
	pathList := findPath( actor, self );
	if ( pathList <> nil ) {
	    local   ok, lim;

	    lim := goToVerb.gtAskLimit;
	    if (lim <> nil and length(pathList) > lim ) {
		"To go there will take ";
		say(length(pathList));
		" turns.  Do you want to go there?\ ";
		if (yorn() = 1) ok := true;
		else ok := nil;
	    }
	    else
		ok := true;
	    if ( ok ) nowGoToRoom(actor, pathList);
	}
    }
    gtRoomCheck( actor ) = {
	/* This method is used by the 'go to' verb to check out a room.  If
	 * the room objects it should return nil, true otherwise.
	 * The method is meant to work somewhat like a combined version of
	 * the normal "roomCheck" and "roomAction" methods. The  differences
	 * are:
	 * - Only one method.
	 * - Only called when the verb in question is 'go to'.
	 * - No global variables must be changed.  Whereas a normal "room-
	 *   Action" can kill the player, abort the current command, etc.
	 *   if it wants to do that, this method is only allowed to express
	 *   is dissatisfaction by returning nil.  This is because the player
	 *   has not actually done the thing, the system is just testing if
	 *   it would be a possible (and fair) thing to do.
	 * - The method may produce output if it wants to, but the output
	 *   is discarded.  This can be useful when calling some other
	 *   "normal" methods that may produce output (they are not allowed
	 *   to change any variables, though).
	 * - The method can't count on the player being in this room.  This
	 *   affects the way a dark room must check for presence of light
	 *   sources, for example (see modifications of darkroom).
	 * - On the other hand this method CAN count on the actor having a
	 *   property gtHasLight that tells whether the actor is carrying a
	 *   light source or not (this is only done to make light-searching
	 *   faster).
	 *
	 * Thus this method can block the 'go to' command from using this
	 * room.  The method is only called in the test-phase, though.
	 * When/if a path is found and the player walks toward his/her
	 * destination, it will be the normal "roomCheck" and "roomAction"
	 * that are in charge, and the steps will be done as if the player
	 * had walked around in the normal way.
	 */
	return( true ); // Answer for standard rooms is "no problem"
    }
    isroom = true
    cantReach( actor ) = {
	/* Called if the room was specified, but it was not accepted.  This
	 * generally means it has not been seen, or it is a nestedroom.
	 */
	"Sorry, but I fail to see where %you% %are% trying to go. ";
    }
;

modify class darkroom
    /* It is not allowed to walk through a dark room with a 'go to <location>'
     * command.  This is a bit stricter than for normal walk (where the
     * walk is allowed if either the starting point or the destination is
     * lit), but it ensures the player will see the route s/he is using.
     */
    gtRoomCheck( actor ) = {
	/* Don't allow 'go to' to use this room if it is dark.	A special
	 * check is needed, partly for speed, partly because the actor is
	 * not necessarily in the room (so self.islit doesn't see any light
	 * source the actor is carrying).
	 */
	if ( actor.gtHasLight ) return( true );
	else return( self.islit );
    }
;

modify class nestedroom
    isnestedroom = true
;

modify doorway
    /* Change the standard of 'destination' to recognize the flag
     * global.justTesting.  If global.justTesting = true no properties
     * must be changed.  There is nothing wrong in printing text, though.
     */
    replace destination = {
	if ( self.isopen ) return( self.doordest );
	else if ( not self.islocked and not self.noAutoOpen ) {
	    if ( not global.justTesting ) {
		self.isopen := true;
		if ( self.otherside )
		    self.otherside.isopen := true;
	    }
	    "(Opening << self.thedesc >>)\n";
	    return( self.doordest );
	}
	else {
	    "%You%'ll have to open << self.thedesc >> first. ";
	    if ( not global.justTesting )
		setit( self );
	    return( nil );
	}
    }
;

modify class chairitem
    roomAction( actor, v, dobj, prep, io ) = {
	/* Expanded roomAction that allows the 'go to' verb.  If this
	 * method was not changed the player would get the error "You
	 * can't reach <location> from here!".  Now the player instead
	 * gets the more suitable message "You don't get anywhere until
	 * you get out of that chair!" (when it is found that the chair
	 * has no exits).
	 */
	if ( v<>goToVerb )
	    pass roomAction;
	// Else:  Do nothing.
    }
;

modify basicNumObj
    verDoGtLimit( actor ) = {}
    doGtLimit( actor ) = {
	if ( self.value >= 1 ) {
	    "New limit of moves without asking for confirmation is ";
	    say(self.value);
	    ". ";
	    goToVerb.gtAskLimit := self.value;
	}
	else {
	    "There is now no limit of moves without asking for confirmation. ";
	    goToVerb.gtAskLimit := nil;
	}
    }
;


initGoTo: function(useLists, extraList, askLimit)
{
    /* Intializes a few variables relevant for goToVerb.  Call this function
     * in the preinit function.
     * In fact, everything is set up so that this function does not HAVE
     * to be called.  Not calling it will produce the same results as calling
     * initGoto(nil, nil, 10).  Still, it's more clean to call it.
     * If useLists = true the list goToVerb.validDoList is set to a list
     * of all rooms in the game, excluding "lostroom"s and "nestedroom"s.
     * If the parameter is false, the list is set to nil, meaning that
     * TADS uses the old (version 1.x) method:  All objects with matching
     * vocabulary words are tried.
     * If the abovementioned list is not enough the extraList parameter can
     * be used.  If extraList <> nil it is added to the list computed as
     * above (only if useLists = true).  Thus e.g. certain nestedrooms can
     * be allowed by use of this parameter.
     * The limit of how long a path can be without the system having to
     * ask the player for confirmation is set by askLimit.  0 or nil means
     * no limit.
     */

    if (useLists) {
	local	roomList, lst, curRoom;

	roomList := [];
	curRoom := firstobj(room);
	while (curRoom <> nil) {
	    /* "nestedroom"s are excluded because they normally can't be
	     * gone to with the normal movement commands, and besides they
	     * may not be real rooms.  For instance, vehicles act more like
	     * normal game objects than like game locations.
	     */
	    if ( not curRoom.isnestedroom )
		roomList += curRoom;
	    curRoom := nextobj(curRoom, room);
	}
	if (extraList <> nil) {
	    // Remove any duplicates and then add the list to roomList.
	    lst := intersect(roomList, extraList);
	    roomList += extraList - lst;
	}
	goToVerb.validDoList := roomList;
    }
    else
	goToVerb.validDoList := nil;

    if (askLimit = nil or askLimit < 0)
	goToVerb.gtAskLimit := nil;
    else
	goToVerb.gtAskLimit := askLimit;
}


findRoomFromObstacle: function(actor, obst)
{
    /* Given the obstacle "obst" this function returns the room that is
     * connected to the obstacle by the "destination" property.  If a room
     * was found that is returned, nil otherwise.  Closed doors that can be
     * opened without problems count as passed.
     * The global variable global.justTesting is used to determine whether
     * this is just a test (doors must not be opened, no text should be
     * shown, etc.) or real.  If globalJustTesting = nil doors are opened
     * if necessary and possible, and suitable text is shown whether the
     * function returns a room or nil.
     */
    local   loc, success;

    if ( obst.isdoor and not obst.isopen ) {
	/* Open the door if possible. */
	if (not global.justTesting) "(Opening the door first) \ ";
	outhide(true);
	obst.verDoOpen(actor);
	if ( outhide(nil) ) {
	    /* There has been some output, meaning that verDoOpen complained.
	     * If this is not just a test then repeat that error message (so
	     * the player can see it) and give up.
	     */
	    if (not global.justTesting) obst.verDoOpen(actor);
	    loc := nil;
	}
	else if (not global.justTesting) {
	    /* No problem with opening the door.  Do it! */
	    obst.doOpen(actor);
	    "\n";
	    loc := obst.destination;
	}
	else {
	    /* The door can be opened, but this is a test.  Since we can't
	     * open the door (that would change the game's state) the room
	     * behind the door can't be found by the "destination" property.
	     * But if the door had been opened that should return the value
	     * of the property "doordest", so we evaluate that instead.
	     */
	    loc := obst.doordest;
	}
    }
    else {
	// It's not a closed door.
	if (global.justTesting) outhide(true);
	loc := obst.destination;
	if (global.justTesting) outhide(nil);
    }
    /* At this point "loc" contains what the obstacle leads to, if it could
     * be passed, and nil otherwise.
     * If it is a room or nil, return that.  If it is a new obstacle, try
     * to pass that.  If it is something else: Strange; return nil to be on
     * the safe side.
     */
    if (loc = nil or loc.isroom) return( loc );
    else if (loc.isobstacle) return( findRoomFromObstacle(actor,loc) );
    else return( nil );
}


findExitList: function(actor, loc, searchNo)
{
    /* Returns a list of exits from the room 'loc'.  The order is:
     * North, south, east, west, ne, nw, se, sw, up, down.  If there is
     * no exit in some direction the list contains 'nil' for that direction.
     */
    local   locList;
    local   i, listLength;
    local   newLoc;

    global.justTesting := true;  /* Signals that we don't want any permanent
				  * changes to happen when evaluating direction
				  * methods (e.g. evaluating 'destination' for
				  * an autoopening door will normally open the
				  * door).
				  */
    listLength := length( goToVerb.dirProps );
    locList := [];
    outhide(true);  /* Some of the following evaluations may show text
		     * (specifically error messages when wrong directions
		     * are tested), so we turn off any output.
		     */
    for( i := 1; i <= listLength; i++ )
	locList += loc.( goToVerb.dirProps[i] );
    outhide(nil);   // Turn on output again.

    /* Clean up the list.  This means removing locations already visited
     * during this search and locations that the player haven't seen yet.
     * And if a new 'location' turns out to actually be a door or another
     * obstacle it needs special treatment.
     */
    for( i := 1; i <= listLength; i++) {
	newLoc := locList[i];
	if (newLoc = nil)
	    continue; // No exit in that direction.  Go on with next direction.
	if (newLoc.isobstacle) {
	    newLoc := findRoomFromObstacle(actor, newLoc);
	    locList[i] := newLoc;
	    if (newLoc = nil) continue; // No room found.
	}
	/* So, there's a room. */
	if ( (not newLoc.isseen) or (newLoc.gtSearchMark = searchNo) ) {
	    // This room is not interesting for this search.
	    locList[i] := nil;
	    continue;
	}
	/* Everything is turning out fine.  The only thing that can go
	 * wrong now is that the room objects.
	 */
	outhide(true);
	if ( not newLoc.gtRoomCheck( actor ) ) locList[i] := nil;
	else newLoc.gtSearchMark := searchNo;	// Mark the room as seen
						// during this search.
	outhide(nil);
    }
    global.justTesting := nil;	// Back to normal state.

    return( locList );
}


findPath: function(actor, toLoc)
{
    /* Find a route from the actor's current location to "toLoc".  It's
     * assumed that both fromLoc and toLoc are rooms and that toLoc is
     * not the actor's current location (actor.location <> toLoc). If a
     * route was found that is returned as a list of directions, where
     * 1=north, 2=ne, 3=east, 4=se, 5=south, 6=sw, 7=west, 8=nw, 9=up,
     * 10=down, corresponding to the lists in goToVerb.
     * If no route was found nil is returned.
     */
    local   fromLoc;
    local   roomList, newRoomList;
    local   pathFound, pathLength;
    local   failureType;

    fromLoc := actor.location;
    if (not fromLoc.islit) {
	/* The 'go to' command can only use lightened rooms, that also
	 * counts for the initial location.
	 */
	actor.gtMessage(3);
	exit;
    }

    /* Find new value of global.gtSearchNumber for this search. */
    if (global.gtSearchNumber = nil) global.gtSearchNumber := 1;
    else global.gtSearchNumber++;

    /* Mark the start location as seen. */
    fromLoc.gtSearchMark := global.gtSearchNumber;

    /* Search any rooms that can be reached from the current room.  Then
     * search any new rooms that can be reached from these rooms, and so on.
     * The search continues until the correct room is found or we run out
     * of rooms to search.
     */
    roomList := [] + fromLoc;
    pathFound := nil;
    pathLength := 0;
    failureType := 0;
    while( length(roomList) > 0 and not pathFound ) {
	local	i, roomListLength;

	pathLength++;
	/* For each room in the current roomList, find all accessible rooms
	 * and store them.
	 */
	newRoomList := [];
	roomListLength := length(roomList);
	for( i := 1; i <= roomListLength; i++) {
	    local   j, lst, lstLength;
	    local   thisLoc;

	    lst := findExitList( actor, roomList[i], global.gtSearchNumber );
	    thisLoc := roomList[i];
	    /* For each room in the list, store (in the room) how we got
	     * there, i.e. from what room did we come and by going in what
	     * direction.
	     */
	    lstLength := length(lst);
	    for( j := 1; j <= lstLength; j++) {
		local	nextLoc;

		nextLoc := lst[j];
		if( nextLoc <> nil ) {
		    /* Make the new room point back to the old room. */
		    nextLoc.gtPreviousRoom := thisLoc;
		    nextLoc.gtPreviousDir := j;
		    if (nextLoc = toLoc) {
			/* Yeah, the room has been found!!! */
			pathFound := true;
			break;	// No reason to search any further.
		    }
		    /* Since we got here it was not the right room.  Put
		     * this room in the list of rooms that will be searched
		     * in the next iteration of the outer loop.
		     */
		    newRoomList += nextLoc;
		}
	    }
	}
	roomList := newRoomList;    // Make list ready for next iteration.

	if (pathLength = 1 and length(roomList) = 0) {
	    /* This is the end of the first iteration and no new locations
	     * has been found.  In other words, there are no (normal) exits.
	     * Set the variable "failureType" to indicate this.
	     */
	    failureType := 2;
	    break;  // There is no point in continuing.
	}
    }

    if (not pathFound) {
	/* Path was not found.  Let the actor give a message and then
	 * return nil to signal failure.
	 */
	if (failureType > 0)
	    actor.gtMessage(failureType);  // Error type has already
					   // been specified.
	else
	    actor.gtMessage(1);  // General error.
	return( nil );
    }
    else // The path has been found - let the actor display a message if it
	 // wants to do that.
	actor.gtMessage(0); // Success.

    /* At this place the room has been found.  Since each room visited can
     * tell from where it was found and what direction was chosen there
     * a path can be put together.  Here a path means a list of directions
     * (coded as north=1, ne=2, etc.) that should be followed, starting
     * from the initial room.
     */
    {
	local	pathList, i, loc;

	/* Following the chain of rooms gives the path in reverse order.
	 * To make everything easy first a list of the right length is
	 * made, then it is filled up from the end.
	 */
	pathList := [];
	for( i := 1; i <= pathLength; i++ )
	    pathList += 0;  // Extend list with something.
	loc := toLoc;
	for( i := pathLength; i > 0; i-- ) {
	    pathList[i] := loc.gtPreviousDir;
	    loc := loc.gtPreviousRoom;
	}

	return( pathList );
    }
}


nowGoToRoom: function(actor, pathList)
{
    /* Given an actor and a path to follow this function moves the actor
     * along the path from his/her current location.  At each new location
     * the normal "checkVerb" and "roomAction" methods are called with
     * a verb that corresponds to the movement
     * To make the travel look as much as possible like a series of normal
     * movements the methods are called AS IF IT HAD BEEN A NORMAL MOVEMENT.
     * This means that if the actor is about to go north because the goToVerb
     * has found that direction "checkVerb" is called with nVerb.  Thus if
     * there is any special code for that movement it will be executed just
     * like it would be if the player had typed "go north" at that location.
     * If the player for some reason can't travel any longer (a door might
     * be locked, for instance) the travel is stopped.
     */
    local   i, pathLength, verboseDesc;

    actor.gtStopWalking := nil;
    pathLength := length(pathList);
    for( i := 1; i <= pathLength; i++ ) {
	local	dirNo;

	if( actor.gtStopWalking)
	    exit;
	actor.gtWalkTurn := global.turnsofar;
	actor.gtAllowDifference := nil;
	dirNo := pathList[i];
	/* The descriptions on the route are short. But if the player
	 * is using VERBOSE mode the final description should be long.
	 */
	verboseDesc := (i = pathLength) and global.verbose;
	if (not actor.gtTravelInDir( dirNo, verboseDesc ) )
	    exit;   // Do not continue if something came in the way.
	/* To make every step count as a turn we run the daemons and fuses
	 * now.  If this was the last move, though, this task is left to
	 * the normal run of daemons and fuses following a command.
	 */
	if (actor.gtStopWalking)
	    exit;
	if (i < pathLength) {
	    actor.gtAllowDifference := true;
	    rundaemons();
	    runfuses();
	    actor.gtAllowDifference := nil;
	    if (actor.gtStopWalking)
		abort;	// The daemons and fuses should not run again, taking
			// yet another turn.
	}
    }
    /* And that was it. */
}

