WelcomeWhat's NewsInform GuideORLibraryDownloadsLinksWebMaster@OnyxRing.com

This site
produced
656719
pageviews since
8/19/2004

Today's date:
10/18/2018


Author: Inform Moderator
Title: Part D2: Advanced Library Entries (continued)

Creation Date: 8/9/2004 6:11:47 PM
Last Updated: 8/9/2004 6:11:47 PM


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.

6. ORNPC_doverb

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:

 
 !—example of traditional code 
 [NPCEat npc obj; 
   print (The)npc," eats ",(the)obj,"."; 
   remove obj; 
 ]; 

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:

 
 !—example of traditional code 
 [NPCEat npc obj; 
   if(parent(obj)~=npc){ 
        if(obj has static or scenery) print_ret (The)obj " cannot be 
                picked up."; 
        print (The)npc" picks up ",(the)obj,"."; 
        move obj to npc; 
   } 
   if(obj hasn't edible) print_ret (The)obj " cannot be eaten."; 
   print (The)npc," eats ",(the)obj,"."; 
   remove obj; 
 ];


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":

 
 npc.DoVerb(##EAT,obj); 

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:

 
 Class MyNPC 
   class ORNPC_doverb ORNPC_asktelllearn ORNPC 
   with heartbeat[; 
           if(random(5)~=1) rfalse; !—only do this 1 in 5 turns (average) 
           if(CanPlayerWitness()==false) rfalse; 
           switch(random(6)){ 
                1: print (ig)CIVerb(self,"cleared","clear")," ",(my)self, 
                      " throat."; 
                2: print (ig)CIVerb(self,"gave","give")," a small sigh."; 
                3: print (ig)CIVerb(self,"looked","look")," around as though 
                      searching for something."; 
                4: print (ig)CIVerb(self,"scratched","scratch","scratches"), 
                      " ",(my)self," chin absent mindedly."; 
                5: print (ig)CIVerb(self,"hummed","hum"), 
                      " a tuneless melody."; 
                6: print (ig)CIVerb(self,"figited","figit"),"."; 
           } 
           rfalse; 
     ] 
   , will_follow_all_orders true 
   , ignore_orders ##Examine 
   , orders[; 
           Kiss: print (ig)CIVerb(self,"shook","shake")," ",(my)self,
                     " head. ~No,~ ",(ig)CIVerb(self,"said","say"),
                     ". ~I don't smooch on demand.~^"; 
           rtrue; 
      ] 
 ; 

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:

 
 >butler, kiss maid 
 The butler shook his head. "No," The butler said. "I don't smooch 
 on demand." 
 
 >butler, examine maid 
 The butler has better things to do. 
 
 >butler, go north 
 He wandered off to the north. 
 
 >n 
 
 Living Room 
 The living room was a comfortable sitting area. It was well decorated, 
 but lacked some of the luster and elegance found throughout the rest 
 of the house. To the south I could make out the foyer, and to the 
 north a dining area. 
 
 On the coffee table were a dish (in which were several dry candies) and 
 a glass bottle (which was empty). 
 I could see a butler there. 
 
 >butler, get candy 
 Taken. 
 >butler, inventory 
 The butler was carrying: 
 a piece of dry candy 
 
 >butler, eat candy 
 (the piece of dry candy) 
 The butler ate the piece of dry candy. 
 
 >butler, eat table 
 The coffee table was plainly inedible. 
 The butler cleared his throat.



7. ORNPC_movement


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.

7.1 Wandering

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:
 
 MyNPC -> butler "butler" has animate 
	with name 'man' 
	,	path foyer e_to staircase
	,	reverse_at_path_end true
	,	firsttime_pre "The butler was stuffy and had a 
	          pretentious air about him. "
	,	descrip "He barely acknowledged me, seeming 
	          preoccupied with other household-related things. "
 ;
	
 MyNPC -> maid  "cleaning lady" has animate female
	with name 'maid'
	,	path foyer livingroom
	,	reverse_at_path_end true
	,	descrip "Dressed in a long and conservative black maid's 
	          uniform, she is young, and slightly attractive. "
	,	firsttime_post "She seemed well-suited to her job by virtue 
	          of the fact that she seemed to be paying more attention to 
	          the dust (or absence of dust) than to the people around her. "
 ;


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:
 
 >z
 Time passed. 
 The butler wandered off to the east.
 The cleaning lady wandered off to the north.

 >z
 Time passed. 
 The cleaning lady entered from the north.


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:
 
 >maid, stay here
 The cleaning lady stood still.

 >z
 Time passed. 
 The butler entered from the east.

 >butler, follow me
 He looked at me and then nodded agreeably. 

 >maid, follow butler
 She looked at him and then nodded agreeably. 

 The butler grimaced at the cleaning lady. She gave him a wink.


Notice that we have set up a little marching party. The maid follows the butler, who follows the player:
 
 >n

 Living Room
 The living room was a comfortable sitting area.  It was well decorated, but 
 lacked some of the luster and elegance found throughout the rest of the house.
 To the south I could make out the foyer, and to the north a dining area. 

 On the coffee table were a dish (in which were several dry candies) and a 
 glass bottle (which was empty). 
 The butler entered from the south (following me).
 The cleaning lady entered from the south (following the butler).

 >n

 Dining Room
 The room around me was strangely bizarre in its decor.  It was elegant, true 
 to the rest of the house, and as large as any dining room I had ever been in.  
 Yet the porcelain tiling and glass tabletop gave it a cold, sterile quality 
 that I had instinctively come to associate with a breakfast nook. Possibly 
 this impression was reinforced by the presence of the walk-through kitchen to 
 the east. To the south I could see a much more comfortable looking room.

 On the glass table was a dictionary cube. 
 The butler entered from the south (following me).
 The cleaning lady entered from the south (following the butler).

 >e

 Kitchen
 The kitchen clean and well kept.  It was brightly lit by the glass room that 
 I could see to the east and cast light to the dining area to the west. 
 The butler entered from the west (following me).
 The cleaning lady entered from the west (following the butler).

 >e

 Sun Room
 This sitting room was adequately furnished with a small table and a couple of 
 chairs being centered in the room.  On its own merit, it would not have been 
 a room that people frequented having little that was especially inviting. By 
 the windowed wall to the north added an ambiance that was beyond measure.  The 
 view it provided gave a feeling of warmth and comfort making this a place that 
 put its occupants at ease and encouraged quiet conversation. A large glass 
 door led eastward to the beach. Almost unnoticeable relative to the view in 
 the other directions lay doorways to the south and west. 
 The butler entered from the west (following me).
 The cleaning lady entered from the west (following the butler).

 >open door
 I opened the glass door. 

 >e

 Beach
 The scenery rendered my thoughts of house murder all but forgotten.  Beautiful 
 and peaceful. My shoes were half buried in the sand.  Water lapped 
 continuously upon the shore merely an arm's reach away. 

 The butler entered from the west (following me).
 The cleaning lady entered from the west (following the butler).


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:
 
 >butler, stop following me
 The butler stopped paying attention to me and looked away.
 The butler cleared his throat. He looked a little disoriented for a moment. 
 He opened the dark door. He wandered off to the south.
 The cleaning lady wandered off to the south (following the butler).



8. ORNPC_moods

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:


NPCLivid
NPCAngry
NPCNormal
NPCHappy
NPCEcstatic


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.

9. ORNPC_interact

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.

10. ORNPC_converse

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...

11. ORKnowledgeScript


Table of Contents

Previous:
Part D1: Advanced Library Entries

Next:
Part E: Library Entries for Library Authors




Copyright 2004 - 2018 : Inform Moderator
OnyxRing.com has been given the potentially non-exclusive right to display the content of this article. However the original author retains all rights. Permission to reproduce this article -- either in part or in whole -- is left strictly to the discretion of the original author.