Aller au contenu

Photo

[Solved] Delay Event in a Spellscript?


  • Veuillez vous connecter pour répondre
9 réponses à ce sujet

#1
Sakaslan

Sakaslan
  • Members
  • 18 messages
I'm trying to basically replicate the rogue Awakening ability Flicker for a specialization.  I have everything generally working.  The spell fires, the caster jumps to each target's location and damage is applied.  However, the jumps happen too fast.  I'd like to apply a small delay (probably a quarter second) between jumps.  I don't fully understand how events work, and after having spent 10 hours trying various things with DelayEvent, I've run out of ideas.  Hoping someone here can help me out.

The code I'm working with:


void main()
{
    event ev = GetCurrentEvent();
    int nEventType = GetEventType(ev); // CAST (directly from engine) or COMMAND_PENDING (re-directed by rules_core)

    switch(nEventType)
    {
        case EVENT_TYPE_SPELLSCRIPT_PENDING:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptCastStruct stEvent = Events_GetEventSpellScriptCastParameters(ev);

            Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_PENDING", Log_GetAbilityNameById(stEvent.nAbility));

            // Setting Return Value
            Ability_SetSpellscriptPendingEventResult(COMMAND_RESULT_SUCCESS);

            break;
        }

        case EVENT_TYPE_SPELLSCRIPT_CAST:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptCastStruct stEvent = Events_GetEventSpellScriptCastParameters(ev);

            Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_CAST",Log_GetAbilityNameById(stEvent.nAbility));

            // hand this through to cast_impact
            SetAbilityResult(stEvent.oCaster, stEvent.nResistanceCheckResult);

            break;
        }

        case EVENT_TYPE_SPELLSCRIPT_IMPACT:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptImpactStruct stEvent = Events_GetEventSpellScriptImpactParameters(ev);

            Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_IMPACT",Log_GetAbilityNameById(stEvent.nAbility));

            // AbiAoEImpact(stEvent.lTarget,  stEvent.oCaster, stEvent.nAbility);
            //_ApplyImpactDamageAndEffects(stEvent);
            
            // only work for sphere spells
            int nAoEType = GetM2DAInt(TABLE_ABILITIES_SPELLS, "aoe_type", stEvent.nAbility);
            if (nAoEType == 1)
            {
                // location impact vfx
                if (stEvent.oTarget != OBJECT_INVALID)
                {
                    stEvent.lTarget = GetLocation(stEvent.oTarget);
                }
                Ability_ApplyLocationImpactVFX(stEvent.nAbility, stEvent.lTarget);

                float fRadius = GetM2DAFloat(TABLE_ABILITIES_SPELLS, "aoe_param1", stEvent.nAbility);

                // get objects in area of effect
                object[] oTargets = GetObjectsInShape(OBJECT_TYPE_CREATURE, SHAPE_SPHERE, stEvent.lTarget, fRadius);
                
                // cycle through objects
                int nCount = 0;
                int nMax = Min(GetArraySize(oTargets), MAX_AOE_TARGETS);
                for (nCount = 0; nCount < nMax; nCount++)
                {                    
                        // per-spell effects
                        SetIndividualImpactAOEEvent(stEvent.oCaster,oTargets[nCount],stEvent.nAbility,stEvent.lTarget);
                        event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
                        DelayEvent(0.5, stEvent.oCaster, evCustom);
                }
             }
        
            break;
        }
        case EVENT_TYPE_SPELLSCRIPT_INDIVIDUAL_IMPACT:
        {
            
            
            struct EventSpellScriptImpactStruct stEvent;
            stEvent.nAbility = GetEventInteger(ev,0);
            stEvent.oCaster = GetEventObject(ev, 0);
            stEvent.lTarget = Location(GetEventObject(ev,2),Vector(GetEventFloat(ev,0),GetEventFloat(ev,1),GetEventFloat(ev,2)), 0.0f);
            //_ApplySpellEffects(stEvent,GetEventObject(ev,1));
            object oTarget = GetEventObject(ev,1);
            
            //event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
            //DelayEvent(2.0, oTarget, evCustom);
            // if not the caster
            if (oTarget != stEvent.oCaster)
            {
                // if the target is not dead
                if (IsDead(oTarget) == FALSE && IsObjectHostile(stEvent.oCaster, oTarget) )
                {
            
            
            location lTargetLoc = GetLocation(oTarget);
            command cJump = CommandJumpToLocation(lTargetLoc);
            AddCommand(stEvent.oCaster, cJump);
            ApplyEffectVisualEffect(stEvent.oCaster, stEvent.oCaster, 90084, EFFECT_DURATION_TYPE_INSTANT, 0.0, stEvent.nAbility);
                    
                   
            // main hand
            object oWeapon  = GetItemInEquipSlot(INVENTORY_SLOT_MAIN, stEvent.oCaster);

            // if the attack hits
            int nResult = Combat_GetAttackResult(stEvent.oCaster, oTarget, oWeapon, 0.0f, stEvent.nAbility);
                   
            if (IsCombatHit(nResult) == TRUE)
            {
                // normal damage
                                float fDamage = Combat_Damage_GetAttackDamage(stEvent.oCaster, oTarget, oWeapon, nResult, 0.0);
                                eEffect = EffectImpact(fDamage, oWeapon,0,stEvent.nAbility);
                                Combat_HandleAttackImpact(stEvent.oCaster, oTarget, nResult, eEffect);
            }
                    
            // off hand
            oWeapon  = GetItemInEquipSlot(INVENTORY_SLOT_OFFHAND, stEvent.oCaster);

            // if the attack hits
            nResult = Combat_GetAttackResult(stEvent.oCaster, oTarget, oWeapon, 0.0f, stEvent.nAbility);
                    
            if (IsCombatHit(nResult) == TRUE)
            {
                                // normal damage
                                float fDamage = Combat_Damage_GetAttackDamage(stEvent.oCaster, oTarget, oWeapon, nResult, 0.0);
                                eEffect = EffectImpact(fDamage, oWeapon,0,stEvent.nAbility);
                                Combat_HandleAttackImpact(stEvent.oCaster, oTarget, nResult, eEffect);
            }
            
            //DelayEvent(0.5f, OBJECT_SELF, ev);
                }
            }
        }
    }
}


