You are here

Conversation starts when hitpoints below number

8 posts / 0 new
Last post
andgalf
Conversation starts when hitpoints below number

Had a hard time describing what I need in the title but anyway...here is the situation:

I have the PC and his companions enter an area. There's a speak trigger and a conversation starts. Enemies are first commoners in this area and need to change faction at the end of the conversation. The enemies have two different tags but there are more than two of them. One leader with one tag and the others have another tag.

So at the end of the conversation I use ga_faction_join and change to $HOSTILE for both the tags. However, it seems you can't do it this way. The leader changes faction but only one of the others (maybe since they all have the same tag). So, I tried to create a group of the enemies(not the leader) (they are halflings that look like ogres) by modifiying a script I recieved from a friendly soul here on the forum:

#include "ginc_group"
 
void CreateEnemyGroup()
{
    ResetGroup("group_ogres");
    object oPC = GetFactionLeader(GetFirstPC());
    int i = 1;
    object oBadGuy = GetNearestObjectByTag("ogre_halfling_c", oPC, i);
    while(GetIsObjectValid(oBadGuy))
    {
        if(!GetIsDead(oBadGuy))
        {
            GroupAddMember("group_ogres", oBadGuy, TRUE);
        }
        i++;
        oBadGuy = GetNearestObjectByTag("ogre_halfling_c", oPC, i);
    }
}
 
void main(int nAction)
{
    object oPC = GetPCSpeaker();
    switch ( nAction )
    {
        //Various conversation options
        case 100:
        break;
 
        case 200: //Enemy attack PC
            CreateEnemyGroup();
            
            DelayCommand(0.0f, GroupGoHostile("group_ogres"));
            break;
    }
}

This seems to work kind of....I think. But then what I really need is when the leader gets his hitpoints below a certain point, to then start a conversation with the PC and the companions, and thus changing factions again to friendly or "commoner" if you will. I did a script that I put on the leader's OnDamaged slot that looks like this:

#include "ginc_ipspeaker"

void main()
{
    
int iHp = GetCurrentHitPoints(OBJECT_SELF);
if (iHp < 80)

    {
            object oPC = GetFirstPC(FALSE);

            CreateIPSpeaker("ogre_halfling", "ogre_halfling_conv", GetLocation(GetFirstPC()), 1.0f);
    }
    
}

When this conversation starts I immediately do a ga_faction_join again and set it to $COMMONER for the leader and for the others I run this script, which is a modified version of the group enemies script above:

#include "ginc_group"
 

 
void main(int nAction)
{
    object oPC = GetPCSpeaker();
    switch ( nAction )
    {
        //Various conversation options
        case 100:
        break;
 
        case 200: //Enemy befriends PC
        
            DelayCommand(0.0f, GroupChangeToStandardFaction("group_ogres", 1));
            break;
    }
}

In game this gets kind of weird. The enemies dissapear completely and then appear again and when, I think, the leader dies the conversation starts and the others have changed faction. Something like that. It's hard to explain because I don't really know what's going on in the game other than it is weird.

Maybe somebody can help with this somewhat complicated problem?

I know I could do a unique blueprint with a unique tag on each enemy but it feels kind of lame to have to do that. There's got to be a way to create a group that behaves as they should....

Thanks for your time!

  • up
    50%
  • down
    50%
andgalf

I tried without grouping the enemies but the result was the same. When the leader get's his hp down low and the conversation is about to start every enemy dissapears for a second or two and then reappears again continue fighting. It's only when the leader dies that the conversation finally kicks in and the others including the dead leader change faction to commoner. So I guess the question is now: How do I make the fighting stop? I have a vague memory seeing this done, I mean starting a dialogue when the hp gets low on an enemy, in NWN1, so it should be possible in NWN2 right?

  • up
    50%
  • down
    50%
fkirenicus

