I have a situation that I think is due to timing problems within NWN2. The whole scene goes like this:
- The PC and the party approaches a bunch of barbarians that have kidnapped a princess. The PC and the barbarians talk and there’s a fight.
- During all this the princess is set to immortal.
- Once she is down to lower than 5 HP, she gets knocked down. The trouble is, she can’t get knocked down if she’s immortal, so I set her to mortal just before being knocked down.
- Once all the barbarians are dead, the knockdown effect is lifted and she stands up, and a conversation starts.
For most of the time all this works splendidly. However, I have had a few instances when testing where the princess is dead instead of just very injured when knocked down. This has me believing that somehow in a short millisecond where she is made mortal, one of the barbarians might get a killing blow against her.
This is my script that’s on the OnDamage of the princess:
#include "s_deathinclude"
void main()
{
int iHp = GetCurrentHitPoints(OBJECT_SELF);
if (GetGlobalInt("princessfirstfight") && iHp < 5)
{
AssignCommand(OBJECT_SELF, ClearAllActions(TRUE));
SetGlobalInt("princessfirstfight",0);
ApplyKnockdown("semaphine","factionpig");
return;
}
else
ExecuteScript("gb_assoc_damage", OBJECT_SELF);
}
This is the OnDeath script for the barbarians:
#include "ginc_ipspeaker"
#include "s_deathinclude"
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 main()
{
object oPC = GetFirstPC();
object oSemaphine = GetObjectByTag("semaphine");
if (CheckIfCreaturesWithTagAreAllDead("native"))
{
RemoveKnockdown("semaphine");
SetCutsceneMode(oPC);
CreateIPSpeaker("semaphine", "c_i3_semaphine", GetLocation(GetFirstPC()), 0.5f);
}
}
Here’s the include script called s_deathinclude:
void Destroyable(object oObject)
{
AssignCommand(oObject, SetIsDestroyable( FALSE,FALSE,TRUE ));
SetImmortal( oObject, FALSE );
SetPlotFlag( oObject, FALSE );
effect eFX = EffectKnockdown();
eFX = SetEffectSpellId(eFX, 9992); //9991 <-- just for example here, you can use
//any valid id that doesn't conflict with
//existing ones in spells.2da or in other scripts
//PrettyDebug("Name of oObject = " + GetName(oObject));
DelayCommand(0.1f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFX, oObject));
}
void ApplyDeath(string sTag, string sFactionPig)
{
//PrettyDebug("Applying Death Effect To: " + sTagString + " Instance = " + IntToString(iInstance));
object oObject = GetObjectByTag(sTag);
object oPig = GetObjectByTag(sFactionPig);
ChangeFaction(oObject,oPig);
AssignCommand(oObject, SetIsDestroyable( FALSE,FALSE,TRUE ));
SetImmortal( oObject, FALSE );
SetPlotFlag( oObject, FALSE );
effect eFX = EffectDeath();
eFX = SetEffectSpellId(eFX, 9991); //9991 <-- just for example here, you can use
//any valid id that doesn't conflict with
//existing ones in spells.2da or in other scripts
//PrettyDebug("Name of oObject = " + GetName(oObject));
ApplyEffectToObject( DURATION_TYPE_INSTANT,eFX,oObject );
}
void ApplyKnockdown(string sTag, string sFactionPig)
{
//PrettyDebug("Applying Death Effect To: " + sTagString + " Instance = " + IntToString(iInstance));
object oObject = GetObjectByTag(sTag);
object oPig = GetObjectByTag(sFactionPig);
ChangeFaction(oObject,oPig);
DelayCommand(0.2,Destroyable(oObject));
}
void RemoveAllTheEffects(object oPC)
{
effect eLoop=GetFirstEffect(oPC);
while (GetIsEffectValid(eLoop))
{
RemoveEffect(oPC, eLoop);
eLoop=GetNextEffect(oPC);
}
}
void RemoveDeath(string sTag)
{
object oDeadObject = GetObjectByTag(sTag);
ChangeToStandardFaction(oDeadObject,STANDARD_FACTION_COMMONER);
if (GetIsObjectValid(oDeadObject))
{
effect eSearch = GetFirstEffect(oDeadObject);
while (GetIsEffectValid(eSearch))
{
if (GetEffectSpellId(eSearch) == 9991) //your custom spell id
{
RemoveEffect(oDeadObject, eSearch);
eSearch = GetFirstEffect(oDeadObject); //safety (removing an iterator from the list)
}
else
eSearch = GetNextEffect(oDeadObject);
}
}
}
void RemoveKnockdown(string sTag)
{
object oDeadObject = GetObjectByTag(sTag);
ChangeToStandardFaction(oDeadObject,STANDARD_FACTION_COMMONER);
if (GetIsObjectValid(oDeadObject))
{
effect eSearch = GetFirstEffect(oDeadObject);
while (GetIsEffectValid(eSearch))
{
if (GetEffectSpellId(eSearch) == 9991) //your custom spell id
{
RemoveEffect(oDeadObject, eSearch);
eSearch = GetFirstEffect(oDeadObject); //safety (removing an iterator from the list)
}
else
eSearch = GetNextEffect(oDeadObject);
}
}
}
My first thought when all this occured was to maybe add something in the OnDeath script of the barbarians to check if the princess is dead and in that case resurrect her (even though she should not be killable). However, I don’t know how that resurrect spell thing works and how to script that. I found only a EffectResurrection function but I’m not sure if that’s enough to use that in this situation.
Can anyone help with this? It must be a timing problem that the princess somehow dies on rare occations…