[Main]
[Previous]   [Next]

Remap

We have already encountered remapping via the remapTo macro; it's used when we want to remap a command on one object to a different command involving the same or maybe different objects (or even no objects at all). So, for example, on the tree object we earlier defined:
 
dobjFor(Climb) remapTo(Up)  
 
What such code actually does is to make the appropriate remap property return a list containing the action followed by the objects involved in the action; the example above is in fact equivalent to:
 
remapDobjClimb = [UpAction]  
 
A more complicated example was the cottage, where we defined:
 
dobjFor(Enter) remapTo(TravelVia, outsideCottage.in)  
 
which is equivalent to:
 
remapDobjEnter = [TravelViaAction, outsideCottage.in]  
 
Mostly, you can use the remapTo macro without worrying about the underlying code the compiler actually sees, but there are a couple of cases where understanding the underlying code can be important. The first thing to realize is that if there is a remap in operation (that is the remap property is non-nil) this will take precedence over all the other action properties (preCond, verify, check and action). In some cases this can lead to unexpected results: you may define verify, check and action for some verb on some object, but find that the object is doing something quite different from what you defined. The reason may very well be that your object has inherited a remap from one of its superclasses, and that this remap is taking precedence over your customizations of the other methods.

For example, the library class PathPassage remaps Take to TravelVia (since take the path normally means follow the path). But this has the unfortunate side-effect that pick up the path is also interpreted as traveling via the path. Suppose that you didn't want this in your game, so you defined:
 
 
modify PathPassage
  dobjFor(Take)
  {
     verify()
     {
       illogical('{You/he} can\'t carry {the dobj/him} around. ');
     }
  }
;
 
 
You'd find that this didn't actually affect anything; take path and pick up path would still result in the player character walking along the path, since the remap would still be in action. What you'd actually need to do is to reset the remap to nil:
 
modify PathPassage
  dobjFor(Take)
  {
     remap = nil
     verify()
     {
       illogical('{You/he} can\'t carry {the dobj/him} around. ');
     }
  }
;
 
In fact, if you set remap to nil, you might not even need to define the verify() routine, since PathPassage inherits from Fixture, and it's illogical to take a Fixture in any case. If you still wanted your custom message all you'd need is:
 
cannotTakeMsg = '{You/he} can\'t carry {the dobj/him} around. '
 
The second area of complication where it can be useful to know how the underlying code works is with conditional remapping. The library defines a macro called maybeRemapTo(), which only remaps if a certain condition holds (that is, if it's first parameter is true). For example, if you had a gate object, and you wanted PUSH GATE to be treated as CLOSE GATE when the gate was open, but in the normal way when the gate was closed, you could define:
 
gate: Door 'gate'  'gate'
  dobjFor(Push) maybeRemapTo(isOpen, Close, self)
;
 
The underlying code here is in fact:  
 
remapDobjPush = (isOpen ? [CloseAction, self] : inherited());
 
The first thing to note is that if the condition is not met (in this case, if the gate is not open), what one gets is not necessarily no remapping, but the inherited remapping. Often the inherited remapping is in fact nil, but it might not be. For example, suppose you defined a macro gVerbName that returned the first word of a player command; you might then try to modify PathPassage with
 
dobjFor(Take) maybeRemapTo(gVerbName == 'take', TravelVia, self)
 
Unfortunately, this wouldn't work as expected, since if gVerbName is not 'take', you get the inherited remapping, which still remaps Take to TravelVia even if the vocabulary used to invoke the command was 'pick up'. What you actually need in this case is:
 
modify PathPassage     
     dobjFor(Take)
     {
       remap = (gVerbName == 'take' ? [TravelViaAction, self] : nil)
     }
;

Another situation when it's useful to use the underlying remap property directly is where you want different remappings depending on circumstances. For example you might want PUSH GATE to open the gate when it's closed, and close the gate when it's open. This is beyond the ability of
maybeRemapTo, but quite possible with the underlying remap property:
 
gate: Door 'gate'  'gate'
  dobjFor(Push) 
  {
     remap = (isOpen ? [CloseAction, self] : [OpenAction, self] );
  }
;
 
By the way, note that when we're writing the 'raw' code rather than using the macro, we have to give the full name of the Action class, hence CloseAction and OpenAction rather than just Close and Open. One of the things the remapTo and maybeRemapTo macros do for us is to add 'Action' to the name of the action we want (e.g. Open or Close), but if we're not using these macros, we have to do it ourselves.


Getting Started in TADS 3
[Main]
[Previous]   [Next]