The main thing that makes actors special is that they're supposed to be living, breathing people or creatures. That substantially complicates the programming of one of these objects, because in order to create the appearance of animation, many things about an actor have to change over time.
The ActorState is designed to make it easier to program this variability that's needed to make an actor seem life-like. The idea is to separate the parts of an actor that tend to change according to what the actor is doing, moving all of those out of the Actor object and into an ActorState object instead. Each ActorState object represents one state of an actor (i.e., one thing the actor can be doing). The Actor object becomes easier to program, because we've reduced the Actor object to the character's constant, unchanging features. The stateful part is also easier to program, because we don't have to make it conditional on anything; we simply define all of the stateful parts in an ActorState, and we define separate ActorState objects for the different states.
For example, suppose we want a shopkeeper actor, whose activities include waiting behind the counter, sweeping the floor, and stacking cans. We'd define one ActorState object for each of these activities. When the shopkeeper switches from standing behind the counter to sweeping, for example, we simply set the "curState" property in the shopkeeper object so that it points to the "sweeping" state object. When it's time to stack cans, we change "curState" to it points to the "stacking cans" state object.
Some authors might not like the idea of automatically suggesting topics every time we greet a character, but nonetheless wish to keep the TOPICS command as a sort of hint mechanism. This flag can be used for this purpose. Authors who don't like suggested topics at all can simply skip defining any SuggestedTopic entries, in which case there will never be anything to suggest, rendering this flag moot.
ActorState objects aren't actual simulation objects, so the 'location' property isn't used for containment. For convenience, though, use it to indicate which actor we're associated with; this lets us use the '+' notation to define the state objects associated with an actor.
For scripted behavior, it's sometimes better to use arrivingTurn() rather than this method to describe the behavior. arrivingWithDesc() is called as part of the room description, so it's best for any message shown here to fit well into the usual room description format. For more complex transitions into the new room state, arrivingTurn() is sometimes more appropriate, since it runs like a daemon, after the arrival (and thus the new room description) is completed.
This returns true if we wish to allow the conversation to end, nil if not.
The suggestions are arranged in a hierarchy, and each hierarchy level can prevent suggestions from a lower level from being included. The top level of the hierarchy is the ConvNode; the next level is the ActorState; and the last level is the Actor. Suggestions are limited at each level with the 'limitSuggestions' property: if true, suggestions from lower levels are not included.
- If our actor has a non-nil current conversation node (ConvNode) object, and the ConvNode wants to handle the event, let the ConvNode handle it.
- Otherwise, check our own topic database to see if we can find a TopicEntry that matches the topic; if we can find one, let the TopicEntry handle it.
- Otherwise, let the actor handle it.
'otherActor' is the actor who originated the conversation command (usually the player character). 'topic' is the subject being discussed (the indirect object of ASK ABOUT, for example). convType' is a ConvType describing the type of conversational action we're performing.
This can be used to update the actor's state after a 'follow' operation occurs; for example, if the actor's state depends on the actor's location, this can update the state accordingly. We don't do anything by default.
States representing scripted activities should override these to indicate what the actor is doing: "Bob is sweeping the floor," for example.
By default, we perform several steps automatically.
First, we check to see if the actor is in a ConvNode. If so, the ConvNode takes precedence. If we haven't been addressed already in conversation on this turn, we'll let the ConvNode perform its "continuation," which lets the NPC advance the conversation of its own volition. In any case, if we have a current ConvNode, we're done with the turn, since we assume the actor will want to proceed with the conversation before pursuing its agenda or performing a background action.
Second, assuming there's no active ConvNode, we check for an "agenda" item that's ready to execute. If we find one, we execute it, and we're done. The agenda item takes precedence over any other scripting we might have.
Finally, if we also inherit from Script, and we didn't find an active ConvNode or an agenda item that was ready to execute, we invoke our doScript() method. This makes it especially easy to define random background messages for the actor - just add an EventList class (ShuffledEventList is usually the right one) to the state's superclass list, and define a list of background message strings.