adv3Lite: Checking Available Departure Directions

My NPC is following the PC around. I want the output text to mention that she is traveling with you immediately after your travel command, not at the end of the room description, like this:

I do this by using regionBeforeAction() (she only follows you within a given region) and checking if you’re traveling:

if (!gActionIs(Travel)) return;

Once we’ve passed that stage, we know the player is doing a travel action, so (subject to certain constraints) we print a “comes along with you” message. This works fine, as long as the player only tries to go places or directions that can actually be gone. But if the player inconsiderately tries something that doesn’t work, I get output like this:

This obviously can’t be right. So what I need is a way to sense whether the direction properties of the current room are (a) nil or (b) a locked door. Needless to say the answers to these questions will be different in just about every room in the region. In some rooms, north will be a simple pointer to another room. In another room, north might be nil, or a door, which might be locked or unlocked.

I’d really rather not write a mess of code with an if-test for every single room in the region. What I need is to add a method to the Room class that will figure out whether gTravelActionIs(dir) points to a local method that is a safe travel direction or not. This can probably be done by evaluating the type of the direction property, but I’ve never tried doing that. How would I do that?

It appears that execCycle should set the direction variable to the current direction of travel, but when I try to use that variable in my roomBeforeAction, it has a value of nil … which doesn’t make a lot of sense, because at this point the library knows that the action is a travel action, and can test things like “if (gTravelActionIs(north))”.

You could start by looking at (a) the showExitsWithLister() method of the exitLister object and (b) the doTravel() method of the TravelAction class to see how they handle analogous problems with exits. It may be you can find something you can adapt from one or the other of those.

In essence, if you have a Direction object (an object of the Direction class), its dirProp property gives you the property pointer for the corresponding property of the Room you’re interested in travelling from (e.g. &north). To find out what sort of thing that property points to you use the propType method of the Room, thus:

   loc.propType(dir.dirProp)

Where loc is the current location and dir is the Direction object you’re interested in (such as northDir). If this evaluates to TypeNil there’s no exit in that direction. Likewise if it evaluates to TypeSString or TypeDString there’s again no exit, just some text to display. If it evaluates to TypeCode (i.e. a method) the exit may or may not go somewhere, depending on what you’ve put in the method. You can see how the exitLister handles this case.

The most interesting case is when the propType evaluates to TypeObject. You probably then need to get at the object to see whether it’s another Room, a TravelConnector or a Door. If it’s a Door you can test whether or not it’s locked. If it’s a TravelConnector you can test whether it’s canTravelerPass() method returns true or whether it has any travelBarriers. As you can see it’s likely to be quite complicated to cover every possible case!

An easier approach might be to use the beforeTravel(traveler, connector) method on Betsy’s ActorState, which will only be fired if travel via connector looks possible. You can then test for whether connector is a locked door before displaying your message.

I’ve been looking at doTravel and trying to use dirProp and so forth. I can’t make heads nor tails of it. Do I have a Direction object? I don’t know. How would I determine the current Direction object in order to test its dirProp? the variable direction doesn’t seem to refer to a Direction object. At any rate, direction.dirProp produces a nil object reference.

Since I haven’t seen your code it’s hard for me to comment on that. But I still think it would be easier to use the beforeTravel() method of the accompanying PC’s ActorState, as I also suggested.

If you must use regionBeforeTravel() then the direction object can be obtained from gAction.direction (once you have established that gAction is a Travel action). You could then do something like:

regionBeforeAction()
{
    if(gActionIs(Travel))
    {
         local loc=gActor.getOutermostRoom();
         local dir = gAction.direction;
         switch(loc.propType(dir.dirProp))
         {
              case TypeNil:
              case TypeSString:
              case TypeDString:
                 /* Do nothing; no travel is possible */
                 break;
              case TypeCode:
                 /* We have a method; what you do here depends on how
                     you've used methods attached to direction properties,
                     but it may be we just do nothing. */
                  break;
              case TypeObject:
                   /* We presumably have some kind of TravelConnector */
                   local conn = loc.(dir.dirProp);  // obtain the connector object

                   /* If travel is possible and the connector is not locked, display our message */
                   if(conn.canTravelerPass(gActor, self) && ! conn.isLocked)
                      "Blah blah";
                   break;
         }    
    }
}

But I really don’t advise doing this since it’s complex and won’t catch cases where travel is carried out by commands to go through doors or up and down stairs and so forth. It really would be much more robust to use beforeTravel() on the ActorState.

ADDENDUM

Perhaps what’s needed is regionBeforeTravel() and regionAfterTravel() methods defined on the Region class; something like this:

modify Region
   
    /* 
     *   This method is called just before travel takes places in this
     *   region.
     */
    regionBeforeTravel(traveler, connector) { }
       
   
    
    /* Method called just after travel has taken place in this region. */
    regionAfterTravel(traveler, connector) { }
;

modify TravelConnector
/* Carry out the before travel notifications for this actor. */
    beforeTravelNotifications(actor)
    {

        inherited(actor);

        /* Carry out before travel notifications in all affected regions */
        foreach(local reg in actor.getOutermostRoom.allRegions)
            reg.regionBeforeTravel(actor, self);
        
    }
    
    /* Carry out the after travel notifications for this actor */
    afterTravelNotifications(actor)
    {

        inherited(actor);       
        
        /* Carry out after travel notifications in all affected regions */
        foreach(local reg in actor.getOutermostRoom.allRegions)
            reg.regionAfterTravel(actor, self);      

    }
;

I haven’t been able to test this yet, but you could try playing with it. I think it should be better than using regionBeforeAction() at any rate, and when I get a chance I’ll consider adding it to the library.