'GO TO ' 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 ' 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 sets the limit to . 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 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