Scripting Quandary

For some reason, the reserve feats created by Kaedrin are not working entirely correctly for me. I have adjusted them to work with different spells, but otherwise, they are the same as what Kaedrin created. I’m not a scripting slouch, thanx to kevL_s, but I just don’t understand why I am getting the results I am getting.
The first script is the Area of Effect creating script

#include "X0_I0_SPELLS"
#include "x2_inc_spellhook" 
#include "cmi_ginc_chars"

int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER, OBJECT_SELF);
int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF);

int GetRuneFlameReserveDamageDice()
{

	if ( (GetHasSpell(116)) && ((nSorcerer > 0 || nWizard > 0)) )
		return 9;
	if ( GetHasSpell(89) || GetHasSpell(2025) )
		return 8;
	if ( GetHasSpell(39) || GetHasSpell(57) || GetHasSpell(135) || GetHasSpell(2078) )
		// 2078 = Warlock Invocation Instill Vulnerability
		return 7;
	if ( GetHasSpell(440) || GetHasSpell(446) || GetHasSpell(837) || GetHasSpell(869)
		|| GetHasSpell(871) || GetHasSpell(2347) || GetHasSpell(2457) )
		// 837 = Warlock Invocation Wall of Perilous Flame
		return 5;

	return 0;
}

void main()
{

/* 
  Spellcast Hook Code 
  Added 2003-06-20 by Georg
  If you want to make changes to all spells,
  check x2_inc_spellhook.nss to find out more
  
*/

    if (!X2PreSpellCastCode())
    {
	// If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
        return;
    }

// End of Spell Cast Hook

	if (GetRuneFlameReserveDamageDice() == 0)
	{
		SendMessageToPC(OBJECT_SELF,"You do not have any valid spells left that can trigger this ability.");	
		return;
	}
	
	//First we need to generate the string that serves as the object ID for this AOE object
	string sSelf = ObjectToString(OBJECT_SELF) + IntToString(SPELLABILITY_RUNE_FLAME);
	//Now we need to see if anything with this tag already exists
	object oSelf = GetNearestObjectByTag(sSelf);
	//If it exists, kill it.
	if (GetIsObjectValid(oSelf))
	{
		DestroyObject(oSelf);
	}

	int nRuneDice = GetRuneFlameReserveDamageDice();
	SendMessageToPC(GetFirstPC(FALSE), "nRuneDice = " + IntToString(nRuneDice));
    //Declare major variables including Area of Effect Object
    effect eAOE = EffectAreaOfEffect(AOE_RUNE_FLAME, "", "", "", sSelf );
    location lTarget = GetSpellTargetLocation();
    int nDuration = 6;
	
    //Create an instance of the AOE Object using the Apply Effect function
    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAOE, lTarget, RoundsToSeconds(nDuration));
								
}

This script works correctly. The problem is the OnEnter Script. The same int GetRuneFlameReserveDamageDice(), which I renamed to int GetRFReserveDamageDice() in the OnEnter Script is returning incorrect information, thereby doing incorrect damage. I have no idea why there are 2 results for the same calculation. Can anyone propose a resolution? Here is the OnEnter script.

#include "X0_I0_SPELLS"
#include "x2_inc_spellhook"
#include "cmi_includes"
#include "cmi_ginc_chars"

int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER, OBJECT_SELF);
int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF);

int GetRFReserveDamageDice()
{

	if ( (GetHasSpell(116)) && ((nSorcerer > 0 || nWizard > 0)) )
		return 9;
	if ( GetHasSpell(89) || GetHasSpell(2025) )
		return 8;
	if ( GetHasSpell(39) || GetHasSpell(57) || GetHasSpell(135) || GetHasSpell(2078) )
		// 2078 = Warlock Invocation Instill Vulnerability
		return 7;
	if ( GetHasSpell(440) || GetHasSpell(446) || GetHasSpell(837) || GetHasSpell(869)
		|| GetHasSpell(871) || GetHasSpell(2347) || GetHasSpell(2457) )
		return 5;

	return 0;
}

void main()
{
	//Declare major variables
	object oTarget = GetEnteringObject();
	object oCaster = GetAreaOfEffectCreator();
	location lTarget = GetLocation(OBJECT_SELF);
//	int nDamage;
	int nFire = GetLocalInt(OBJECT_SELF, "NW_SPELL_DELAY_RUNE_FIRE");

//	int nRuneDice = GetRFReserveDamageDice(oCaster);
//	SendMessageToPC(GetFirstPC(FALSE), "nRuneDice Base = " + IntToString(nRuneDice));
//	int nDC = GetReserveSpellSaveDC(nRuneDice,oCaster);
//	SendMessageToPC(GetFirstPC(FALSE), "nDC = " + IntToString(nDC));
//	nRuneDice = nRuneDice * 2;

	effect eDam;
	effect eExplode = EffectVisualEffect(VFX_RUNE_FLAME);
	effect eVis = EffectVisualEffect(VFX_IMP_FLAME_M);
	//Check the faction of the entering object to make sure the entering object is not in the casters faction
	if(nFire == 0)
	{
//		SendMessageToPC(GetFirstPC(FALSE), "Inside nFire if statement.");
//		if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF))
		if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster))
		{
//			SendMessageToPC(GetFirstPC(FALSE), "Inside first spellsIsTarget statement.");
			SetLocalInt(OBJECT_SELF, "NW_SPELL_DELAY_RUNE_FIRE",TRUE);
			ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eExplode, lTarget);