Modifié par Sakaslan, 18 août 2010 - 03:03 .


#2
Phaenan

Phaenan
  • Members
  • 315 messages
In that code, you're sending the event at the creature or player core, since the event is thrown in the caster's queue :
event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
DelayEvent(0.5, stEvent.oCaster, evCustom);

So that's where you should handle CUSTOM_EVENT_03 and code the jump thingy. However, you may end up having to override the default core scripts for that, depending on who the caster is, so if I were in your shoes I would prolly handle everything in the ability script itself :
DelayEvent(0.5f, stEvent.oCaster, evCustom, GetCurrentScriptName());
Which will ignore stEvent.oCaster's default core and will actually throw the delayed event at the current ability script. Then, you'll just have to handle the CUSTOM_EVENT_03 (or whatever) by adding a new case block in that same script. :o

Modifié par Phaenan, 17 août 2010 - 10:53 .


#3
Sakaslan

Sakaslan
  • Members
  • 18 messages
Thanks, Phaenan. Using your advice I got the delay to work, but it seems like all of the individual impacts are applied at once. What ended up happening is that the complete spell effects executed (all targets in the AE were jumped to and damaged) and then the delay hit. Once the delay was over, the effects executed again, followed by another delay, until every target was dead. I understand why that's happening--I'm telling it to execute the event again after the delay, but I don't really understand how to rectify the situation.



