Why won't these scripts work?

I’ve tested many times now, but for some inexplicable reason this doesn’t work. It only partly works. I don’t know what I’m doing wrong.

Ok, this will contain spoilers for a side quest in my upcoming module A Strange World III, so if you don’t want to be spoiled, you have been warned.

I have a scene where I have a wizard. This wizard is transformed (polymorphed) into a water elemental. At first when entering this area I have a script that polymorphs the wizard and then makes him script hidden. This script I found at the NWN Script Archive and modified it a bit. This works perfectly:

void main()
{               

object oNPC = GetObjectByTag("element_npc");

//object oPC = GetEnteringObject();

//if(!GetIsPC(oPC)) return;

effect eVis = EffectVisualEffect (VFX_FNF_SUMMON_UNDEAD);

eVis = SetEffectSpellId(eVis, 9996);

DelayCommand(1.0, ApplyEffectAtLocation (DURATION_TYPE_INSTANT, eVis, GetLocation(oNPC)));
effect eShape = EffectPolymorph (POLYMORPH_TYPE_HUGE_WATER_ELEMENTAL);
DelayCommand(1.0, ApplyEffectToObject (DURATION_TYPE_PERMANENT, eShape, oNPC));

DelayCommand(2.0, SetScriptHidden(oNPC,TRUE));
    
}

Then at a certain point he is made visible again (not script hidden) and attacks the party. This works too. Then we come to the stuff that doesn’t work properly. I have a script on his OnDamage. When he gets below 15 HP, he’s immortal by the way, the polymorphed effect is supposed to disappear and then I want him to get knocked down, and lastly a conversation is to be triggered. Here’s my OnDamage script:

//:: Created By: Preston Watamaniuk
//:: Created On: Oct 16, 2001
//:://////////////////////////////////////////////

#include "hench_i0_ai"
#include "ginc_behavior"
#include "ginc_ipspeaker"

void RemoveElementalEffect()
{
   

    
       	object oElementalMan = OBJECT_SELF;
		string sTag = GetTag(oElementalMan);

        if (GetIsObjectValid(oElementalMan))
        {
            effect eSearch = GetFirstEffect(oElementalMan);
            while (GetIsEffectValid(eSearch))
            {
                if (GetEffectSpellId(eSearch) == 9996) //your custom spell id
                {
                    RemoveEffect(oElementalMan, eSearch);
                    eSearch = GetFirstEffect(oElementalMan); //safety (removing an iterator from the list)
                }
                else
                    eSearch = GetNextEffect(oElementalMan);
            }
        }
       
    
}

void main()
{
	object oPC = GetFirstPC();

	object oElementalMan = OBJECT_SELF;
	string sTag = GetTag(oElementalMan);
	//object oNPC = GetObjectByTag("assates");

    int iHp = GetCurrentHitPoints(OBJECT_SELF);
	
    if (iHp < 15 )
    {
	
	SetGlobalInt("elementaldead",1);
	
	RemoveElementalEffect();
	
	//AssignCommand(oElementalMan, ClearAllActions(TRUE));
   		
	effect eFX = EffectKnockdown();

	DelayCommand(1.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFX, oElementalMan));
		
	ChangeToStandardFaction(OBJECT_SELF, STANDARD_FACTION_COMMONER);
	//AssignCommand(OBJECT_SELF, ClearAllActions(TRUE));
	
	CreateIPSpeaker("kentharuil", "c_ne_pit_conv", GetLocation(oPC), 0.5f);
	
	//AssignCommand(oPC, ClearAllActions(TRUE));
	//SetCommandable(TRUE, oPC);
	
	}
	    

    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));
    }
}

What happens ingame is this: When below 15 HP he changes faction and falls down BUT he doesn’t polymorph back to his normal self and the conversation doesn’t trigger (I see now what may cause the conversation to not trigger, didn’t notice that before, I think put the wrong conversation there). Can someone see what I’m doing wrong here?

Another thing that I’m wondering about is this: When in water elemental form he doesn’t cast any spells while in combat. Even though he’s a wizard, maybe he can’t cast spells while in polymorphed form?

KISS

TBH I wouldn’t bother with the polymorph. Just make a new blueprint of a a huge water elemental and give it the ability to cast spells. Add a OnDeath script to that elemental that makes a bright flash or something and spawns in the man and subsequently apply a knockdown to him. Then delay a convo on the same script.

2 Likes

Ok. :confused: I guess I’ll have to do it that way then. It would have been so much cooler to have it like this though.

a possible fudge is, instead of applying effect Polymorph … just alter the creature’s appearance

// Sets the creature's appearance type to the value specified (uses the APPEARANCE_TYPE_XXX constants)
void SetCreatureAppearanceType(object oCreature, int nAppearanceType);

( this would bypass the vagaries of effects and Poly )

But i honestly like travus’ idea better – since it allows stuff like Ranger’s Favored Enemy to work etc.

