Travel Connectors and Barriers

TravelConnectors

In addition to Doors, the direction property of a Room can point to a TravelConnector (a Door is in fact a particular kind of TravelConnector). A TravelConnector is a kind of object that allows you to define:

The methods and properties that enable a TravelConnector to be used for these purposes are as follows:

Note that the parameter of canTravelerPass() etc. is called traveler: it will normally be the actor doing the traveling but it could also be an object that an actor is attempting to push through this TravelConnector (via a command like PUSH TROLLEY NORTH, for example). This means that canTravelerPass(traveler) can also be used to selectively prevent the pushing of objects in certain directions; for example, you might want to use this method to prevent a heavy object being pushed up a flight of stairs.

An example of a TravelConnector which conditionally blocks travel could be one that only allows travel down a smoke-filled passage if the player is wearing a wet blanket, which could be defined thus:

landing: Room 'Landing' 'landing'
    "The smoke is already becoming so thick here that it's hard to see much.
    Your bedroom lies to the north -- if you can make your way through the
    smoke. Most of the other upstairs rooms are down the passage the other way,
    to the south, but the worst of the smoke seems to be coming from there. "
    
    down = landingStairs
    
    north: TravelConnector
    {
        destination = bedroom
        
        travelDesc = "You manage to force your way through the smoke, coughing
            and choking as you go. "
        
        canTravelerPass(actor)
        {
            return blanket.wornBy == actor && blanket.isWet;
        }
        
        explainTravelBarrier(actor)
        {
            if(blanket.wornBy == actor)
                "You take a few steps down the corridor but the smoke forces you
                back as the blanket starts to get singed. ";
            else
                "The smoke is too thick; you find yourself coughing and choking
                after the first step and are forced to retreat. ";
        }
    }
    
    
    south  { "The smoke is too thick that way; you almost choke to death
        with the first step south you take. Well, it's not as if there's
        anything down there you really need all that much right now. "; }
    
    regions = upstairs
;

This incidentally illustrates that if we only want to use a particular TravelConnector once, we don't need to define it as separate named object, since we can instead define it as an anonymous nested object directly on the appropriate direction property.

Note that a TravelConnector only establishes a connection in one direction: the TravelConnector defined on the north property in the example above creates a direction north from the landing to the bedroom, but no connection back south from the bedroom to the landing. Often, as here, that's what we want (since we wouldn't want the same travel restrictions to apply to the attempt to return from the bedroom to the landing), but if you do want a TravelConnector that works the same both ways, you could try using the SymConnector defined in the symconn extension.

TravelBarriers

For many simple cases conditionally preventing travel by using the canTravelerPass() method of a TravelConnector will suffice, but there may be situations where it's not the most convenient way to do it. The two most common situations where this might arise are where:

  1. You want to apply the same condition (and display the same refusal message if it's not met) to a number of TravelConnectors, and it would be tedious to have to define the same canTravelerPass() and explainTravelBarrier() methods on each of them.
  2. Several different conditions apply on a TravelConnector, each requiring a different explanation if it's not met, so that the explainTravelBarrier() method would need to contain a messy mass of if statements that largely duplicate those in the canTravelerPass() method.

If either or both of those two conditions obtain, you might be better off defining and using a TravelBarrier object to represent the conditional barrier to travel. There are basically two methods you need to define when creating a TravelBarrier object:

Note the additional connector parameter on these methods. This is most likely to be useful when you want the explainTravelBarrier() method to name the connector that's being blocked. For example, suppose our game had several exits that required the player character to be wearing a wet blanket in order to penetrate the smoke. We might set it up thus:

landing: Room 'Landing' 'landing'
    "The smoke is already becoming so thick here that it's hard to see much.
    Your bedroom lies to the north -- if you can make your way through the
    smoke. Most of the other upstairs rooms are down the passage the other way,
    to the south, but the worst of the smoke seems to be coming from there. "
    
    down = landingStairs
    
    north: TravelConnector
    {
        destination = bedroom
        
        travelDesc = "You manage to force your way through the smoke, coughing
            and choking as you go. "
        
        theName = 'the corridor'
		
        travelBarriers = [smokeBarrier]
    }
    
    
    south: TravelConnector
    {
        destination = bathroom
	   
        travelDesc = "With the aid of the blanket, you are able to make your way 
	     through the smoke to the bathroom. "
		
        theName = 'bathroom passage'
	   
        travelBarriers = [smokeBarrier]
    }	
    
    regions = upstairs
;

smokeBarrier: TravelBarrier
        canTravelerPass(actor, connector)
        {
            return blanket.wornBy == actor && blanket.isWet;
        }
        
        explainTravelBarrier(actor, connector)
        {
            if(blanket.wornBy == actor)
                "You take a few steps down <<connector.theName>> but the smoke forces you
                back as the blanket starts to get singed. ";
            else
                "The smoke is too thick; you find yourself coughing and choking
                after the first step and are forced to retreat. ";
        }
;

Note how we've added a custom theName property to the two TravelConnectors so that smokeBarrier.explainTravelBarrier(actor, connector) can refer to them.