It seems like I need an event that handles one iteration of jump/attack/damage vs. a single target in the oTargets[] array. Then I'd need to have the event handling code for it run once for each target and have it delayed each time. After a few more hours of tinkering, though, I just don't think I understand events well enough to do it.

#4
Phaenan

Phaenan
  • Members
  • 315 messages
I guess you could easily use the SetEventYada() routines to store an iteration count or any variable deemed necessary. And retrieve those in the event handling bits.
For example :
event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
evCustom =
SetEventInteger(evCustom, 0, 1); // Stores 1 at the index 0
DelayEvent(0.5f, stEvent.oCaster, evCustom, GetCurrentScriptName());

And during in the handling :
int iIteration = GetEventInteger(ev, 0); // retrieve the int0 var
object oNextTarget = oTargets[iIteration];

Modifié par Phaenan, 17 août 2010 - 04:48 .


#5
TimelordDC

TimelordDC
  • Members
  • 923 messages
What is the code you've written to handle the custom event?
If you are calling the function
SetIndividualImpactAOEEvent
iteratively again over all the targets, that will happen.

I believe DelayEvent doesn't actually pause script execution. So, this block you have here -
for (nCount = 0; nCount < nMax; nCount++)
{                    
    // per-spell effects
    SetIndividualImpactAOEEvent(stEvent.oCaster,oTargets[nCount],stEvent.nAbility,stEvent.lTarget);
    event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
    DelayEvent(0.5, stEvent.oCaster, evCustom);
}
will send the Impact event to each individual target. Then, if you are doing the same in your custom event, it will get applied every time the custom event is called.

I think if you move the
SetIndividualImpactAOEEvent
function to the custom event, it should work as you desire.

#6
Sakaslan

Sakaslan
  • Members
  • 18 messages

Phaenan wrote...

I guess you could easily use the SetEventYada() routines to store an iteration count or any variable deemed necessary. And retrieve those in the event handling bits.
For example :
event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
evCustom =
SetEventInteger(evCustom, 0, 1); // Stores 1 at the index 0
DelayEvent(0.5f, stEvent.oCaster, evCustom, GetCurrentScriptName());

And during in the handling :
int iIteration = GetEventInteger(ev, 0); // retrieve the int0 var
object oNextTarget = oTargets[iIteration];


I tried to set this up so that when the event is handled, it only applies the jump/damage to the current target of oTargets[iIteration], but it seems like in order to call the event multiple times I have to put the DelayEvent inside the original For Loop.  Doing so just repeats my results of all iterations occuring per event handle, which is then looped repeatedly until all targets are dead.  I've tried calling the custom event inside the For Loop in SPELLSCRIPT_IMPACT and I've also tried sending the SetIndividualImpactAOEEvent inside the For Loop and calling the custom event at the end of SPELLSCRIPT_INDIVIDUAL_IMPACT.  Both produced the same results.  That's not to say that I coded it correctly, though.  So confused at this point, I'm not sure what's done correctly anymore.

I'm not sure how the script for Flicker gets the delay between jumps without using DelayEvent in some fashion, unless the vfx used for each jump (a white misty column-shaped particle effect) somehow enforces a pause.

#7
Sakaslan

Sakaslan
  • Members
  • 18 messages

TimelordDC wrote...

What is the code you've written to handle the custom event?
If you are calling the function

SetIndividualImpactAOEEvent
iteratively again over all the targets, that will happen.

I believe DelayEvent doesn't actually pause script execution. So, this block you have here -
for (nCount = 0; nCount < nMax; nCount++)
{                    
    // per-spell effects
    SetIndividualImpactAOEEvent(stEvent.oCaster,oTargets[nCount],stEvent.nAbility,stEvent.lTarget);
    event evCustom = Event(EVENT_TYPE_CUSTOM_EVENT_03);
    DelayEvent(0.5, stEvent.oCaster, evCustom);
}
will send the Impact event to each individual target. Then, if you are doing the same in your custom event, it will get applied every time the custom event is called.