I’ll try with travus idea. His ideas are almost always the best when it comes to scripting, I sincerely believe. :slightly_smiling_face:
As long as I can find a cool flash effect to use when the elemental dies…And if I find a good effect it will be a very little difference to my original way of doing it, I guess.

1 Like

I did the death script like this and it seems to work quite well. Everything fired as expected. I’m not sure about the flash effect though. I didn’t see if it actually fired.

//:://////////////////////////////////////////////////
//:: NW_C2_DEFAULT7
/*
  Default OnDeath event handler for NPCs.

  Adjusts killer's alignment if appropriate and
  alerts allies to our death. 
 */
//:://////////////////////////////////////////////////
//:: Copyright (c) 2002 Floodgate Entertainment
//:: Created By: Naomi Novik
//:: Created On: 12/22/2002
//:://////////////////////////////////////////////////
// chazm 5/6/05 added DeathScript
// ChazM 7/28/05 removed call to user defined event for onDeath
// ChazM - 1/26/07 - EvenFlw modifications
// ChazM -5/17/07 - Spirits don't drop crafting items, removed re-equip weapon code
// JSH-OEI 5/28/08 - NX2 campaign version.

#include "x2_inc_compon"
#include "x0_i0_spawncond"
#include "ginc_object"
#include "ginc_ipspeaker"

void main()
{

	SetGlobalInt("elementaldead",1);
	
	object oPC = GetFirstPC();
	object oElemental = OBJECT_SELF;
	string sTag = GetTag(oElemental);
	
	location lTarget = GetLocation(oElemental);
		
    ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_HIT_DIVINE), GetLocation(oElemental));

	object oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "element_npc", lTarget);
	
	effect eFX = EffectKnockdown();

	DelayCommand(1.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFX, oSpawn));
	
	CreateIPSpeaker("kentharuil", "c_ne_kentharuil", GetLocation(oPC), 1.0f);

	string sDeathScript = GetLocalString(OBJECT_SELF, "DeathScript");
	if (sDeathScript != "")
		ExecuteScript(sDeathScript, OBJECT_SELF);
	
    int nClass = GetLevelByClass(CLASS_TYPE_COMMONER);
    int nAlign = GetAlignmentGoodEvil(OBJECT_SELF);
    object oKiller = GetLastKiller();

    // If we're a good/neutral commoner,
    // adjust the killer's alignment evil
    //if(nClass > 0 && (nAlign == ALIGNMENT_GOOD || nAlign == ALIGNMENT_NEUTRAL))
    //{
    //    AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
    //}

    // Call to allies to let them know we're dead
    SpeakString("NW_I_AM_DEAD", TALKVOLUME_SILENT_TALK);

    //Shout Attack my target, only works with the On Spawn In setup
    SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);
	
	/*if (!GetIsSpirit(OBJECT_SELF))
    	craft_drop_items(oKiller);*/
	
}

However, I had two crash to desktop before being able to see this. My guess is that it has to do with the Go Home And Sleep scripts in this area. Maybe when in an intense battle with everything going on, and on top of that all of a sudden a bunch of heartbeat scripts kick in, the game crashes. What do you think? Could that be it? I’m actually thinking of abandoning the Go Home And Sleep thing with kamal’s scripts. It does only a little to get a point across to the players that they feel like: “Ok, I can see everyone goes to sleep, that’s why they haven’t seen this creature (the elemental)”.

I just tested your script and it works nice - no crashes. Although, the water elemental’s body kind of washed out the divine effect when it dies. Possibly use the following line for a more dramatic effect:

    ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_HIT_AOE_HOLY), lTarget);
1 Like

Thanks travus! I’ll try that. Yes, I didn’t quite notice any effect the first time around. Maybe this will be better.
And I’ll try with abandoning my Go Home And Sleep thing, and see if that reduces the crashes to desktop. Will try all this tomorrow.

I tried your visual effect, @travus and took away the Go Home And Sleep system and now the game didn’t crash (tested it two times). Since there are about 25 NPCs in the area that will go and sleep, not all at the exact same time, but they still have that heartbeat script running, my guess it became too much for the game to handle while the battle started with the elemental.

One thing I guess there’s nothing one can do about, is the fact that when the guy spawns in in this script and is knocked down, it looks a slightly bit weird that he isn’t lying on the ground already. There’s no way of spawning in an NPC lying down directly is there?

If you use a prone animation instead of a knockdown, I think you’ll get the results you are looking for. With this script, you won’t see the NPC fall-down animation:

// Put this on the elemental's OnDeath event.

#include "ginc_ipspeaker"

void Animate(object oSpawn)
{
	PlayCustomAnimation(oSpawn, "proneB", 1);
}

void main()
{
	location lTarget = GetLocation(OBJECT_SELF);
	object oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "element_npc", lTarget);

	SetGlobalInt("elementaldead", 1);
	ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_HIT_AOE_HOLY), lTarget);	
	DelayCommand(0.1f, Animate(oSpawn));
	CreateIPSpeaker("kentharuil", "c_ne_kentharuil", GetLocation(GetFirstPC()), 1.0f);
}

