# RemapCmd

## Introduction

The RemapCmd module offers a way to reinterpret players' input that lies midway between a Doer and a StringPreParser. This module has been provided by Mitchell Mlinar; this documention page is closely based on his.

The RemapCmd class provides the following functionality:

- Source phrase used for matching can be arbitrary text in that the words used do not need to be any known object (noun, verb, preposition, etc.)
- Source phrase can have variations using or ("|"), grouping with "(...)" and multiple phrases with ";"
- When the source phrase matches, we have several options for how this gets processed
  - Provide a complete phrase to replace it (this one, though, MUST use words understood by the game)
  - OR emit some set of messages to the console
  - OR redirect to some command
- In addition, the specified remap command could optionally be
  - location-sensitive
  - have a when condition
  - have a during condition (for Scenes)

## Usage

The adv3Lite library header file (advlite.h) provides the following template for use with the RemapCmd class:

`RemapCmd template 'cmd' @where? 'remappedCmd'?;`

RemapCmd has one source text 'cmd' that contains one or more phrases (separated by ';') that supports grouping via '(...)' and or via '|'. Here is an example (and, yes, most of this would work natively but this is just an example).

Suppose we have a verb HoldUp (hold up) that takes one object. However, light and ceiling are not nouns and we don't want to define them but want to handle the paper. When you write your source text, it is very similar to a VerbRule:

`RemapCmd 'hold paper up to light;hold paper against (|ceiling) light' @brightRoom 'hold up paper';`

Here, the first item ('hold up...light') defines the cmd property, the second (@brightRoom) the where property (where the actor must be for this RemapCmd to be matched) and the third ('hold up paper') the remappedCmd property (the string the parser will interpret to execute the command).

With this definition of the `cmd` property the RemapCmd will consider any of the following player inputs a match:

- hold paper up to light
- hold paper against light
- hold paper against ceiling light

If the player input matches one of these strings, the RemapCmd will replace that string with the command `hold up paper` — as if that is what the player typed in. However, it will ONLY do this if the player character's current location is `brightRoom.` (The `where` property can be defined as a Room, Region, or a list of Rooms and/or Regions.)

Another use case is to just emit some message(s) to the console. This is a great way to respond to some text without having to define all (or any) of the objects that entails writing some additional code to define words only used once. You do this by NOT specifying the remapped text and defining an execute() function on your RemapCmd object:

    RemapCmd 'spit (|(on the ground))'
      execute()
      {
        "How often have you told yourself to quit doing that... ";
      }
    ;

So now when you run this, here is what you see:

    >spit on the ground
    How often have you told yourself to quit doing that...

    >

Your may also use `doInstead(...)` within the `execute` method of a RemapCmd. It works much like using `doInstead()` in a Doer, for example:

    RemapCmd 'sing loudly'
      execute()
      {
        doInstead(Yell);
      }
    ;

As in a Doer, do in a RemapCmd `doInstead` can take between one and four arguments: `doInstead(action, dobj?, iobj?, aobj?)`. Also as in a Doer, you can use the macro `goInstead()` to bring about travel, e.g., `goInstead(east)`. There are, however, a couple of things to note here. First, `doInstead(...)` (or `goInstead()execute()` method. Second, the turn counter will be affected by what the redirected `doInstead(...)` command does and the turnsTaken property defined on your RemapCmd onject will be ignored.

We don't have to use `doInstead()` at all; within reason, we can use the `execute` method of our RemapCmd to anything we like. To adapt an example from the original Colossal Cave Adventure, we could, for example, do something like this:

    RemapCmd 'fee fie foe foo' @giantRoom
      execute()
      {
        eggs.moveInto(giantRoom);
        "The eggs you gave to the troll have mysteriously re-appeared on the floor in front of you. ";
      }
      when = eggs.isIn(troll)
    ;

RemapCmd defines the following properties/methods (of which the first three may be set via the template):

- cmd: The source text that defines the phrase(s) that should be remapped (as described above).
- remappedCmd: If present, the replacement command text; this must be a command your game processes.
- where: Set to nil by default, which means this remap command works everywhere. You can set this to a Room, Region, or list of Rooms/Regions where this remap command is active; it will then be inactive (and, thus, unavailable) outside of those Rooms/Regions.
- when: Set to true by default, but you can define specific condition(s) needed to be true for the remap command to be active.
- during: Set to nil by default, so active during all Scenes. You can set this to a Scene (or list of Scenes) where this remap command is active; it will then be inactive (and, thus, unavailable) outside of those Scene(s).
- turnsTaken: Normally, you would want any non-system command to take a turn, so this is set to 1 by default. However, if it is a system command or there is another reason to not count a turn, set this to 0. Note that if doInstead(...) is used, this property is ignored.
- execute(): This is only called if remappedCmd = nil and contains normal TADS3/Adv3Lite code.

NOTE: RemapCmd has a restriction on the source text phrase in that, outside of the special characters described above, it does not allow any other special or punctuation characters other than comma ',' or apostrophe (').

## Operation

RemapCmd parses up the cmd and does not make any attempt to map the words to existing objects in the game. Rather, it builds all the possible variations (by the use of "|", "(" and ")" operators) and then matches that against the user input FIRST before any other routines get a whack at it. You can also provide two or more disjoint phrases that are separated by semi-colon ";" to provide additional phrases that match.

Phrases can contain words enclosed in single quotes ('..') but you will need to use backslash as in \' within the text itself. This capability can be used to have a compound word with a space in it treated as one effective word.

RemapCmd takes advantage of string hashing to ensure high performance within your game.

RemapCmd modifies Parser.parse(str).

Finally, please note that RemapCmd takes effect before the parser has resolved the action and any objects involved in the command. That means that we cannot refer to `gAction`, `gDobj` or `gIobj` in any part of our RemapCmd definition, since these will be yet to be determined. In some cases this may make it difficult to define a `cmd` property that refers to the object we intend. For example if we have a crystal ball in one location and a football in other in our game and the player can refer to either object as simply 'ball' (which may seem quite unambigous to the player) a RemapCmd like the following may not always work as intended, especially :

`RemapCmd 'play with (football| (foot|) ball))' 'throw football';`

One can mitigate the risk here by adding a `when` condition, such as `when = gPlayerChar.canReach(football)`, but in cases of potential ambiguity like this it may well be safer to use a Doer or SpecialVerb.

adv3Lite Library Manual
Table of Contents | Optional Modules > RemapCmd
Prev: Pathfinding Next: Scenes