NOTE: This article has been depreciated along with all of the original ORLib Manual efforts. The newer manual can be found at http://www.onyxring.com/ORLibDocToc.aspx
Part D2: Advanced Library Entries (continued)
In this section we will continue our discussion of select library entries.
Like ORNPC_AskTellLearn, ORNPC_doverb is a component class. It leverages the ORNPCVerb module and can be used to give an NPC the ability to perform actions in much the same way as the player does.
In a traditional Inform program, NPC actions must be coded directly. In order for an NPC to pick up an object and eat it, code like the following might be written and called from the NPC's "orders" property when an Eat command has been issued to him:
The problem with this approach is that it doesn't enforce any of the rules that are enforced for the player character. A slightly better implementation would be:
Despite the extended code, this still isn't a complete implementation. No "before" or "after" routines are called to enable, for example, the rules a magic elixir would run. Nor are the "react_before" or "react_after" routines run which may interfere with the actions completely.
The previously discussed module ORNPCVerb modifies the standard library's verbs and a few other routines to enable them to work with the current actor, and therefore an NPC, rather than the player. The previously discussed module OREnglish modifies the default messages to support NPCs performing actions instead of the player. These two modules are requirements for this module, which actually enables NPCs to act.
When derived from the ORNPC_doverb class, an NPC's actions are initiated by the DoVerb() property-routine. For example, the following single line could accomplish what the above code does and still honor any rules like "before" and "after":
This module also adds additional support to NPCs for following orders, preventing the developer from having to code an "orders" routine to handle individual verbs with a call to "DoVerb". Setting the "will_follow_all_orders" property to true will turn this support on for every verb available to the player. Alternatively, the follow_orders property can contain a list of verbs that the NPC will follow. Similarly, a list of actions can be defined in the "ignore_orders" property. For orders that are not handled, the default message is issued, however orders can also be handled and specialized messages issued in the traditional manner via the "orders" property.
Below is our base NPC class as it exists in its entirety up to this point. Note the inclusion of ORNPC_doverb on the "class" line to include ORNPC_doverb in the class hierarchy. Also notice the handling of the "Kiss" verb in the "orders" routine, and the suppression altogether of the "Examine" verb:
Now we can boss the butler around (except for kissing and examining) and see how the same rules bind him as they do the player:
ORNPC_movement implements what was previously referred to as an "NPC action." As the name describes, this module gives the NPC the ability to initiate acts related to movement. Movement can be defined in a number of ways.
By default, when an NPC has made the decision to move, he will wander. That is, the NPC will walk around in any available direction from which he did not just come, and turn around to retrace his steps only when there are no other exits. Additionally, wandering NPCs will open closed doors as they pass through, possibly unlocking them if they hold the key.
7.2 Following a Path
As an alternative to wandering, a path can be defined for an NPC to follow in the "path" property list. An element of this list can either be a direction or a neighboring room. If a neighboring room is specified, then the direction in which the room lies is determined and an attempt is made by the NPC to walk that way.
By default, when the end of the defined path is reached, it is looped back to the beginning and started over. It is possible, however, to have an NPC retrace his steps and follow the path in reverse. This behavior can be implemented by setting the "reverse_at_path_end" property to true.
It should be noted that there are occasions where an NPC's path is made impossible to follow. Assuming the validity of the defined path, these occasions generally occur when an NPC's location has been changed by an external means. For instance, transporting an NPC to another location would do this, perhaps making the NPC "lost". When this occurs, an NPC will wander until it reaches a location that it recognizes (is in its path), and then resumes following its path from there. It is important to note, that an NPC’s ability to gracefully recover when diverted from its path is stunted by the use of direction objects in the path list rather than room objects. The NPC’s ability to recognize its location is limited to the rooms in the path list. Perhaps the most versatile method is to use a combination of both.
7.3 Following an Object
In addition to wandering around or following a path, there is one other option available for governing an NPC's movement. In particular, an NPC can be made to follow another object's movements. This could either be another NPC, or perhaps an object that the NPC carries. The "follow_object" property is used to specify the followed focus in code, but NPCs can also be ordered to follow an object. (See section 7.5 below.) When following something, an NPC's "path" list is ignored.
7.4 The Can_Move Property
Turning off an NPC's ability to move is accomplished by setting the "can_move" property to false, or by defining it as a routine which returns true or false depending on specific conditions.
7.5 The Follow and Stop Verbs
The verb "follow" has been implemented. Generally, this command issued by the player, when attempting to follow an NPC that has just wandered away. However, when issued as a command to an NPC that inherits from ORNPC_movement, a reference to the object is placed NPC's "follow_object" property and the NPC will follow it wherever it goes.
To stop an NPC from following another object, the "stop" order can be issued.
7.6 The Halt and UnHalt Verbs
Similar to the "follow" and "stop" verbs previously discussed, NPC can be ordered to stop wandering around or moving at all with the order "halt."
For our tutorial, let's go ahead and derive the maid from our ORNPC class as well. Both the butler and the maid were created early in this tutorial and could stand a little revamping to take advantage of some of the modules we've introduced. We won't cover the ORFirstThoughts-related modifications; however, note the "path" properties. We've assigned two separate paths to the two NPCs which converge in the Foyer so that the two may interact. The revamped NPCs are as follows:
Note also that the "reverse_at_path_end" property is set to true, so these NPC's will now reverse the path that they have been following. See how they return when their paths are completed:
The maid has returned first, let make her stick around and wait for the butler. We can exercise some of the new verbs from this entry, which thanks to the ORNPC_doverb module, can act as orders to our NPCs:
Notice that we have set up a little marching party. The maid follows the butler, who follows the player:
Now we can tell the butler to stop following us. On a technical point, the butler's path is all out of whack, hence the "disoriented" statement. Now he'll simply wander about until he recognizes his location. The maid will dutifully follow:
Typically, when we code an NPC to conditionally take actions, it is based upon world elements. We could, for example, cause the maid to pick up anything near her that is out of place. While this is a necessary technique for determining whether or not an NPC can or will take an action, there is an additional factor that influences people which has thus far not been addressed: their state of mind.
The maid might choose to not pick up an object if someone she does not like drops it, or not at all if she is simply in a bad mood. The ORNPC_moods class does not directly affect the NPC's behaviors; rather it provides a "mood system" for other modules to utilize. For NPCs that derive from this class, a state of mind is maintained and mechanisms for adjusting the NPCs frame of mind are provided. Additionally, these NPCs can remember how they feel about someone or something so that slapping an NPC, running away, and returning later does not result in instant forgiveness.
8.1 Agitating and Cheering Up an NPC
This class adds two routines to an NPC. The first, "Agitate", adds a little irritation to an NPC's mindset when called. The second, "CheerUp" makes an NPC a little less irritable and a little more disposed toward happiness.
Both of these routines take a parameter named "silent". When passed as true, the adjustment to the NPC's attitude is performed without feedback. This is useful when more than one call is made (when an event is really irritating). If "silent" is not true, then the appropriate message contained in either the property "cheerup_msg" or the property "agitate_msg" is printed.
8.2 The Mental State of an NPC
An NPC's attitude can be evaluated with a call to the property routine "MentalState". This routine will return one of five predefined constants:
It is important to understand that these values are a form of "mindset notation," a simplified representation, of the NPC's current mood. Aggravating an NPC in a "normal" state of mind will not necessarily make him "angry," but aggravate him enough and he will eventually become angry and finally cross the threshold into lividness.
8.3 Changing the standard of mindset
The "mindset notation" discussed previously is defined in a property routine called "FrameOfMind". It is this routine that translates a broad-ranged numerical value into one of the five notation constants. "FrameOfMind" can be overridden to redefine the ranges at which an NPC is considered "Happy", or "Angry".
8.4 Recalling and Recording Impressions
As we interact with the people around us, we remember our past experiences of them. We harbor grudges and play favorites based upon our likes and dislikes of the people we know, however it is seldom that we like someone by a logical choice. More often than not, we remember how we felt when last we interacted with them. To the point, we generally like or dislike people based upon how they make us feel.
Two routines are provided by NPCs that inherit from the ORNPC_moods class to implement this behavior. The first, "Record_Impression" takes an object as a parameter and will associate the way the NPC currently feels with that object.
As time passes, the NPC's mood may change, but his last impressions of an object, that is, how he felt at the time the impressions were made, can always be retrieved with a call to the routine "Recall_Impression."
Both of these routines deal with a snapshot of the NPC's mindset and the result of "Recall_Impression" can be passed through the "FrameOfMind" routine to transform it into mindset notation.
8.5 Effect by Recollections
Our impressions of things do more than determine our likes and dislikes. They also affect our current frame of mind. When coming across a person he dislikes, even if currently in a good mood, a person's frame of mind will likely be adversely affected.
The property routine "affect_from" takes an object as a parameter. This routine looks for the object in the NPC's likes and dislikes and makes adjustments to the NPC's mindset accordingly.
Opening doors and picking up non-animate items are all good actions for an NPC to perform, but interacting with other NPCs is something altogether different. Most interactions require the involvement of two people. Speaking to a person who is ignoring you isn't all that fulfilling. The ORNPC_interact class provides a series of routines to enable two characters to engage one another in some form of interaction. Note that the specific type of interaction is not defined by this module; rather ORNPC_interact defines a system which can be leveraged by other modules that define an interaction of some kind. ORNPC_converse is one example of this.
9.1 Engaging Characters
Whether it is kissing, fighting, or simple conversation there are countless ways in which two characters may interact. When a character attempts to initiate an interaction with another character, we say that he is "engaging" someone. It is not until both characters have engaged each other that they can truly interact. The "engage" property routine is provided by NPCs that inherit from this class. "Engage" takes a single parameter called "count". When "count" is true, then the routine will simply count up all the nearby characters with whom it might be possible to interact with and return that number. This is useful when attempting to determine if an engaging action is even possible.
When the "count" property is not passed in, then the routine will select a nearby NPC at random to engage. The property "interacting_with" is set to this character.
When it is determined that the character should do something else, he can cease current interactions with a call to the "disengage" property.
9.2 Acting in Concert
Many interacting actions are comprised of an initial action by the first character, and a responding action by the second character. For many of these types of interactions, such as conversing, it is appropriate to only act once per turn, regardless of who initiates the interaction. When a character has interacted, perhaps by answering a question, the "interacted" property routine can be called. This registers that the NPC has already acted this turn. Subsequently, the property routine "has_already_interacted" has been provided to determine if the NPC has already participated in an interaction with another character this turn, or if he is available to initiate an interaction of his own.
9.3 The Will_Engage_Player property
The "will_engage_player" property is set to true by default. This being the case, an interacting NPC will consider interacting with the player as well as other NPCs. Set this property to false to have the NPC ignore the player when considering a target for interaction.
A cousin to ORNPC_AskTellLearn, this module gives an NPC the ability to initiate conversations.
10.1 The Can_Converse Property
Although an NPC may derive from this class, the "can_converse" property effectively disables this action. An NPC who is tied up and gagged, for instance, would have this property set to false.
10.2 The Talk verb
A new verb called "Talk" has been implemented. Talk is very similar to the "Tell" verb except that it takes no topic to talk about. Instead, the "talk" verb selects a random topic which is passed off to the "Tell" command.
It should be noted that some effort is made to select an appropriate topic. For instance, only topics that the NPC knows about are considered. Additionally, the topic must have the "initiatable" attribute. Finally, the NPC must not have already talked to the player about the topic. This last step can be circumvented by the topic the "repeatable" attribute.
10.3 The Initiatable Attribute
The "Initiatable" attribute is given to properties that can be volunteered. Without it, NPCs would be especially closed-mouthed and refuse to share their knowledge with others.
10.4 The Repeatable Attribute
As was mentioned previously, an attempt is made to ensure that the talking NPC does not repeat himself. In the normal course of a conversation, topics that have already been discussed are ignored. There are some topics, however, that this is not appropriate for. For these routines the attribute "repeatable" can be given to them and they will continue to be brought up regardless of whether or not they have already been talked about.
10.5 The Current_Subject Property
Some ORKnowledgeTopics last more than one turn, especially ORKnowledgeScripts, derivation of ORKnowledgeTopics (see section 11). When a knowledge topic is selected, it is placed in the "Current_Subject" property. When it has been discussed to completion, that is, when the "HasBeenSpokenOfBy" routine returns true, it is removed and the "Current_subject" property is set to zero. While the "Current_Subject" property is not equal to zero, the ORNPC_converse action takes precedence over all other potential NPC actions.
Additionally, it should be noted that the dialog formatting routines discussed in section 1 "ORKnowledgeTopic" have all been implemented in this module.
For our tutorial...
Table of Contents
Part D1: Advanced Library Entries
Part E: Library Entries for Library Authors