You could create a UD event that fires when hp < 3, for example. When that happens, set plot flag on him, and have him initiate conversation. Also, I suspect your problem is similar to one I encountered not long ago, where the answer was to create custom factions and corresponding faction pigs (! Yes, you're reading correctly). 
See this guide pp 219- and a few pages on.
https://www.nexusmods.com/neverwinter2/mods/1085/?tab=1&navtag=http%3A%2...

- fkirenicus -

  • up
    100%
  • down
    0%
andgalf

Thanks for the reply! I don't know how to create a UD (user defined? Is it the User Define Event Script slot you mean perhaps?) event or how to set a plot flag I'm afraid, but I guess I'll see if I can find it in the manual or in a toolset guide somewhere.

I'll read about the custom factions....but I've worked around that problem at the moment, I think. I just need to stop the fighting somehow between the enemies and the PC...

  • up
    50%
  • down
    50%
kevL's

TL;DR Clear actions on the group and the party, and change the group's faction to COMMONER *before* running CreateIPSpeaker().



stopping combat when there are a number of creatures fighting can be tricky (almost impossible if there are lots of creatures, since somebody always wants to get the last swipe in and ~boom~ the whole thing starts up again)

--
The user-defined event is like a catch-all for the other AI events. For example, you can use the OnDamaged script _or_ you can check the on-damaged event in the user-defined event. But the on-damaged event in the user-defined event has to be activated in the OnSpawn event before it works - you can see from that statement why the UD-handler is perhaps a bit overrated. There are times when it's like buttering the cat, however; if you don't want to alter a stock AI script (for whatever reason) just enable the ud-event in the OnSpawn script and deal with it in the UD handler. Or if there are several events you want altered, again enable each in the OnSpawn and all of them can be dealt with together in the OnUserDefined script, which is very convenient especially if they all trigger a single routine. There are also a few UD-events (extra) that the engine fires up regardless of whether they are enabled in the OnSpawn: things like "failed to path during combat" and "player-control changed", and these can be hooked only in a ud-script.

Oh, and what it was designed for: define your own event and signal it to fire. Literally a "user defined" event.

--
part of what i think you're missing, andgalf, is ClearAllActions() on the group "group_ogres" and the party both.

in 'ginc_group'

// Causes all members of sGroup to clear their actions.
// - string sGroup         : name of group
// - int bClearCombatState : true to clear combat actions
void GroupClearAllActions(string sGroup, int bClearCombatState = FALSE)
{
    object oGroup = GetFirstInGroup(sGroup);
    while (GetIsObjectValid(oGroup))
    {
        AssignCommand(oGroup, ClearAllActions(bClearCombatState));
        oGroup = GetNextInGroup(sGroup);
    }
}



and in 'ginc_cutscene'

// Clears all oPC's party's actions.
void ClearPartyActions(object oPC, int bClearCombatState = FALSE)
{
    object oParty = GetFirstFactionMember(oPC, FALSE);
    while (GetIsObjectValid(oParty))
    {
        AssignCommand(oParty, ClearAllActions(bClearCombatState));
        oParty = GetNextFactionMember(oPC, FALSE);
    }
}



Also, trying to change the hostile group's faction - to COMMONER - in the second dialog strikes me as too late. Try altering your script:


#include "ginc_ipspeaker"
void main()
{
    int iHp = GetCurrentHitPoints(OBJECT_SELF);
    if (iHp < 80)
    {
        object oPC = GetFirstPC(FALSE);
        CreateIPSpeaker("ogre_halfling", "ogre_halfling_conv", GetLocation(GetFirstPC()), 1.0f);
    }
}



to more like this:

// 'ogreboss_od' (or whatver)
/*
    OnDamaged script for the boss of the halfling ogres.
    Stops combat when the boss drops below 80 hitpoints. Starts a dialog.
*/

#include "ginc_group"
#include "ginc_ipspeaker"

void main()
{
    int iHp = GetCurrentHitPoints(OBJECT_SELF);
    if (iHp < 80)
    {
        GroupChangeToStandardFaction("group_ogres", STANDARD_FACTION_COMMONER);
        GroupClearAllActions("group_ogres", TRUE);

        object oPC = GetFirstPC(FALSE);
        ClearPartyActions(oPC, TRUE);

        CreateIPSpeaker("ogre_halfling", "ogre_halfling_conv", GetLocation(oPC), 1.f);
    }
}



i mean ... this isn't easy stuff (which is good to see :)

ps. I should point out that this OnDamaged script bypasses all the checks and actions in the standard OnDamaged script. The two scripts should either be merged or, as fkirenicus points out, the on-damaged block in the OnUserDefined handler should be enabled (yes in its OnSpawn event). But it's okay to try the shortened OD script as a test (the code can be transferred to the UD script later if desired),



pps. It's not strictly necessary to enable UD event(s) in an OnSpawn script .... It's just another function-call (in 'x0_i0_spawncond')

    SetSpawnInCondition(NW_FLAG_DAMAGED_EVENT);

but there is not any special reason it has to be done in the OnSpawn handler. For example, in the RustMonster scripts i passed to kamal recently, to enable the UD-heartbeat, I simply wrote a 1-line script that set the monster's spawn-in condition (hb), and gave the blueprint/instance of the creature a string variable "SpawnScript=<myscript>" and since the default OnSpawn script checks that variable and runs a script of that filename (if found) ~boom~ the UD-hb block started firing IG.

You could even enable such event-signalling simply by running a little script as a dialog-action in the first conversation, etc

  • up
    50%
  • down
    50%
fkirenicus

Sorry - tribal language. :-) 
UD is user defined event, yes, and kevL's explained it better than I. 

