erb subroutines do it all. They enforce gravity; they differentiate between light and darkness; they decide where a character can travel. These little routines implement the laws of physics in our game world and govern what can and cannot be done. Yet for some reason, historically they only affect the player character. The game world laws that restrict the player do not bind NPCs. The following code exemplifies:
The guard has now been coded to respond to the "take" order and does so too well. In the above example, he will successfully "take" pretty much anything in scope, even things that he should not be able to:
Obviously this sort of "empowered taking" holds a very narrow slice of the "desired NPC abilities" pie. By convention, the filtering of what an NPC cannot do is also coded in the appropriate routine:
For the majority of Inform works, that's the way NPC action is accomplished. Piles and piles of example source code and sample NPCs follow this convention of manipulating the game world directly. There are some drawbacks to this, however:
Fear not, for there is another, more powerful way...
Redirecting to Verb Routines
A more intuitive solution is to leverage the verb routines themselves. Successful implementation of this method addresses all of the above drawbacks and actually makes the code more readable. At first glance, it seems a deceptively simple means of implementing NPC actions. Many new developers have posted questions to the RAIF newsgroup asking what was wrong with their code and why redirecting actions doesn't seem to work. The following is an example or redirecting an ordered action using the library's "action and return" operator (<<...>>):
In the initial test of this example, everything appears to work fine:
It is not until you look with a little more scrutiny that the flaw in this approach appears:
The library's verb routines presume that the player carries out all actions. The player is therefore the one that actually picked up the apple. This is unfortunate, but not insurmountable.
Actor, Good; Player, Bad
The standard library is glorious. It is, perhaps, the most useful collection of IF routines ever created. To look upon the Library's code is to be humbled.
Now then, having appropriately praised the library, we can now proceed with a slightly more critical discussion of it. In particular, if there is one area where I feel the library should have been written differently, it is library verbs and messages. Specifically, these two aspects of the library rely upon the "player" variable, rather than the "actor" variable. See how forcing the player variable to momentarily equal the actor variable almost fixes the above behavior:
Of course this is simply a patch, and it prevents us from completing our implementation of NPC actions, as we'll see below. A legitimate solution would replace the library's verb and verb-helper routines with slightly modified versions that check the actor variable rather than the player variable. We can do this pretty easily to the "take" and "eat" verbs by cutting-and-pasting the appropriate routines from verblibm.h and then running a search-and-replace against them to change references to "player" into references to "actor":
As shown above, both TakeSub and EatSub call additional routines that also need to be updated in the same fashion. These are AttemptToTakeObject and ObjectIsUntouchable. We won't reprint the modified versions of these two routines since they are long and the changes are minute, but the point should be made that a simple search-and-replace fixes these as well.
Adding the four modified routines to your source code and putting the appropriate replace directives at the top makes the take and eat verbs available to the NPC without having to change the value of the player variable.
Also, ObjectIsUntouchable is called by numerous other verbs, and TakeSub is called by derivatives verbs. By replacing these four routines, we have also automatically made available to NPCs, all of the following routines and thus their corresponding verb forms:
TakeSubSince so many verbs are available to the NPC we will now drop the use of the library's "action" operators (<...> and <<...>>) in favor of the library's more generic routine, ActionPrimitive. This allows us to forego addressing the implied switch(action) statement that exists in all "reaction" routines (such as orders, life, before, etc...) and redirect all orders to the appropriate verbs with a single line of code:
As we now have the ability to have the guard act for us, we can proceed with ordering him about and testing the affect of using "actor" instead of "player" in verb routines. Notice how drop, which has not yet been updated, does not function correctly:
What's with "YOU eat the apple?" It's understandable that the guard could not drop the apple since DropSub has not been updated, but why didn't the guard eat the apple?
Morphing Library Messages
Actually, the guard did eat the apple, but the default library message for the verb "eat" was written expecting the PC to be the only character eating. It, and many more library messages, will need to be changed to accommodate this whole new world of NPC actions. Usually, the recommend way of overloading a library message is to declare a LibraryMessages object between "Parser.h" and "Verblib.h" and implement it in the before routine. The following replacement for the "eat" message demonstrates this approach:
Notice that the message now changes form depending on who is performing the action:
The number of library messages that need to be changed in order to implement NPC actions is somewhat excessive. Because of this, duplicating the language definition file (English.h) and making modifications there (specifying the modified file on the Inform command line or in the ICL file) may be a preferable method to instantiating a LibraryMessages object.
Initiating NPC Actions
For the examples given above, we have been calling ActionPrimitive() from within the NPC's orders routine. This has been convenient because all of the necessary library variables have already been set up for us. When the player is not issuing orders at run-time, however, we need to set these values ourselves. Additionally, there are another couple of complications that we must address:
These issues can be addressed in a common routine, which seems to be most at home as part of an NPC base class:
Additionally, BeforeRoutines() and AfterRoutines(), and MoveFloatingObjects() should all be modified in the same fashion as was discussed above. One exception to this "player equals actor" rule is in the call to the player's ORDERS property in the BeforeRoutines() method. This section of code should only be run if the player and actor are the same, otherwise an endless recursion results the will blow the interpreter's stack. A slightly modified BeforeRoutine() which addresses this follows:
Assuming our NPC is derived from the NPC class, this addition will allow us to direct NPC actions from any section of code (like a controlling daemon):
A Peek Inside Pandora's Box
Perhaps the reason this technique is not often seen is the number of convoluted changes that need to be made in order to implement it. At times, implementation of this technique can seem horribly daunting.
The examples given above are far from complete. In addition to the excessive number of messages that must be modified, a quick scan through the verblibm.h file uncovers the following verb routines that need to be updated (only two of these were done in the above examples):
InvSubThere are also several additional routines what are called by these verb subs that also need to be updated. Further, in many of these routines, there are references to the library variables "location" and "real_location." These need to be changed to the library variable "actors_location."
Obviously a complete implementation of this technique requires major modifications to the library, but once done allows for greater flexibility with simplified, yet more powerful, coding capabilities. Additionally, since the "actor" variable and the "player" variable are normally the same, these changes are transparent. That is, the new behavior only occurs when the new functionality is used and does not affect existing code. Without calling NPC actions, games run as they always have.
A Note on Code
The examples above were given to demonstrate this technique, but they do not necessarily represent the best way to implement it. The definition of what is "best" is left up to the reader. As a suggestion, however, rewriting messages to change form can be a convoluted and monotonous task. The print rules discussed in §1 of this article series "Pronouns on Steroids" are particularly useful.
As a final point, it should be noted that a complete implementation of the techniques discussed in this article can be found at the ORLibrary in the entries ORNPCVerb.h, OREnglish.h, and ORNPC_doverb.h.
A special thanks goes out to Stephen Robert Norris, whose input and bug fixing in the ORLibrary necessarily impacted this article.
Wandering and NPC Movement
Conversation and Learning