Aller au contenu

Photo

Generic NPC Conversation Scripting


  • Veuillez vous connecter pour répondre
30 réponses à ce sujet

#26
Pstemarie

Pstemarie
  • Members
  • 2 745 messages

I was working on a portion where the PC can frighten the NPC and found a problem with the default state script nw_g0_fear.

 

nw_g0_fear only forces the frightened creature to run away from enemies rather than the person who applies the frighten effect to the subject. One potential solution to this would be to find the creator of the frightened/fear effect, but I wasn't sure if this would eat up too many resources on each heartbeat. So instead I created a local integer flag to check for.

int GetCreatureIsAfraidOfTarget(object oCreature, object oTarget)
{
    return GetLocalInt(oCreature, "AI_AFRAID_"+ObjectToString(oCreature));
}

The downside of this is that I have to delete the flag with a delay if the fear has a particular duration. Say a Delay of 30 seconds.

 

So which is worse? Having a 30 second delay or once a heartbeat iterating through all the affects on an NPC looking for the creature which caused the fear effect in the first place?

 

What about doing something like this...

void main()
{
    int nFear = GetHasEffect(EFFECT_TYPE_FRIGHTENED);
    object oCreator = GetEffectCreator(nFear);
 
    SendForHelp();
    //Allow the target to recieve commands for the round
    SetCommandable(TRUE);
 
    ClearAllActions();
    int nCnt = 1;
 
    //Get the nearest creature to the affected creature
    object oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, nCnt);
    float fDistance = GetDistanceBetween(OBJECT_SELF, oTarget);
 
    while (GetIsObjectValid(oTarget) && fDistance < 5.0)
    {
        if (oTarget == oCreator)
        {
            //Run away if they are the source of the fear effect
            ActionMoveAwayFromObject(oTarget, TRUE);
            break;
        }
 
        //If not an enemy interate and find the next target
        nCnt++;
        oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, nCnt);
    }
 
    //Disable the ability to recieve commands.
    SetCommandable(FALSE);
}


#27
henesua

henesua
  • Members
  • 3 863 messages

To answer both of you let me clarify what I did:

if(     fDistance <= 5.0
    &&(     GetIsEnemy(oTarget)
        ||  GetCreatureIsAfraidOfTarget(OBJECT_SELF,oTarget)
      )
  )
{
    //Run away
    ActionMoveAwayFromObject(oTarget, TRUE);
    break;
}

I kept the behavior more or less the same, but in addition to commanding creatures to flee from enemies they also flee from anyone they are afraid of. The token check I mentioned earlier is wrapped in that function, GetCreatureIsAfraidOfTarget(), which allows me to change the method of establishing whether an NPC is afraid someday in the future. At present its just a token, but I can incorporate a graduated "fear" scale if I want rather than a binary flag.

 

I went with this rather than searching for the creator for a few reasons.

  1. it is more efficient than a loop through all effects on the creature. (Aside: I don't believe what you posted works, Pstemarie, because the return value of GetHasEffect() is only a 0 or a 1 as far as I know. It certainly isn't the special struct, "effect", which I believe is what is needed in GetEffectCreator().)
  2. I was thinking that when a creature is afraid, it flees from anything that is a threat. Fear is not necessarily so specific as to be limited to a single target, but instead I am thinking of fear as an overwhelming emotion which dominates one's outlook and response to any threat.
  3. In the conversation I track a variable labeled "nerve" on the NPC. When the PC reduces the NPC's nerve to 0, they are very easily frightened. Another thing I want to do is test "nerve" in combat - especially if an NPC becomes a henchman. When nerve breaks in a combat situation they would flee. Basically I could have simply called this "morale" to use D&D terms, but I didn't think of that when I set this up.

  • Rolo Kipp aime ceci

#28
henesua

henesua
  • Members
  • 3 863 messages

I just laid the ground work for religious conversion and am amazed at how complex something like this can get.

 

I had to consider:

  • whether one religion considers another an enemy
  • whether the conversion is though intimidation (conversion by the sword) or persuasion, and have not yet worked out how bluffing will work
  • whether the NPC is a devoted follower of a religion, a shallow follower of a religion, or undevoted to any religion
  • whether the NPC and PC are already in similar faiths, but not exactly the same

... and so on.

 

A bit much. All of this is due to the fact that I allow one religion to be a parent of another, and I have introduced whether religions can be enemies. This is still only a caricature of reality, but I realize it is not worth adding any complexity further because the whole thing could collapse under its own weight.



#29
henesua

henesua
  • Members
  • 3 863 messages

I have been plugging away at this z-dialog script off and on for almost a month now. Getting close to usable for the module I am working on. I updated the first post in this thread with a link to my zdlg script on pastebin.
 
Yesterday I actually had a little bit of time to work on this uninterrupted and got two conversation options working:

  • [Ask for information]
  • [Make small talk]

I'm also working on merchant and house keeper sections of the dialog. Merchants are done. The house keeper is coming along. The way these work is that if an NPC is flagged with either of these roles, this section of the dialog starts at the beginning of a conversation, but a PC has the option to drop out of them to proceed with generic conversation.

 

 

[Ask for information]
has one core function which returns generic information about the area. at present this is limited to the name and location of a merchant, and a nearby innkeeper. those are things i typically ask for when travelling so i figured it made sense to allow the "people on the street" to supply the same information.
 