Ah, good call. I’ve used proneB before in this module, but I’ve fired that when entering a building on an already spawned character. I’ll try it out.

Yes, that looked better. However, now I got a crash to desktop again. I didn’t quite copy your script exactly here. Maybe it has to do with the order of things if the game crashes. I will copy your code here exactly, but I will add the

string sDeathScript = GetLocalString(OBJECT_SELF, "DeathScript");
	if (sDeathScript != "")
		ExecuteScript(sDeathScript, OBJECT_SELF);
	
    int nClass = GetLevelByClass(CLASS_TYPE_COMMONER);
    int nAlign = GetAlignmentGoodEvil(OBJECT_SELF);
    object oKiller = GetLastKiller();

    // If we're a good/neutral commoner,
    // adjust the killer's alignment evil
    //if(nClass > 0 && (nAlign == ALIGNMENT_GOOD || nAlign == ALIGNMENT_NEUTRAL))
    //{
    //    AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
    //}

    // Call to allies to let them know we're dead
    SpeakString("NW_I_AM_DEAD", TALKVOLUME_SILENT_TALK);

    //Shout Attack my target, only works with the On Spawn In setup
    SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);
	
	/*if (!GetIsSpirit(OBJECT_SELF))
    	craft_drop_items(oKiller);*/
	

at the end, so that the elemental dies and disappears. Shouldn’t I do that?
Like this:

//:://////////////////////////////////////////////////
//:: NW_C2_DEFAULT7
/*
  Default OnDeath event handler for NPCs.

  Adjusts killer's alignment if appropriate and
  alerts allies to our death. 
 */
//:://////////////////////////////////////////////////
//:: Copyright (c) 2002 Floodgate Entertainment
//:: Created By: Naomi Novik
//:: Created On: 12/22/2002
//:://////////////////////////////////////////////////
// chazm 5/6/05 added DeathScript
// ChazM 7/28/05 removed call to user defined event for onDeath
// ChazM - 1/26/07 - EvenFlw modifications
// ChazM -5/17/07 - Spirits don't drop crafting items, removed re-equip weapon code
// JSH-OEI 5/28/08 - NX2 campaign version.

//Modified version of NW_C2_DEFAULT7 for Huge Water Elemental creature death. /by andgalf

//#include "x2_inc_compon"
//#include "x0_i0_spawncond"
//#include "ginc_object"
#include "ginc_ipspeaker"

void Animate(object oSpawn)
{
	PlayCustomAnimation(oSpawn, "proneB", 1);
}

void main()
{
	location lTarget = GetLocation(OBJECT_SELF);
	object oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "element_npc", lTarget);

	SetGlobalInt("elementaldead", 1);
	ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_HIT_AOE_HOLY), lTarget);	
	DelayCommand(0.1f, Animate(oSpawn));
	CreateIPSpeaker("kentharuil", "c_ne_kentharuil", GetLocation(GetFirstPC()), 1.0f);


	string sDeathScript = GetLocalString(OBJECT_SELF, "DeathScript");
	if (sDeathScript != "")
		ExecuteScript(sDeathScript, OBJECT_SELF);
	
    //int nClass = GetLevelByClass(CLASS_TYPE_COMMONER);
    //int nAlign = GetAlignmentGoodEvil(OBJECT_SELF);
    //object oKiller = GetLastKiller();

    // If we're a good/neutral commoner,
    // adjust the killer's alignment evil
    //if(nClass > 0 && (nAlign == ALIGNMENT_GOOD || nAlign == ALIGNMENT_NEUTRAL))
    //{
    //    AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
    //}

    // Call to allies to let them know we're dead
    SpeakString("NW_I_AM_DEAD", TALKVOLUME_SILENT_TALK);

    //Shout Attack my target, only works with the On Spawn In setup
    SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);
	
	/*if (!GetIsSpirit(OBJECT_SELF))
    	craft_drop_items(oKiller);*/
	
}

Maybe those things at the end of the script doesn’t matter for the death of the creature since it’s only if you have a local string on the character that that string executes (or something)? I must say I don’t quite grasp this bit of code.

Sorry if this is a stupid question. Just trying to learn.

Basically, that’s it. If there’s a variable “DeathScript” attached to the NPC, the script with the same name as the value of the variable will run.
For example, if DeathScript equals “runthiswhenidie”, the game will fire runthiswhenidie.nsc

I’ve alway been told there are no stupid questions (actually, quite the contrary: if you don’t ask questions, you won’t learn anything).

1 Like

When I was in the Military 20 years ago I used to tell my soldiers. The only stupid question is the one that is never asked.

1 Like

Thanks for the replies! Ok, then I guess I can safely take all that away and really use travus’ death script exactly as he posted it, since this character doesn’t have that variable and won’t call to allies or to “attack my target”.