SetPlotFlag should be a function you can find in the script assistant straight away (i.e. you shouldn't need to go through any include files to find it). 

  • up
    50%
  • down
    50%
andgalf

Thanks both of you! Wow, that was a lot to take in kevL's. I think I'll have to read your post a couple of times to really grasp it. smiley

I knew this thing would be complicated. Still, it's nice to see that it could probably be done.

  • up
    100%
  • down
    0%
andgalf

Eventually I got it to work. I added to the already default OnDamaged script. I also found out that I hadn't included the boss in the group, but whatever. I just did a separate clear-all-actions and change faction for him. I also did a Stand Ground for the companions just to be sure they're not attacking even if the clear-all-actions is already in use. Maybe it's not necessary, but anyway. I will reset the stand ground in the dialog after. This is how the script looks like now, and I think everything works. I will double check later:

//::///////////////////////////////////////////////
//:: Default On Damaged
//:: NW_C2_DEFAULT6
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    If already fighting then ignore, else determine
    combat round
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Oct 16, 2001
//:://////////////////////////////////////////////

#include "hench_i0_ai"
#include "ginc_behavior"
#include "ginc_group"
#include "ginc_ipspeaker"
#include "x0_i0_assoc" // SetAssociateState()

void main()
{
    
        
    // 'ogreboss_od' (or whatver)
/*
    OnDamaged script for the boss of the halfling ogres.
    Stops combat when the boss drops below 80 hitpoints. Starts a dialog.
*/

    int iHp = GetCurrentHitPoints(OBJECT_SELF);
    if (iHp < 150)
    {
        GroupChangeToStandardFaction("group_ogres", STANDARD_FACTION_COMMONER);
        ChangeToStandardFaction(OBJECT_SELF, STANDARD_FACTION_COMMONER);
        AssignCommand(OBJECT_SELF, ClearAllActions(TRUE));
        GroupClearAllActions("group_ogres", TRUE);

        object oPC = GetFirstPC(FALSE);
        ClearPartyActions(oPC, TRUE);
        
        SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("Elinthriel"));
        SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("NiyaMoonglow"));
        SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("LilyMoonglow"));
        SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("Zecane"));
        
        CreateIPSpeaker("ogre_halfling", "ogre_halfling_conv", GetLocation(oPC), 1.f);
        return;
    }
    

    int iFocused = GetIsFocused();
    
    // I've been damaged so no longer partially focused
    if (iFocused == FOCUSED_PARTIAL)
    {
        SetLocalInt(OBJECT_SELF, VAR_FOCUSED, FOCUSED_STANDARD); // no longer focused
    }
    if (iFocused == FOCUSED_FULL)
    {
        // remain focused
    }
    else if(GetFleeToExit())
    {
        // We're supposed to run away, do nothing
    }
    else if (GetSpawnInCondition(NW_FLAG_SET_WARNINGS))
    {
        // don't do anything?
    }
    else
    {
        object oDamager = GetLastDamager();
        if (!GetIsObjectValid(oDamager))
        {
        // don't do anything, we don't have a valid damager
        }
        else if (!GetIsFighting(OBJECT_SELF))
        {
            if ((GetLocalInt(OBJECT_SELF, HENCH_HEAL_SELF_STATE) == HENCH_HEAL_SELF_WAIT) &&
                (GetPercentageHPLoss(OBJECT_SELF) < 30))
            {
                // force heal
                HenchDetermineCombatRound(OBJECT_INVALID, TRUE);
            }
            else if (!GetIsObjectValid(GetAttemptedAttackTarget()) && !GetIsObjectValid(GetAttemptedSpellTarget()))
            {
//    Jug_Debug(GetName(OBJECT_SELF) + " responding to damage");
                if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL))
                {
                    HenchDetermineSpecialBehavior(oDamager);
                }
                else
                {
                    HenchDetermineCombatRound(oDamager);
                }
            }
        }
    }
    if(GetSpawnInCondition(NW_FLAG_DAMAGED_EVENT))
    {
        SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DAMAGED));
    }


}

  • up
    50%
  • down
    50%