There are really two parts to understanding the command system. The first is understanding the process of creating a command, and the second is just how commands are used and applied during the game. Much of the discussion here will revolve around the code found in command-system.
Commands are always added using the add-command or add-player-command functions. Each of these takes a specification, a description, and a function. Check the engine interface for those details.
Specification
The specification is what is matched against the player's input. If the specification agrees with the input, then the function in the command is executed, otherwise the command is skipped and we try to match the input with another command. You should note that only one command can be executed, even if several command specifications match the input. There are a total of four forms that a command specification can take. When matching the input, it first tries matching the raw input string against these forms:
- A string which is matched exactly
- A predicate which is applied to the input string
If neither of the above forms are used for the specification, the input is converted to a list and matched against the following:
- A list that holds scheme values or predicates. The elements from each list are matched one to one (so they have to have the same length). Values in the specification list and input must be equal. Predicates in the specification list are applied to the respective element of the input list, and must return #t.
A combined approach to writing a spec list would be:
- A nested list where :or is the first element followed by any number of specs just like the ones described above. If any of the nested specs match the input list, then the command is executed.
Some examples of specifications are:
- "quit"
- (lambda (s) (= (string-length s) 4))
- (list 'look symbol?)
- (list :or "look" (list 'look symbol?))
Help Description
This is simply a string that carries information that might be useful to display to the player. It is displayed whenever the player types help, or depending on the context when the player looks at an object. An example of a few help strings would be:
- "quit quit the game immediately"
- "help get the list of available commands"
Note, if the description string is just "", it is considered a hidden command because the player has no information about the command. Of course, the command can always be documented somewhere else, or the player can just try to guess.
Function
When a successful match is made with the spec, this function is applied to the player object and the input. Thus the function should probably begin with something like (lambda (plyr input) ...). Note that the input passed to this function will be in the same form as what matched the specification. So, if the specification was a string, a string will be passed as the input, and likewise with a list.
The function should do some type of effect depending on the command. For instance, a go command should call transfer. There's really no restriction at what the command can do - it can check the location and contents of the player, or anything else necessary. In the end, this function should return one of the following:
- Returning #f means that there will be no more input events. This essentially kills the player, as it will prevent them from interacting in the game. It's probably not a good idea to return this, unless the player object is also destroyed.
- Returning a string will result in it being printed out immediately followed by another input request. This is used primarily to mark errors in the way a command was formed.
- A number will indicate the next time that the player should be asked for an input. So 0 will result an immediate request for input, while a 5 will result in a five second delay for the next piece on input.
- Anything else, preferably #t, will result in an input request delayed by the default value.
Notice that every object in the game can carry a set of commands. This allows for contextual commands that show up only when the player is in or around the object holding the command. Looking at the code for commands in players, you'll note the hierarchy of commands for players. Commands have the following precedence:
- Commands stored in the player object
- Commands stored in the current location
- Commands of the exits
- Commands of the objects in the player's inventory
- Commands of the contents in the current location
- The global list of commands available to all players
- The default command (very last attempt)
So you could override a command by placing one with the same specs in anything with a higher precedence. You run the risk of seeing that command twice when typing help. You won't be overriding commands this way for the problem set, but you may find it handy if you try the contest.
Note that after completing the problem set, you will have a much more flexible way of assigning priority to commands!