Best-Practice Way to Check for Two NPC Deaths?

I know how to update journals upon the death of an NPC via scripting, but I need a script that will do this only after both NPCs have died.

I am keeping performance in mind too, a heartbeat-based if statement would probably be a bad idea, so I am open to other’s solutions. Thanks!

I think I can help you with this. I’m using similar scripts in the module I’m working on now. Let me take a look in the toolset.

1 Like

wherever the journal gets the update,

condition it with

object oNpc1 = ; // define these
object oNpc2 = ;

if (   (!GetIsObjectValid(oNpc1) || GetIsDead(oNpc1))
    && (!GetIsObjectValid(oNpc2) || GetIsDead(oNpc2)))
{
    // do journal update
}

(this assumes both NPCs are instantiated at the same time – that is, there isn’t one roaming around while the other isn’t, or can be killed before the other spawns. If you have something like that, then probably need to set a local_int on the death of each and then check those locals instead)

3 Likes

Here’s one of my scripts:

int CheckIfCreaturesWithTagAreAllDead(string EnemyTag)
{
    object oPC = GetFirstPC();
    int n = 1;
    object oNPC = GetNearestObjectByTag(EnemyTag + IntToString(n),oPC);
	
	while(GetIsObjectValid(oNPC))
    {
        if(!GetIsDead(oNPC)) 
		{
			return FALSE;
        }
        n++;
        oNPC = GetNearestObjectByTag(EnemyTag + IntToString(n),oPC);
    }
	return TRUE;
}


void DoTheStuff()
{

		object oPC = GetFirstPC();
	
		AddJournalQuestEntry("questtag",1,oPC);

}


void main()
{

	if (CheckIfCreaturesWithTagAreAllDead("enemytag")) 
	{
   		AssignCommand(GetModule(), DoTheStuff());
	}
    ExecuteScript("nw_c2_default7", OBJECT_SELF);
				
}

Place this on the OnDeath of both the creatures, and it assumes they have the same tag only with a different number at the end: like enemy1 and enemy2. There are other ways of doing this of course…

EDIT: Probably easier to go with what @kevL_s suggested but…well…
EDIT2: @Shadooow 's approach is also equally good, of course.

3 Likes

two approaches

  1. In the ondeath script of either npc, check if the other npc - find it by tag either doesn’t exists or is dead - if that condition is true then you know that they both died (this will work even if they both have same tag - but if you choose to not use unique tag you need to find the other npc using GerNearestObjectByTag instead of just GetObjectByTag )
  2. this one is better suited with more than 2 npcs - when each npc you are tracking dies, you will increase local variable on an area or module - if that variable reaches certain value, in this case 2, you know that both npcs were killed and you can do stuff
3 Likes

Hey guys, thank you for all of your suggestions. I will try them out and let you know which approach I will go with!

1 Like

Another option is to use ginc_group and add both NPCs to a group … then use one of the group_functions to fire the journal update.

1 Like

It’s throwing an error on line 5 here. Any ideas?

object oNpc1 = GetObjectByTag("c_voaraghamanthar", 0);
object oNpc2 = GetObjectByTag("c_waervaerendor", 0);
object oPC = GetFirstPC(1);

if (   (!GetIsObjectValid(oNpc1) || GetIsDead(oNpc1))
    && (!GetIsObjectValid(oNpc2) || GetIsDead(oNpc2)))
{
    	AddJournalQuestEntry("nw_mere_dragcult", 5, oPC, 1, 0, 0);
}

this compiled for me

void main()
{
	object oNpc1 = GetObjectByTag("c_voaraghamanthar", 0);
	object oNpc2 = GetObjectByTag("c_waervaerendor", 0);
	object oPC = GetFirstPC(1);

	if (   (!GetIsObjectValid(oNpc1) || GetIsDead(oNpc1))
		&& (!GetIsObjectValid(oNpc2) || GetIsDead(oNpc2)))
	{
		AddJournalQuestEntry("nw_mere_dragcult", 5, oPC, 1, 0, 0);
	}
}
3 Likes

Ah yes, I forgot the void main() part. I was thinking in JavaScript. Thanks, @kevL_s!

1 Like

np Fs

if it’s their OnDeath scripts you might want to add the line

    ExecuteScript("nw_c2_default7", OBJECT_SELF);

as in Ag’s script … (cant remember if the stock OnDeath script actually does much in Nwn2 though)

2 Likes