I think if you move the
SetIndividualImpactAOEEvent
function to the custom event, it should work as you desire.


I've tried handling the custom event a couple of different ways.  First, just including the code to handle the jump, followed by SetIndividualImpactAOEEvent:


// if not the caster
            if (oTarget != oCaster)
            {
                // if the target is not dead
                if (IsDead(oTarget) == FALSE && IsObjectHostile(oCaster, oTarget) )
                {


            location lTargetLoc = GetLocation(oTarget);
            command cJump = CommandJumpToLocation(lTargetLoc);
            AddCommand(oCaster, cJump);
            ApplyEffectVisualEffect(oCaster, oCaster, 90084, EFFECT_DURATION_TYPE_INSTANT, 0.0, nAbility);

            SetIndividualImpactAOEEvent(oCaster,oTarget,nAbility,lTarget);


I thought this would jump to the target and then send it to SPELLSCRIPT_INDIVIDUAL_IMPACT for that specific target, then repeat the custom event for each target (the DelayEvent command for the custom event is still inside the For Loop of SPELLSCRIPT_IMPACT). Instead, it continues to jump to each target and apply damage with no delay.  When all targets have been jumped to, then it's delayed and the custom event handle starts again.

I assume this is because, as you mentioned, DelayEvent doesn't pause scripting so the entirety of the For Loop is executed, sending everything to the custom event at once, and then the custom event sends everything to SPELLSCRIPT_INDIVIDUAL_IMPACT for damage resolution.  Then the delay takes effect, after which the custom event is called again for every target and sends everything back to SPELLSCRIPT_INDIVIDUAL_IMPACT for anothe round of damage.  It repeats until all targets are dead.

The other way I tried handling the custom event was to apply the jump and the attack resolution/damage inside the custom event so that nothing was ever passed to SPELLSCRIPT_INDIVIDUAL_IMPACT.  Rather than use SetIndividualImpactAOEEvent, I just applied combat results/damage directly in the custom event.  It worked the same as everything else.  A round of jump/attack/damage followed by delay, repeating until all targets were dead.

It seems like the issue is trying to add DelayEvent inside the For Loop.  But if that doesn't work, I don't know how to apply DelayEvent in another way so that each iteration of jump/attack/damage is delayed.

Hope the last two posts make sense.  It's all starting to blur together for me at this point.

#8
TimelordDC

TimelordDC
  • Members
  • 923 messages
Can you try this? No need for a custom event or DelayEvent.

int nCount = 0;
int nDelay = 0; //Delay in milliseconds
int nMax = Min(GetArraySize(oTargets), MAX_AOE_TARGETS);
for (nCount = 0; nCount < nMax; nCount++)
{                    
SetIndividualImpactAOEEvent(stEvent.oCaster,oTargets[nCount],stEvent.nAbility,stEvent.lTarget, nDelay);
nDelay = nDelay + 500; //Add 0.5 seconds delay for next target
}

Edit: The SetIndividualImpactAOEEvent has an additional optional parameter to delay the event by the required amount of milliseconds. By default, this is zero so taking into account the speed of script execution, this would mean the event is fired immediately for all targets. By putting a delay in, the event would be pushed back by that many seconds in the event queue - all theory, of course ;)

Modifié par TimelordDC, 18 août 2010 - 02:40 .


#9
Sakaslan

Sakaslan
  • Members
  • 18 messages
All theory, but in this case sound theory. It works perfectly. You're my hero, Timelord. Can't thank you enough. Thanks Phaenan, too, for your efforts.



The tragic part is that about 30 hours ago, the first thing I tried in order to add delay was the optional parameter of SetIndividualImpact. All it did was add half a second between the cast animation and the beginning of the effect, so I promptly moved on. It never once occurred to me to increment the delay per iteration.



Thanks again!

#10
TimelordDC

TimelordDC
  • Members
  • 923 messages
Glad to be of help! :)