# LookDir

## Overview

The standard library provides only rudimentary support for directional looking commands (commands of the form LOOK NORTH). All it does is:

1. If there's an object in scope whose vocab matches the direction name speficied, such as a 'north wall' or a sky object with 'up' in its vocab, that object will be described (just as if the player had typed X NORTH or X UP).
2. Otherwise, if the player typed LOOK IN they'll be asked what they want to look in.
3. Otherwise a message will be displayed saying there's no need to look in any direction in this game.

For many games this may be perfectly adequate, but some games may benefit from a more elaborate implementation, which is what the LookDir extension aims to provide. With LookDir included in your game, the sequence of events in response to a directional looking command becomes:

1. If there's an object in scope whose vocab matches the direction name speficied, such as a 'north wall' or a sky object with 'up' in its vocab, that object will be described (just as if the player had typed X NORTH or X UP).
2. Otherwise, if the player typed LOOK IN they'll be asked what they want to look in.
3. If a dirLook property (e.g. northLook or portLook) is defined on the actor's location, it will be displayed to describe what can be seen in the direction requested (e.g., NORTH or PORT), provided there's light to see by (if not, the player will be told it's too dark). For example, you might define `northLook = "You see a long row of trees marching off to the middle distance. "`
4. Otherwise, if the direction was DOWN and the location provides a floor object, that floor object will be described.
5. Otherwise, the extension will attempt to generate a response based on what the corresponding direction property of the actor's location points to. For example, if the player issued the command LOOK SOUTH and the actor's location defines `south = frontDoor`, then we'll respond with "To the south you see the front door."
6. If all else fails, a message will be displayed saying you see nothing unexpected in that direction.

The automated response generated at step 5 may not always be ideal, but it may often be good enough for game authors to save themselves some work by only having to customize those responses it doesn't produce to their liking.

To use the LookDir extension you just need to include it in your build, after the adv3Lite library files.

## Details

### The xxxLook Properties

With the LookDir extension added to your game, you can add xxxLook properties to Rooms (or nested rooms) to provide the responses to LOOK XXX. For example, `northLook` would provide the response to LOOK NORTH, `southeastLook` to LOOK SOUTHEAST or LOOK SE, and `upLook` to LOOK UP. An xxxLook property may be defined as a single-quoted or double-quoted string or a method to display some text.

This is handled by the lookDir(dir) method, which first checks that there's enough light to see and complains that it's too dark otherwise. If there is enough light to see the method then checks whether the Room defines a non-nil dirLook property corresponding to dir. If it does, it invokes that property, otherwise it calls `describeView(dir)` to attempt to generate an automated response (more on which below).

The extension also defines the downLook method on Thing to change the action to examining the Room's floor object (floorObj) if there is one. This method may, of course, be overridden in game code if some other way of handling LOOK DOWN is wanted.

There's currently only limited support for looking in a specified direction from a nested room (i.e. when the player character's location isn't their outermost room). The extension calls lookDir(dir) on the actor's outermost room unless the actor is in a closed opaque Booth, in which case it will be called on the Booth instead.

#### Custom Directions

Adv3Lite allows game authors to define custom Directions. If you want LOOK XXX to work with your custom direction, e.g. LOOK NNE, you need also to define a lookProp property on your custom Direction to hold a property pointer for the lookXXX property you want your custom Direction to work with, for example:

    nornoreastDir: CompassDirection
        name = 'nornoreast'
        dirProp = &nornoreast
        sortingOrder = 1450
        opposite = sousouwestDir //assuming you were also defining a sousouwest direction
        lookProp = &nornoreastLook // ADD THIS to be able to use a nornoreastLook property on Rooms in your game.
    ;

    grammar directionName(nornoreast): 'nornoreast' | 'nne' : Production
        dir = nornoreastDir
    ;

In this case you may prefer to use a briefer property name, e.g., lookProp = &nneLook.

### The Automated Response - describeView(dir)

The `describeView(dir)` method is called on the actor's location if there is no xxxLook property corresponding to the direction specified in the player's command (e.g., no `westLool` property when the player typed LOOK WEST). It attempts to generate a passable response from what's defined on the corresponding direction property (e.g. `west` by following these steps:

1. If the relevant property value is a single-quoted string, it displays that string.
2. Otherwise, if it's a double-quoted string or a method, we first check if the destination is nevertheless known via libGlobal.extraDestInfo, say that that destination lies that way. Otherwise we just say it looks like you could go that way.
3. If the property contains an UnlistedProxyConmnector (from use of `asExit` it uses the exit it's a proxy for. E.g. if we have defined `out asExit(southeast)` then we'll work with whatever's defined on the `southeast` property.
4. If the property holds a Travel connector for which isHidden is true or isConnectorApparent is nil, we treat it as being not present by calling `sayNothingUnexpectedThatWay(dir)`
5. If the property hold a TravelConnector that defines a lookDirDesc property (or method) then that property will be used to construct the response; whatever text `lookDirDesc` defines will be preceeded by an introduction like "To the east you see ".
6. Otherwise, if the property holds a physical travel connector such as a Door, Passage or Stairway, we display a message stating that in that direction lies the physical connector in question (e.g., "To the north you see a red door"), unless the physical connector forms the way into an Emterable, in which case the Enterable will be mentioned first (e.g., "To the north you see a small white cottage which is enterable via a red door. ")
7. Otherwise, if the property holds an AskConnector, we list the physical connectors (doors and such) in its options list (e.g., "To the west you see the yellow door, the long passage and the broad flight of stairs").
8. Otherwise, if the property holds a Room that has been visited or is already familiar or is visible to the player (through being in the same SenseRegion), or an abstract travel connector leading to such a Room, we say that that Room lies in that direction.
9. Otherwise, if the property holds a TravelConnector not otherwise catered for or a double-quoted string or a method, call `sayCouldGoThatWay(dir)` to say we could go that way (since there's nothing more this routine can deduce about it.
10. Otherwise, if all else fails, call `sayNothingUnexpectedThatWay(dir)` to say we see nothing unexpected that way.

These responses may not always be ideal, but there are various ways in which can customise them:

- We can define the lookDirDesc property on any kind of TravelConnector to stipulate how we want it described when looking in the direction in leads; `lookDirDesc` can be a single-quoted or double-quoted string or a method that displays something (which we can vary according to circumstances). The standard behaviour of the library is to display "To the [whichever direction] you see " before the text output by lookDirDesc, but game code can change this by overriding introLookDirDesc(dir) on the player character's room (from where the looking is taking place).
- If we see clumsy prose like "To the south you can see edge of forest" it's probably because we haven't defined the relevant room's vocab property to give it a more suitable name (such as 'edge of the forest'. Where a room's `roomTitle` doesn't make a good name, this is probably worth doing anyway.
- If we see clumsy response to LOOK dir generated via a single-quoted string on the relevant dir property (e.g. LOOK WEST giving us "You can't walk on water") we could consider rephrasing it (e.g., to "That way lies the river, which you can hardly walk over"). The alternative would be to define the relevant dirLook property.
- We can override the Thing method sayNothingUnexpectedThatWay(dir) to say something different from the library default. This is probably most worth doing on on Thing, Room, or subclass of Thing such as Booth.
- We can override the Room method sayCouldGoThatWay(dir) to say something different. This could well be worth doing on the Room class, or on a subclasses of Room (if we create any for our game), but may be less worth doing on individual rooms unless we want to handle an unusual special case.

Note that it may often be easier to define the relevant dirLook property than to fiddle with the above methods to correct an individual case. On the other hand, there may be cases where overriding one of the methods outlined above could be less work, for example if we want the same response to apply to one different groups of directions, .e.g,:

    meadow: Room 'Meadow'
       "This meadow is bordered by a wide river to the south, a dense forest to the west, and a high fence to the north,
         leaving the path to the east as the only route out. "

        sayNothingUnexpectedThatWay(dir)
        {
           switch(dir)
           {
             case northDir:
             caae northWestDir:
             case northEastDir:
               "A high fence runs along the northern border of the meadow, blocking both egress and the
                  view in that direction. ";
                 break;
            case south:
            case southEast:
            case southWest:
               "A broad river runs along the southern boder of the meadow. ";
               break;
            case west;
               "A dense forest abuts the western edge of the meadow. ";
                break;
            default:
               inherited(dir);
            break;
          }
        }

    	east = meadowPath // assume this is elsewhere defined as a PathPassage
    ;

Note that we don't need to handle any `case east` here, since `describeView(dir)` will already have provided a message relating to the `meadowPath` object.

## Directional Looking from Nested Rooms

If the actor is sitting on a chair or standing on a stool, their view in any particular direction is probably little different from what they'd see if they were simply located in the outermost Room. On the other hand, if the Room is a theatre containing a Platform representing the stage at its northern end, the views north and south from the stage might be rather different from those from the rest of the Room (representing the auditorium). Again, if the actor is in a Booth it may well be that the sides of the Booth restrict what the actor can see in at least some directions, even if the Booth is open. There are a couple of things we can do to model such situations.

The first is to define dirLook properties on the nested room (such as the stage or the booth) which will then be used in preference to the corresponding dirLook properties on the enclosing Room when the actor is in the nested room. For example, on the theatre Room we might define:

        northLook = "To the north you see the stage, on which an incompetent troupe of actors are
           making a total hash of playing Hamlet. "
        southLook = "To the south you see the rows of seats behind you and the exit signs over the doors. "
    ;

While on the stage you could define:

        northLook = "To the north you see the backdrop depicting the battlements of Elsinore Castle. "
        southLook = "To the south you see the auditorium full of the most unappreciative audience
           you have ever played to. "
    ;

The situation with a Booth may be more complicated. Suppose we have a Booth representing a garden shed. Notionally, its door faces south across the garden to a greenhouse, while its (west-facing) window overlooks another part of the garden and the house. If the actor is inside the shed, they shouldn't be able to see what lies to the north or west of the garden, as they would be able to from outside the shed.

In this case we can define the shed's handleLookDir to be true, meaning that when the player character is inside the shed, we want to use the shed's version of `lookDir()` rather than the garden's. We could then define the shed's `lookDir()` method along the following lines:

            handleLookDir = true

            lookDir(dir)
            {
                switch(dir)
                {
                case westDir:
                    "\^<<windowView>>. ";
                    break;
                case southDir:
                    "<<if isOpen>><<doorView>><<else>>You see the inside of the shed door<<end>>. ";
                    break;
                case outDir:
                    "<<if isOpen>><<doorView>>, while <<else>>\^<<end>><<windowView>><<end>>. ";
                    break;
                default:
                    "That way all you can see is the inside of the shed wall. ";
                    break;
                }
            }

            windowView = 'through the window you see the section of the garden leading up to the house'
            doorView = 'Through the open door you see part of the garden and the greenhouse opposite'

By default, `handleLookDir` is true on Room and on any Thing we can't see out of, but game code can always override the latter to be unconditionally true in instances like our garden shed where we always want to use the nested room's version of `lookDir(dir)`.

The default behaviour of Thing's (as opposed to Room's) version of `lookDir(dir)` is to call `lookDir(dir)` on the next suitable container, if we can see out to one, or to display our own "see nothing expected" message (i.e. to call our own `sayNothingUnexpectedThatWay(dir)` method) if we can't see out. In this context 'next suitable container' means the innermost of the objects we're inside that's a Room, or from which we can't see out, or for which `handleLookDir` is true. For a Booth we'll almost certainly want to override `lookDir(dir)` if we want more realistic results, unless the Booth is meant to be see-through in every direction.

Adv3Lite Manual
Table of Contents | Extensions > LookDir
Prev: Fueled Light Source Next: MobileCollectiveGroup