LookDir TADS 3 Library Extension ================================ Version 4 (October 2005), by Eric Eve The purpose of this extension is to allow commands of the form LOOK NORTH, LOOK EAST, LOOK SOUTHWEST and LOOK DOWN. To use this file in your project, just include it in your list of source files after adv3.t. The Basics ---------- To supply a customised response to a LOOK DIR command, simply define the corresponding dirLook property on the location in question. The property should contain a double-quoted string or a method that displays a string. For example, if you have an open field with a fence to the south and a distant mountain to the northeast you might define: field: OutdoorRoom 'Open Field' "This large open field is bounded by a tall fence to the south. In the distance to the northeast you see a tall mountain. " southLook = "To the south you see the boundary fence. " northeastLook = "To the northeast a distant mountain rears up against the sky. " ; If the player types LOOK DIR when the appropriate dirLook property is not defined, the game will display the message "You see nothing remarkable to the dir. ", unless you've overridden this default, or unless it picks up a room part (see below). If you define an xxxLook property on a Superclass but don't want it to be active on an instance of that class, simply define it as nil on that instance. The directions supported for a LOOK DIR command are NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST, UP, DOWN, OUT, FORE, AFT, PORT and STARBOARD. Note that LOOK IN will be treated as an incomplete LOOK IN something command (and will thus evoke the response "What do you want to look in? " If no outLook property is defined, LOOK OUT will similarly be treated as an incomplete LOOK THROUGH something command. When all else fails (there's no xxxLook property corresponding to the DIR XXX command, and no corresponding room part to describe), dirLook calls defaultLook(dirn) on the appropriate location. By default this displays a message of the form "You see nothing remarkable to the xxx. ", but you can override it to something more appropriate if you wish. Note that since it's a method taking dirn as a parameter you can even override it to provide different messages for different directions. This might be useful, for example, when the player character is on a beach and there's sea in one set of directions and sand in the rest. Instead of having to define a whole lot of similar xxxLook properties you could instead define: defaultLook(dirn) { if(dirn is in (northDirection, northEastDirection, northWestDirection) "You look out over a vast ocean. "; else "To the <> you see nothing but sand. "; } Complication 1 -- Room Parts ---------------------------- If you don't define a custom dirLook property in a particular location, then by default if the player tries to LOOK in that direction, the game will try to describe the corresponding room part (on the basis that this is what the player character would see if nothing else intervenes). For example if the player types LOOK NORTH and there's no northLook property defined (or it's defined as nil) the game will describe the north wall (if there is one). LOOK UP and LOOK DOWN will cause the game to describe the ceiling or sky and the floor or ground respectively. Note that this only works for RoomParts that are included in the location's roomParts list; if, for example, you define a custom north wall as a Fixture object not listed in the location's roomParts property it will not be described in response to LOOK NORTH. If you don't want LOOK DIR to describe room parts, you can customise this behaviour by overriding lookDirToRoomPart(dirn), either on a Superclass (e.g. Room) or on individual rooms. Note that there's no need to do this if you're already defining all the appropriate xxxLook properties, since these will always take precedence over the room parts (e.g. if you define an eastLook property, then LOOK EAST will cause your eastLook property to display, not the east wall). To stop any rooms parts being described in a response to a LOOK DIR command, override lookDirToRoomPart(dirn) to simply return nil. To selectively block room parts from being described in response to a LOOK DIR command, override lookDirToRoomPart(dirn) to return true or nil according to the direction looked in. For example, if you're happy for LOOK UP and LOOK DOWN to dscribe the floor and ceiling, but don't want LOOK NORTH, LOOK EAST etc. to describe the walls, you could define: lookDirToRoomPart(dirn) { return dirn in is (upDirection, downDirection); } On the other hand, there may be some locations where your custom wall-description is so stunning that you want it to be displayed in addition to your xxxLook property. You can achieve this by defining your xxxLook property as a method that returns the special enum value showRoomPart. For example, if you want to define an eastLook property but want the east wall to be described as well, you could define it thus: eastLook { "You look east along the row of chairs that stretches all the way to the wall. "; return showRoomPart; } Finally, you may like to know how lookDir selects which room part to display. For LOOK DOWN, it simply selects the current location's roomFloor. For LOOK UP it tries to find a room part that has 'sky' or 'ceiling' among its nouns (actually, it's a bit more general than that; what it actually does is to find a roomPart with defaultCeiling.name or defaultSky.name among its nouns). Lastly, it looks for a room part with an adjective matching the direction name; for example if the player types LOOK NORTH, the game will look for the first room part for which 'north' is an adjective. This means that if you define an 'aft bulkhead' or a 'port wall' or a 'northeast fence' as a room part and include it in the current location's roomParts list, it will be found by a LOOK AFT, LOOK PORT or LOOK NORTHEAST command. Complication 2 -- Light and Darkness ------------------------------------ Everything described so far assumes that there's enough light to see by. If a player types LOOK N (or whatever) at a dark location, then by default the game will respond with "It's too dark to see anything to the north. " Normally, that's what you want, and so you won't need to do anything about it. But there may be occasions when it's not what you want. That you are in a darkened location doesn't always necessarily mean that you can't see anything in any direction. For example, you might be in a dark cave but still able to see the dim outline of an exit to the west; or you may be in the middle of a dark field on a pitch-black night but still able to see a light on a distant hillside. To handle this kind of situation you can define an xxxLookDark property on the current location. For the two examples just given you might define: westLookDark = "To the west you can just make out the dim outline of an exit. " northeastLookDark = "To the northeast you can see a light on the distant hillside. " Then the corresponding messages will be displayed when the player types LOOK WEST or LOOK NORTHEAST when no light source is present (you could also define westLook and northeastLook properties on the same locations to display different messages for lit conditions). Note that in such cases you will probably want to override roomDarkDesc to provide an 'in the dark' description that corresponds with your xxxLookDark, and possibly also define an appropriate object (or objects) with brightness = 1 to respond to commands such as X EXIT or X LIGHT that refer to any objects mentioned in your roomDarkDesc and xxxLookDark descriptions; e.g.: moor: OutdoorRoom 'Deserted Moor' "There is little to see in this bleak spot apart from a track leading south and a distant hill to the northeast. " roomDarkDesc = "It is totally dark here; you can see nothing apart from a distant point of light to the northeast. " ; + Distant 'distant bright point/light' 'distant light' "You can just make out a point of light, somewhere on a hillside to the northeast. " brightness = 1 specialDesc = "There's a bright light on a distant hillside to the northeast. " ; (In practice this example would probably need refining further, but it suffices to give the general idea). If you want to customise the behaviour of lookDir in the dark further than use of the xxxLookDark properties will allow, you can do so by overriding tooDarkForDirLook(dirn) or lookDirInDark(dirn) either on individual locations, or on BasicLocation or one of its subclasses. For details, consult the comments in the lookDir source code. Complication 3 -- NestedRooms ----------------------------- The discussion so far has more or less assumed that the actor is directly in a top-level room, but this is not always the case, so we next need to consider what happens when the player character (PC) is in a NestedRoom (such as a bed or chair) when a LOOK DIR command is issued. Normally, you wouldn't expect the view in a particular direction to change because you were sitting on a chair or standing on a platform, so by default lookDir won't change the response either. If you sit on a chair, or lie on a bed, or stand on a platform or in a booth and issue a LOOK DIR command, by default the command will be handled by the properties on the outermost enclosing room. The one exception is if the NestedRoom totally encloses the PC so s/he can't see out (for example, the PC is inside an Openable Booth that is both closed and opaque), in which case the NestedRoom will deal with the LOOK DIR command. In this particular case, the enclosing room's room parts will not be visible, and the enclosing room's xxxLook properties will not be used; instead, by default, the NestedRoom will report back with it's "You see nothing remarkable to the dir. " message (which you could override to something more appropriate). There will be many exceptions where this default behaviour is not what you want, however, so lookDir offers several ways of overriding it. Firstly, if you define an xxxLook property on a NestedRoom, it will be used when the PC is in that NestedRoom, even if the corresponding property is defined on an enclosing location. This allows for the case where the view from the platform (for example) differs from the view from its enclosing room. Secondly, it may be a NestedRoom blocks the view in certain directions. If the PC is sitting in a chair that's fixed facing north, s/he may not be able to see much to south, southeast or southwest, for example. Or if the chair is in a three-sided Booth, s/he may only be able to see out to north, northeast and northwest. To handle this situation you can supply a list of occluded directions on the NestedRoom by listing them in its occludedDirs property (a list). For example, in the first case you might define: + fixedChair: Chair, Fixed 'chair' 'chair' ... occludedDirs = [southDirection, southeastDirection, southwestDirection ] defaultLook(dirn) { "The chair is fixed facing north, and you can't twist round to look to the <> while you're sitting in it. "; } ; This would have the effect of forcing lookDir to use the fixedChair object rather than its enclosing room to respond to a LOOK SOUTH, LOOK SOUTHEAST or LOOK SOUTHWEST command. This will then cause fixedChair's defaultLook method to be called (assuming you hadn't defined southLook, southeastLook and southwestLook properties on the chair, which would be another, but more laborious, way of dealing with this situation). As shown above, you'd probably want to override the chair's defaultLook method to give an appropriate message. This is fine if only a few directions are occluded, but if most are it could become tedious to list all the others. In such a case it may be easier to override lookDirOccluded(dirn) instead. For example, if the PC's in a three-sided booth from which one can see out only to north, northeast and northwest you might define: + Booth 'three-sided booth' 'three-sided booth' ... lookDirOccluded(dirn) { return dirn not in (northDirection, northeastDirection, northwestDirection); } defaultLook(dirn) { "All you can see to the <> is the wall of the booth. "; } ; If for some reason you have a NestedRoom for which you always want lookDir to be handled by that NestedRoom rather than its enclosing location, the simplest way to make this happen would be to override its lookLocation(dirn) always to return self. For further details, consult the comments in the source code, (and the source code itself). A word of explanation on how NestedRooms are handled is in order. What lookDir does is first to decide which object will handle the LOOK DIR command (the NestedRoom or its enclosing location). Once that decision is made, it will determine which object's xxxLook, xxxLookDark and defaultLook properties are used. This decision can vary by direction or some other condition (so that, for example, a NestedRoom uses its own northLook property but defers to its location for everything else, or a Booth uses its own defaultLook method when closed but its location's properties when open), but once made, it does not change during the course of a single turn. Finally, the way this is set up should work correctly for NestedRooms nested within NestedRooms. For example, if a chair is put on a platform, and the platform defines a northLook property (but the chair does not), the platform's northLook property should be triggered by a LOOK NORTH command issued from the chair. Complication 4 -- SenseConnectors --------------------------------- If two rooms are joined by a SenseConnector (typically a DistanceConnector), you might reasonably expect looking from one room in the direction of the other to describe the other room and even, in an ideal world, to list the other room's visible contents. LookDir provides two methods to facilitate this: listRemoteContents(loc) and describeRemoteRoom(loc). The first of these simply lists the contents of loc as seen from the actor's current location (note that this only works if the actor can see the other location, i.e. if there's a suitable SenseConnector joining the two). The second method, describeRemoteRoom(loc) displays loc's roomRemoteDesc (which you'll need to have defined appropriately) and then lists its contents. For example, suppose we have a hall divided into two locations, northHall and southHall; to take advantage of these methods we could define them thus: DistanceConnector(northHall, southHall); northHall 'North End of Hall' 'the north end of the hall' "This large hall continues to the south. " south = southHall southLook { describeRemoteRoom(southHall); } ; southHall 'South End of Hall' 'the south end of the hall' "This large hall continues to the north. " north = northHall roomRemoteDesc(actor) { if(actor.isIn(northHall)) "The south end of the hall looks much the same as this end, only further away. "; else inherited(actor); } northLook() { "You look towards the north end of the hall. "; listRemoteContents(northHall); } ; + redBall 'red ball' 'red ball' "It's round and red. " ; This illustrates both ways of using these methods. In the case of northHall.southLook we leave southHall.roomRemoteDesc() to do the work of describing how the south end of the hall looks from the north end, so we only need to call describeRemoteRoom(southHall); For southHall.northLook, on the other hand, we have northLook display a suitable description and then use listRemoteContents(northHall) to list the contents of northHall. Which way you do it is entirely up to you. Reminder: these methods presuppose that the locations in question are joined by a suitable SenseConnector. If there is no such SenseConnector, describeRemoteDesc will simply show the other room's roomRemoteDesc property (without a list of contents) and listRemoteContents will do nothing. Note that if both locations are in darkness, LOOK NORTH etc. won't trigger northLook etc. in any case. You can experiment with using northLookDark etc. for this kind of case, although it's probably simplest for xxxLookDark to display a custom description for dark viewing conditions followed by a call to listRemoteContents() if there are likely to be any self-illuminating objects in the other location that might show up under dark viewing conditions. Final Notes ----------- Up to now we have assumed that the xxxLook and xxxLookDark properties must be either double-quoted strings or routines that display something. In fact they also work perfectly well if they're defined as single-quoted strings or routines that return single-quoted strings. This is mainly in the interests of fault tolerance (and because it was easy to do). The one situation where this might actually be useful is if you want to change one of these properties programmatically. For example, you might start out with: meadow: OutdoorRoom 'meadow' "This large meadow continues to the north, where you can see a tree. " northLook = 'Over to the north you see a tall tree. ' ; If the tree were subsequently cut down you could then use the statement: meadow.northLook = 'Over to the north you see the stump of a tree. '; Which you could not do if you had orginally defined northLook as a double-quoted string. Of course, there is a perfectly good way of achieving the same effect with a double-quoted string, e.g.: meadow: OutdoorRoom 'meadow' "This large meadow continues to the north, where you can see a tree. " northLook = "Over to the north you see <> tree. " ; But at least lookDir gives you the choice! I've separated out the language-specific parts of lookDir.t at the end of the file, but I haven't (yet?) separated them out into a different file. At present the best option for someone wishing to incorporate lookDir.t into a non-English game is to override (modify) VerbRule(LookDir), and the necessary properties in RoomPart and playerActionMessages with appropriate messages for their target language in a source file that comes after lookDir.t in their build. Since few messages are involved, this should not be too arduous. It is, however, conceivable that the way my code handles room parts contains assumptions that don't hold in your target language. For that reason I have separated this part of the code out into the LookDirAction.findRoomPart(dirn) method so that, if necessary, it can be overridden separately from the rest of the code. If you find any bugs, or have any comments on lookDir, please contact me on eric.eve@hmc.ox.ac.uk.