The Actor Object

Thing-like Properties

The first and one absolutely essential step in defining an actor (or Non-Player Character, NPC) is to define an Actor object to represent that NPC in the game world. If you're defining an NPC of any complexity, one that involves a number of ActorStates, AgendaItems and TopicEntries, for example, you might wish to place all the code and objects relating to that NPC in a separate source file, rather than in the source file containing the game map, say. If you're defining a really simple NPC for which the Actor object alone will suffice, you probably don't need to do this.

The Actor class inherits from Thing, and you can thus define/override/use all the usual properties and methods of Thing on Actor, if you so wish/need. If, however, you plan to use ActorStates (which you don't have to) it makes better sense to define the corresponding properties on the ActorState (from which the Actor object will then obtain the appropriate value).

The common properties you will normally want to define on all Actors, in just the same way as you'd define them on Thing are:

A minimal Actor definition might thus look something like this:

george: Actor 'George; tall thin; man; him' @hall
    "He's a tall thin man. "    
   
    globalParamName = 'george'
;

We might additionally define the following properties of an Actor if we're not going to use any ActorStates with this Actor; otherwise one would define them appropriately on the ActorState objects:


Actor-Specific Properties

In addition to the properties the Actor class more or less shares with Thing are a number peculiar to Actor. The most commonly useful of these (from the point of view of the ones game authors might use) are:

There are also a number of properties and methods specific to actor conversation:

The purpose of some of the above should become more apparent when we get deeper into the conversation system. There are also a number of methods used for manipulated the Actor's agenda, whioh will be explained in the section on AgendaItems. Most of the other methods and properties of Actor are intended for internal use by the library, and would not normally be directly employed in game code.

Attacking, Touching and Kissing

The way Actor (and to some extent Thing) handles responses to ATTACK, TOUCH and KISS is slightly unusual. This is because while attacking, touching or kissing someone is not exactly the same as conversing with them, it is nevertheless a potentially complex form of interpersonal behaviour.

All three actions can be ruled out at the verify stage (on Thing or Actor) by setting isAttackable, isFeelable or isKissable respectively to nil. By default these are all true, except for isAttackable on Thing (on the basis that it doesn't normally make much sense to go around attacking inanimate objects).

All three actions can also be ruled out at the check stage (on both Thing and Actor) by setting checkAttackMsg, checkFeelMsg or checkKissMsg to some non-nil value (normally a single-quoted string, although a double-quoted string or a method that displays some text will also work). This will cause the action to be stopped at the check stage with a display of the message defined on the relevant property. The default value of all three properties is nil.

At the action stage, all three commands work differently on Actor from the way they do on Thing (since attacking, kissing or touching an inanimate object is rather different from attacking, kissing or touching a person). On the Thing class the result is to display the futileToAttackMsg (if attacking is allowed at all, by default it isn't since it's stopped at the verify stage so we'd normally see the cannotAttackMsg instead), the feelDesc, or the futileToKissMsg if it is non-nil (by default it's nil) or else the default message generated at the report stage, respectively.

On the Actor class, however, if any of these commands reach the action stage they are handled quite differently, on the grounds that a person will react very differently to being attacked, touched or kissed than an inanimate object. In the first instance, the ATTACK, TOUCH or KISS command will be handled by a HitTopic, TouchTopic or KissTopic if one is available; otherwise the attackResponseMsg, touchResponseMsg or kissResponseMsg (all of which should be defined as single-quoted strings) is displayed.

At first sight it may seem a bit confusing that some properties refer to 'feel' (e.g. feelDesc, checkFeelMsg) while others refer to 'touch' (e.g. touchResponseMsg). The pattern is that properties defined on Thing use 'feel' while those peculiar to Actor use 'touch'; the rationale of this is that the naming of the Thing properties follows the convention of using the action name, which is actually Feel, but that it's rather more natural to talk in terms of touching someone than feeling someone (hence touchResponseMsg and TouchTopic). However, to alleviate any confusion that might arise the library defines a number of macros that convert the plausible but wrong form of the property name into the correct one:

While these can't be relied upon in every single case, and it's better to use the correct names, these macros will probably cover the great majority of cases where the plausibly wrong property name is used, so that most of the time it won't matter whether, for instance, you use feelResponseMsg or touchResponseMsg as the property name, since in most common cases they'll end up meaning exactly the same thing.


ProxyActor

Defining an actor is not just a matter of defining a single Actor object, but also a number of other objects, such as ActorStates, TopicEntries, AgendaItems, and ConvNodes that define the actor's behaviour. For a very complex actor we may find that we have so many of these that we'd ideally like to split them over more than one file. The apparent difficulty is that all (or most) of these objects need to be directly located directly within the Actor whose behaviour they help to define. We could explicitly set the location property of all such objects to refer to an Actor object in another file, but this could soon become quite laborious, and may easily lead to errors if we slip back into using the + notation we're more used to. To address these problems we can use a ProxyActor object. This can be placed at the head of any second or subsequent file we're using to define the same actor, and then we can continue to use the + notation to locate ActorStates, AgendaItems and all the rest in the ProxyActor just as it if were the original Actor. The only other step we need to take is to set the ProxyActor's location property to the Actor it's standing in for, which we can do via the @ notation in the ProxyActor template, e.g.:

ProxyActor @guard    
;

+ guardAlertState: ActorState
   isInitState = true
   specialDesc = "A burly guard blocks your path, eyeing you suspiciously. "
   stateDesc = 'You certainly don\'t want to mess with him. '
   
   beforeTravel(traveler, connector)
   {
      if(traveler == gPlayerChar && connector == forbiddenDoor)
      {
          "The guard blocks your path with a warning snarl. ";
          exit;
      }
   }
;

+ guardAgenda: AgendaItem
   ...

;