• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Detecting expiration of ability

Status
Not open for further replies.
Level 5
Joined
Jan 6, 2010
Messages
115
Is there a good way of detecting when an ability EXPIRES? For example, I have a spell based on Divine Shield, and I trigger a buff on the casting unit that is supposed to be removed when the spell ends.

It seems to me Stops Casting an ability and Finishes casting neither refers to the expiration point of the ability (what's the difference between these then?).

If no function detects this, I guess I have to trigger a wait function for an amount of time based on the current level of the skill holder? seems a bit tedious, but..

My first temporary fix on this was a 2 sec periodic trigger that checked if a unit is casting the spell, and removing it if not. Is this a viable solution, or is such a continous operation a significant and uncessary load on CPU/memory? The map can have over 1000 units...

Appreciate your thoughts!
 
Register when the spell effect is started, then start a countdown timer equivalent to how long the buff would last. You'll also need to register when the deactivate order is issued, in case the user ends the spell prematurely.

A periodic trigger is generally more expensive. I don't think it'll make a noticeable difference, but it is (1) slightly less accurate [you'll end up running the trigger up to 2 seconds after it has finished, unless you reduce the timer period] (2) from a programming standpoint, direct events are a better practice than repeatedly checking.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
PurgeandFire, that is only true for abilities that cannot/are not dispelled pre-maturely. For both dispelling and trigger buff removal support you would need separate systems that correctly terminate the buff effect pre-maturely when one of those sources is used. This would mean a custom function for buff removal and all dispel abilities would need to be triggered. For this reason a polling approach is perfectly acceptable in my opinion as long as implemented correctly.

Firstly you only need to poll units that actually have the buff in the first place. This instantly reduces the scope. You do this by keeping track of them in a collection and adding them when the buff is added. Secondly you do not need very accurate timing, a 0.5 second poll rate will give you a worst case error of <0.5 seconds.

The system can be made generic to. Keep track of units in an array accompanied by a trigger array for the handler function and an integer (buff type in GUI) for the buff to poll. Every 0.5 seconds loop through the entire array checking if the unit still has the buff and if not then run the handler trigger. Obviously if no units are registered with the system you turn the trigger off (and obviously turn it on when registering).

Additional scaling can be achieved by staggering the checks over many ticks. Instead of checking the entire list every 0.5 seconds, check half the list every 0.25 etc. The register process could adjust a timer to do this dynamically to prevent more than a certain number of units being checked in a single tick.

I am glad in SC2 they had sense and made proper behaviour trigger events. There it is easy to get when a buff or debuff is applied, refreshed, stack changes, expires, removed etc (all separate events).

There will probably be a problem with spell steal but luckily this is one of the least used abilities in WC3 out side of melee.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
It is unfortunate they did not provide native functions for this kind of information. The majority of the immutable object fields have no corresponding native to read them in game (via triggers) and instead have to be cached (arbitrarily) beforehand.

So yes, you will need to do this, until you decide to move on to Dr Super Good's more general solution if that's what you really need.

I guess I have to trigger a wait function for an amount of time based on the current level of the skill holder? seems a bit tedious

I would not worry about the more complex case Dr Super Good mentions until you get the basic system in place, which of course won't work correctly 100% if there's anyway to prematurely cancel a buff.

If you're only doing Divine Shield or other buffs which can't be cancelled (e.g. Doom), then you will be good following Puregandfire's solution.

Register when the spell effect is started, then start a countdown timer equivalent to how long the buff would last. You'll also need to register when the deactivate order is issued, in case the user ends the spell prematurely.
 
Level 5
Joined
Jan 6, 2010
Messages
115
Yeah, I really wish this editor was as powerful as the SCII one. *sigh*. I've wasted a lot of nights working on things that would seem trivial and basic.

Anyway, some simple arithmetics and eventually finding the function calling the ability level of the unit sorted it out in a way. The solution was a bit more primitive than Dr Super Good's, but for now, I gotta proceed completing the core stuff of my project. And btw, luckily any spell stealing will happen against the opposite faction, so I don't really need to worry about it.

Thanks for your replies, gents.
 
Level 12
Joined
May 20, 2009
Messages
822
PurgeandFire, that is only true for abilities that cannot/are not dispelled pre-maturely. For both dispelling and trigger buff removal support you would need separate systems that correctly terminate the buff effect pre-maturely when one of those sources is used. This would mean a custom function for buff removal and all dispel abilities would need to be triggered. For this reason a polling approach is perfectly acceptable in my opinion as long as implemented correctly.

Firstly you only need to poll units that actually have the buff in the first place. This instantly reduces the scope. You do this by keeping track of them in a collection and adding them when the buff is added. Secondly you do not need very accurate timing, a 0.5 second poll rate will give you a worst case error of <0.5 seconds.

The system can be made generic to. Keep track of units in an array accompanied by a trigger array for the handler function and an integer (buff type in GUI) for the buff to poll. Every 0.5 seconds loop through the entire array checking if the unit still has the buff and if not then run the handler trigger. Obviously if no units are registered with the system you turn the trigger off (and obviously turn it on when registering).

Additional scaling can be achieved by staggering the checks over many ticks. Instead of checking the entire list every 0.5 seconds, check half the list every 0.25 etc. The register process could adjust a timer to do this dynamically to prevent more than a certain number of units being checked in a single tick.

I am glad in SC2 they had sense and made proper behaviour trigger events. There it is easy to get when a buff or debuff is applied, refreshed, stack changes, expires, removed etc (all separate events).

There will probably be a problem with spell steal but luckily this is one of the least used abilities in WC3 out side of melee.

Or, instead of scripting all Buff removers in the game, you could instead check if the unit has the buff or not while the timer is running, and if they don't just stop the timer and remove all other effects that buff was suppose to give.

Like,

  • Untitled Trigger 001
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer IndexInit) from 1 to MaxIndex, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • IndexedTimer[IndexInit] Less than 33
            • Then - Actions
              • Set IndexedTimer[IndexInit] = (IndexedTimer[IndexInit] + 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (IndexedUnit[IndexInit] has buff The Best Buff Evar!1!!) Equal to False
                • Then - Actions
                  • Deindex, remove bonuses buff gives/does, etc
                • Else - Actions
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Stuff
            • Then - Actions
              • Other trigger things
            • Else - Actions
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
Why bother running a timer then since you are polling already. The entire point of the timer based system and scripted buff removal is to avoid polling the buff.

As I said, in this situation polling is perfectly fine however I would not poll at 0.03 as that is unnecessary overhead for something that does not need to be that precise. As long as the triggered effects are removed vaguely after the buff it does not really mater if they lasted a fraction of a second longer.

Also your timer is not a real timer. You are using a counter which you increment/decrement every poll and not a timer object which will run script when it expires. Such counters are useful when you need to do something every period but are no replacement for a real timer when you just need to run something in X seconds time since that has no overhead for the period in between where as counters have an overhead proportional to the update frequency.
 
Status
Not open for further replies.
Top