Okay, I will add that to it just in case. Thanks!

1 Like

note to self: I hope the creature is considered ‘dead’ in its OnDeath script /hehe

 
that is, the routine might need a very short Delay (unlikely but possibly)

1 Like

here’s Shadooow #1 method (which looks a bit more complicated but is probly more robust and efficient)

// OnDeath script for "c_voaraghamanthar" and "c_waervaerendor".
void main()
{
	ExecuteScript("nw_c2_default7", OBJECT_SELF);

	object oOther;

	string sTag = GetTag(OBJECT_SELF);
	if (sTag == "c_voaraghamanthar")
	{
		oOther = GetObjectByTag("c_waervaerendor");
	}
	else if (sTag == "c_waervaerendor")
	{
		oOther = GetObjectByTag("c_voaraghamanthar");
	}
	else return; // safety.

	if (!GetIsObjectValid(oOther) || GetIsDead(oOther))
	{
		AddJournalQuestEntry("nw_mere_dragcult", 5, GetFirstPC(TRUE));
	}
}

 
[edit] revised (same thing but different) →

// OnDeath script for "c_voaraghamanthar" and "c_waervaerendor".
void main()
{
	string sTag2 = "";

	string sTag = GetTag(OBJECT_SELF);
	if      (sTag == "c_voaraghamanthar") sTag2 = "c_waervaerendor";
	else if (sTag == "c_waervaerendor")   sTag2 = "c_voaraghamanthar";

	if (sTag2 != "") // safety.
	{
		object oOther = GetObjectByTag(sTag2);
		if (!GetIsObjectValid(oOther) || GetIsDead(oOther))
		{
			AddJournalQuestEntry("nw_mere_dragcult", 5, GetFirstPC(TRUE));
		}
	}

    ExecuteScript("nw_c2_default7", OBJECT_SELF);
}
3 Likes

By default any creature has :

string sDeathScript = GetLocalString(OBJECT_SELF, "DeathScript");
	if (sDeathScript != "")
		ExecuteScript(sDeathScript, OBJECT_SELF);

So when you create the creature you can do :

object C1 = GetObjectByTag("Creature1");
object C2 = GetObjectByTag("Creature2");
SetLocalString(C1,"DeathScript","mydeathcounter");
SetLocalString(C2,"DeathScript","mydeathcounter");

And now you need “mydeathcounter” script

void main(){

int iDeathCount = GetLocalInt(GetArea(OBJECT_SELF),"DeathCount")+1;
SetLocalInt(GetArea(OBJECT_SELF),"DeathCount",iDeathCount);

if(iDeathCount == 2){


			AddJournalQuestEntry("nw_mere_dragcult", 5, GetFirstPC(TRUE));
}
else{

SetLocalInt(GetArea(OBJECT_SELF),"DeathCount",iDeathCount);
}


}

That’s the hard way. It 's similar to what has been proposed so far, but it’s more flexible, it’s easy to have more than 2 creature involved, and most importantly it doesn’t mess or change the death script.

Now commes the easy way, you know what ? Obsidian&& Bioware already scripted all this stuff beceause you need that kind of functions all the time. It’s all in the librairy “ginc_group”

on on enter area script or before your encounter.

#include "ginc_group"
void main(){

string sGroup = "TheyWillDie";
object oCreature1 = GetObjectByTag("Creature1");
object oCreature2 = GetObjectByTag("Creature2");
AddToGroup(sGroup,oCreature1);	
AddToGroup(sGroup,oCreature2);	

GroupOnDeathSetJournalEntry(sGroup,"nw_mere_dragcult",5);

}

There is no limit on the number of creature involved, but the action set on the group needs to be made after you have formed the group.

Check that library there are a lot more action that can be triggered on a group death such starting a conversation or launching a custom script.

3 Likes

Thank you for all of the help everyone, I was able to make the quest work because of your input!

2 Likes

I once did something similar in a module. I wanted a gate to open only if all three NPCs died.

  • A trigger was used rather than an encounter to spawn these “key” (pun!) enemies.
  • The trigger script would first check to ensure that all three had already been killed, then spawn them and then erase the three NPC death variables on the gate (setting them to zero).
  • In each creature’s on-death script, the death variable for that creature on the gate would be set to non-zero.
  • After setting the death variable, all three variables would then be checked and if all three were non-zero, the gate would unlock and open.
    I’d suggest you use a similar approach since it avoids the need for a heartbeat script.
1 Like