Moods and Stances

Overview

In many games it may be enough (or more than enough) to track what various actors know and believe, but human beings have affective as well as cognitive properties, and if we're trying to create lifelike NPCs in our game, it may be that we want to track something about their emotional lives too. Moods and Stances in adv3Lite provide a framework for tracking each Actor's current emotional state (Mood) and (positive or negative) attitude towards the player character (and, if we so wish, towards other NPCs).

An Actor's mood and/or stance can be tested (and changed) in game code to affect the way the Actor behaves, for example in his or her converational responses or AgendaItems.

Moods

A Mood is a simple representation of an Actor's current emotional state. Moods are objects of the Mood class. Each Mood object has a programmatic name ending in Mood (e.g., happyMood) with a corresponding name< property giving the name of the Mood as a single-quoted string (e.g., 'happy'). A number of Moods come predefined in the language dependent part of the library (english.t): neutral, calm, happy, euphoric, contented, sad, depressed, angry, furious, afraid, terrified, confident, bold. lonely, bored, and excited. You can easily define more if you want them. For example, to define an irritable Mood you'd just use:

 DefMood(irritable);
 

This would create the following object:

 irritableMood:Mood
     name = 'irritable'
;

The Actor class defines the following properties and methods to support the use of Moods:

The default initial actorMood for an Actor is the value of libGlobal.defaultMood, which in the english library is neutralMood. It's done this way so that translators (or game authors) working in different languages can readily define their own default mood.

The library also defines the following macros to work with moods:

In each case the name parameter is the name of the Mood without the suffix 'Mood', so that, for example, you would use gMoodIs(happy) rather than gMoodIs(happyMood).

The macros with names starting with g all relate to getActor in contexts where getActor makes sense, i.e. in an ActorTopicEntry, TopicGroup or AgendaItem where getActor is the actor any of these belong to). To get or set the mood of any other actor, or in any other context, you need to use one of th macros beginning a and specify the actor parameter to denote the actor in question.

The macros gMood and aMood(actor) return the Mood object e.g. happyMood.

Moods can be used in various ways to customize NPC responses in various ways. Here's one very simple example:

++ AskTopic @bob    
     "<q>How are you today?</q> you enquire.\b
      <q>I'm quite <<gMood.name>>,</q> he replies. " 
;
 

More typically, we might do something like this:

++ AskTopic @bob
    "<q>How are you today?</q> you enquire.\b
    <q>I'm <<if gMoodIn(happy, euphoric, excited, confident)>>great<<else if gMoodIn( 
        sad, depressed, lonely, bored)>>not so good<<else>>okay<<end>>,</q> he tells you. " 
;
 

You can also extend the Mood framework in any way you think fit. For example, if you wanted to add strength and affect properties to Mood (so that Moods with the same affect could, for example, be grouped together and compared for their strength) you could do something like this:

#define ModMood(mood_, strength_, affect_)\
    modify mood_ ## Mood\
    strength = strength_\
    affect = #@affect_

modify Mood
    strength = 0 
    afffect = 'none'
;

ModMood(furious, 50, anger);
ModMood(angry, 25, anger);
ModMood(happy, 25, happiness);
ModMood(euphoric, 50, happiness);
;

// and so on
 

The macro expansion would then result in:

modify furiousMood
   strength = 50
   affect = 'anger'
;

// and so on
 

We don't do this in the library since we assume that different game authors will want to adapt the moods framework in different ways. The above example would allow embedded expressions (or other such tests) like "<<if gMood.affect == 'happines'>>happy text<<else if gMood.affect == 'anger'>> angry response<<else ...>>".

Stances

Stances provide a simple representation of the the (positive or negative) attitude an NPC has towards the Player Character (and also, if you wish to track it, towards other NPCs). Stances are not ActorState dependent, but they are Actor-dependent in the sense that an NPC's stance towards other actors in the game may obviously vary by actor. Unlike Moods, stances can be ranked by score, a crude measure of the degree of positivity or negativity an actor has towards the Player Character (and possible other actors).