I am a little proud of a loop within my function, string DlgGetInformationOverview(object oPC); This is not fancy, and seems to work, but if others have a better way of finding areas connected to the current area I'd like to hear it. Basically I am looping through all objects in the area and looking for transition targets to a different area than this one. If I find an area that contains housing, the NPC tells the PC the direction to the transition to the housing.

        // we are not in "housing", lets look for Public Housing in an adjacent area

        object oHouse, oDest, oDestArea;


        object oTrans   = GetFirstObjectInArea(oArea);
        while(GetIsObjectValid(oTrans))
        {
            oDest    = GetTransitionTarget(oTrans);
            if(oDest!=OBJECT_INVALID)
            {
                oDestArea   = GetArea(oDest);
                if(oDestArea!=oArea)
                {
                    sHouseTag = GetLocalString(oDestArea,"HOUSE");
                    if(sHouseTag!="")
                    {
                        oHouse = GetWaypointByTag(sHouseTag);
                        if(GetLocalInt(oHouse,"HOUSE_PRIVATE"))
                            oHouse  = OBJECT_INVALID;
                        else
                            break;
                    }
                }
            }
            oTrans  = GetNextObjectInArea(oArea);
        }
        if( oHouse!=OBJECT_INVALID )
        {
            sOverview   += GetName(oHouse) +" "+ DlgGetLocationDescription(oDest) + "offers lodging.";
        }

[Make small talk]
First checks both the bluff skill and the persuade skill against a DC. If full or partial success is achieved, a random selection from a list of "small talk" dialogs set on the NPC in local strings is spoken. Otherwise the NPC emotes their boredom of the PC's small talk.
 
I like how this works in play. I created a function for getting small talk so that I can later change the method. Currently I am using local strings set on the NPC, but it would be very cool to move conversation snippets to a MySQL database and then create a web interface for generating and categorizing "small talk". I think some of these could be rumors too even though small talk should mostly be flavor text, and I should add a rumors list or "crunchy" information bit for [Ask for information]

string DlgGetSmallTalk(object oPC, int nSmallTalkCount)
{
    string sSmallTalk   = GetLocalString(   OBJECT_SELF,
                                            "DIALOG_SMALLTALK_"
                                            +IntToString(Random(GetLocalInt(OBJECT_SELF,"DIALOG_SMALLTALK_COUNT"))+1)
                                        );
    if(sSmallTalk=="")
    {
        switch(d4())
        {
            case 1: sSmallTalk="*Chats about the weather.*"; break;
            case 2: sSmallTalk="*Jokes with you.*"; break;
            case 3: sSmallTalk="*Gossips with you.*"; break;
            case 4: sSmallTalk="*Shares an anecdote.*"; break;
            default:break;
        }
    }

    return sSmallTalk;
}

  • Proleric, Rolo Kipp et meaglyn aiment ceci

#30
henesua

henesua
  • Members
  • 3 863 messages

I've been making slow progress on Inn/House Keepers, and thus had to revisit my Persistent Inn System which still needs a fair amount of work as I had neglected to complete a number of key functions.

 

Since this is getting fairly complicated I abstracted out the conversation mode checking/establishing function. This shows the main branches of the conversation at this point, although each of those categories allows drilling down into them as well.

void DlgSetStartingPage(int bConvContinue, int bUnequippingWeapon=FALSE)
{
    object oPC          = GetPcDlgSpeaker();
    int nNPCInterest    = DlgGetNPCInterestInConversation(oPC);
    int nConvLang       = GetCurrentLanguageSpoken(oPC);

    if(     !bUnequippingWeapon
        &&  !GetLocalInt(oPC, "CONV_WEAPON_WIELDED") // a PC can continue a conversation with the weapon wielded
        &&  GetIsWieldingWeapon(oPC)
      )
    {
        SetDlgPageString(PAGE_CONV_WEAPON); // this is a temporary awkward moment when conversation starts
        SetLocalInt(oPC, "CONV_WEAPON_WIELDED", TRUE);
    }
    else if(nNPCInterest<0)
    {
        SetDlgPageString(PAGE_CONV_REJECT);
    }
    else if( !GetSpeaksLanguage(OBJECT_SELF, nConvLang) )
    {
        SetDlgPageString(PAGE_CONV_LANG);
    }
    else if(GetLocalInt(OBJECT_SELF,"HOUSE_KEEPER"))
    {
        SetDlgPageString(PAGE_CONV_HOUSE);
        SetLocalInt(OBJECT_SELF, "CONV_LANGUAGE_SPEAKING", nConvLang);
    }
    else if(GetLocalInt(OBJECT_SELF,"MERCH"))
    {
        SetDlgPageString(PAGE_CONV_MERCH_GREET);
        SetLocalInt(OBJECT_SELF, "CONV_LANGUAGE_SPEAKING", nConvLang);
    }
    else if(bConvContinue||!nNPCInterest)
    {
        SetDlgPageString(PAGE_CONV_CORE);
    }
    else
    {
        SetDlgPageString(PAGE_CONV_GREET);
    }
}

  • Rolo Kipp aime ceci

#31
henesua

henesua
  • Members
  • 3 863 messages
I've been making use of all this conversation scripting lately as I populate our module. I am also playing with the Social Phenotype in Project Q. I made a bunch of modifications to the social AI that Tom Banjo put together so that I could set the stage for competition over seats at an Inn. I next need to create a conversation branch which an NPC can use to approach a PC and say "Hey! What are you doing in MY chair?" if the PC is actually in their claimed chair. I do have dynamic "chair ownership" as far as NPCs are concerned so far. Next I need to enable the NPCs to deal with others that have taken their seat.
 
Also got the innkeeper stuff working nicely so far. Here's a shot I took of a room rental with dynamic response letting the PC know where the room is, when check out is and so on:
v2_inn4.jpg