//			SendMessageToPC(oCaster, "Rune activated with " + IntToString(nRuneDice) + "d6 power.");			
//			Cycle through the targets in the explosion area
			oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
			while(GetIsObjectValid(oTarget))
			{
				if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster))
				{
//					Fire cast spell at event for the specified target
					SignalEvent(oTarget, EventSpellCastAt(oCaster, SPELL_DELAYED_BLAST_FIREBALL));
					{
						int nRuneDice = GetRFReserveDamageDice();
						SendMessageToPC(GetFirstPC(FALSE), "nRuneDice = " + IntToString(nRuneDice));
						int nDamage = d6(nRuneDice * 2);
						int nDC = GetReserveSpellSaveDC(nRuneDice,oCaster);
//						Change damage according to Reflex, Evasion and Improved Evasion
						nDamage = GetReflexAdjustedDamage(nDamage, oTarget, nDC, SAVING_THROW_TYPE_FIRE, oCaster);
//						Set up the damage effect
						effect eDam = EffectDamage(nDamage, DAMAGE_TYPE_FIRE);
                        if(nDamage > 0)
                        {
                            //Apply VFX impact and damage effect
                            ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
                            DelayCommand(0.01, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
                        }
                    }
                }
                //Get next target in the sequence
                oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
            }
            DestroyObject(OBJECT_SELF, 1.0);
        }
    }
}

Hopefully someone out there can figure this out. My head hurts from pounding it against the wall.

The on enter script is triggered by the AoE object itself, hence the following lines at the start:

int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER, OBJECT_SELF);
int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF);

as well as the GetHasSpell() calls in the following function are actually getting the levels and spell availability of the AoE object, rather than the caster. Use GetAreaOfEffectCreator() to get the caster instead.

2 Likes

That scripting is already there in the OnEnter script. If you are talking about it being inthe AOE creation script, made no difference. Already thought of that. I consider myself a pretty fair scripter so, simple resolutions I have already thought of. Even put debug lines in the OnEnter script and still got the correct result from the creation script and the incorrect result from the OnEnter script.

@temyankee

I did not look too closely (not at main computer), but look at OBJECT_SELF in your OnEnter script.

EDIT: Removed as referring to wrong OnEnter.

What is OBJECT_SELF being destroyed later? What does your debug code tell you that it is?

1 Like

kevL_s sorted it out. I keep learning more and more because of kevL_s. But, thanx.

1 Like

@temyankee
@kevL_s

So what was the problem then? (Save me looking any closer.)

I was trying to give some pointers that may have helped you to discover for yourself.

Akhacha nailed it above. The spellscript that creates the AoE-object is run on the caster while the AoE’s OnEnter script is run on the aoe-object … OBJECT_SELF had to be sorted out

1 Like

Thanks … I realise now that the OnEnter was a completely different Event Hook than I thought he was referring to. :+1:

I must give up trying to look at scripts on my phone! :dizzy_face:

1 Like

event hooks and script owners, the not so subtle basics of scripting …

/hehe

1 Like

When all you read is OnEnter, the first thing I think is “area”, then I think, “oh, what about Module”, and finally, “Triggers”!

And when you are feeling groggy… and on a mobile, that’s a totally bad combination! :rabbit:

Then, AoEs! … And only after my brain moves on a gear or two. :grin:

areas, modules, triggers … and aoe-objects … hence the begged question: what is OBJECT_SELF ?

i hear ya,

1 Like

Exactly … At least I had a partial correct response, as the debug should have helped here, as I said. :+1:

(I need to ping you a PM about a quick matter of detecting VFX on items … it should be a quickie for you.)

1 Like

here’s a first line of debug (for those who need one)

SendMessageToPC(GetFirstPC(FALSE), "name_of_script - " + GetName(OBJECT_SELF) + " ( " + GetTag(OBJECT_SELF) + " )");

 
 
 
/anywho

1 Like

@kevL_s

Maybe one could do something with the GetEventHandler function too? :thinking:

P.S. I assume that would be “name of script owner”…

///////////////////////////////////////////////////////////////////////////////
// GetEventHandler
///////////////////////////////////////////////////////////////////////////////
// Created By: Brock Heinz - OEI
// Created On: 12/20/05
//
// Description: Retrieves the name of a script bound to a given event ID
// Arguments
//  oObject -  The object to which you want to bind a new script
//  iEventID - The event ID which you are binding the script to.
//
// Returns: Returns a string with the script name which the object has bound
//          to the event. This will be empty if the creature or event ID is
//          invalid.
///////////////////////////////////////////////////////////////////////////////
string GetEventHandler( object oObject, int iEventID );

not as useful for debugging … it gets the name of the script that, well, you’re looking at … unless used to get the name of a different eventhandler …

I mean, sometimes i use it to print all the current handlers of an object’s events (for whatever reason)

1 Like

@kevL_s

Yes, this.

That said, I have also used it to patch a game. It was one of those really helpful functions at that time I discovered it for myself. :slightly_smiling_face:

1 Like

@temyankee ,

You might want to store the damage dice on the AoE object when it is created in the spell script. If the caster uses up a spell giving the current HD in the six rounds after the AoE is created, the damage dice number would change, even going to zero. The best place to store the value is the AoE tag but that is already being used to prevent duplicate AoEs. An alternative would be to store the AoE object as a local variable on caster to check for duplicates.

1 Like

i noticed that … thought it was an amusing ‘feature’ …

there are some holes in the scripts … eg. a tag is not assigned so a prior instance won’t be destroyed on a second instance …

The tag is correctly assigned as a parameter in the call to EffectAreaOfEffect. From what I remember the OnEnter for the AoE is immediately called when it is created so that is why the hit dice needs to be available to the script.

Another alternative to saving the hit dice is storing the hit dice on the caster using SetLocalInt in the spell script before calling ApplyEffectAtLocation.

ok you’re right, I was looking at a different version of the script /sry

the version i fixed up for Tem had the last 4 args of EffectAreaOfEffect() commented out … so i simply made a comment about it there (and am reminding him here :)