Stances are objects of the Stance class. Each Stance object has a programmatic name ending in Stance(e.g., friendly) with a corresponding name< property giving the name of the Stance as a single-quoted string (e.g., 'friendly'), along with an associated score<, e.g. 20. A number of Stances come predefined in the language dependent part of the library (english.t); in ascending order of score (from -50 to 50) these are: loathing, rancorous, hostile, unfriendly, cool, neutral, lukewarm, friendly, warm, loving, and amorous. You can easily define more if you want them. For example, to define a cordial Stance with a score 25 of you'd just use:

 DefStance(cordial, 25);
 

This would create the following object:

 cordialStance: Stance
     name = 'cordial'
     score = 25
;

The Actor class defines the following properties and methods to support the use of Stances:

In the case of the final four of these, the Actor is excluded from the list under consideration (consider the case where Bob has a positive stance towards everyone else but his stance towards himself has been left at neutralStance; to learn that Bob likes himself the least probably isn't the information we'd be looking for here).

As with Mood, there are a number of macros that can be used with Stance; the following all concern other actors' stances towards the Player Character.

In each case the name parameter is the name of the Stance without the suffix 'Stance', so that, for example, you would use gStanceIs(friendly) rather than gStanceIs(friendlyStance).

The macros with names starting with g all relate to getActor in contexts where getActor makes sense, i.e. in an ActorTopicEntry, TopicGroup or AgendaItem where getActor is the actor any of these belong to). To get or set the stance of any other actor towards the player character, or in any other context, you need to use one of th macros beginning a and specify the actor parameter to denote the actor in question.

The macros gStance and aStance(actor) return a Stance object e.g. friendlyStance.

Because Stances have an associated score, they can be ranked and compared by score. The following operators are available to compare Stances:

(The last three of these operators were chosen in place of the more conventional ones as being the closest available operators TADS can override).

There are a further trio of Stance operators which may perhaps be useful to some game authors:

If you need to set up a fair number of attitudes at the start of play, you can make use of the stanceInitializer object, modifying it to define a list of stances, each stance entry being a list of the form [actor1, stance, actor2], meaning actor1 starts out holding stance towards actor2. To avoid having to keep typing 'Stance' at the end of each stance name, you can make use of the Sta macro, for example:

 modify stanceInitializer
    stances = [
        Sta(me, friendly, bob),
        Sta(bob, warm, me),
        Sta(bob, amorous, mavis),
        Sta(mavis, loving, bob),
        Sta(me, lukewarm, mavis),
        Sta(mavis, cool, me)
    ]
; 
 

Note that you don't need to add any entries for the neutralStance, since this is the default.

A simple example of what can be done with Stances might be varying greeting responses according to stance:

++ HelloTopic
    "<q>Hello, Bob,</q> you say.\b
    <q>Hello,</q> he replies. "
;

+++ AltTopic
     "<q>Hi there, Bob,</q> you say.\b
    <q>Hi, great to see you!</q> he declares. "
   isActive = gStance >> neutralStance
;

+++ AltTopic
    "Hello, you say.\b
    In reply Bob merely grunts. "
    isActive = gStance << coolStance    
;
 

Or you could do something like this:

++ QueryTopic 'how Mavis feels about you; does feel me'
    "<q><<if aStanceIn(mavis, neutral, lukewarm, warm, friendly, loving)>>I think she quite likes
    you<<else>>I'd rather not say<<end>>,</q> he tells you. "
;
 

Or as an extreme example of using a Stance in connection with an AgendaItem:

 bobLoathingAgenda: AgendaItem
    invokeItem()
    {       
        "Bob snarls as you enter the room. <q>I <i>hate</i> you</q>!\b
        So saying, he snatches up the nearest heavy blunt object and smashes 
        it down on your head with all the force he can muster.\b";
        
        finishGameMsg(ftDeath, [finishOptionUndo]);        
    }    
    
    agendaOrder = 50
    initiallyActive = true
    isReady = gStanceIs(loathing) && gRoom == getActor.getOutermostRoom    
;

 

No doubt game authors will be able to come up with many more ingenious uses for Moods and Stances than the examples given here, but hopefully we have suggested enough to get you started.