Stun System 1.12

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: deepstrasz
A channel based stun system.

Pros:
Can be used on any unit or structure, irregardless of invulnerability or status.

Creates one (non periodic) timer for tracking duration & one trigger to track orders (stun, pause, sleep etc). Effectively making it very computationally light.

Cons:
Interrupts orders

JASS:
/* 
STUN SYSTEM 1.12
Credits to Maker & IcemanBo for the original concept

API DOCUMENTATION:

public function Unit takes unit u, real dur, boolean stacks returns nothing
    - Stuns a unit using a real duration and boolean for duration stacking.

    - For non-stacking & multiple applications, the highest duration will be applied.

public function End takes unit u returns nothing
    - Immediately ends a stun on a unit.

public function SetRemaining takes unit u, real dur returns nothing
    - Sets the direct duration of a stun.

public function GetRemaining takes unit u returns real
    - Gets the remaining stun duration.

function IsUnitStunned takes unit u returns boolean
    - Returns a unit's stun status. 
*/

library Stun initializer init
    globals
        private constant integer SPELL_ID = 'STUN'
        private constant integer ORDER_ID = 852239 //String Order: "request_hero"
        private constant hashtable HASHTABLE = InitHashtable()
        private constant string EFFECT = "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget"
        private constant string EFFECT_POINT = "overhead"
        private timer TIMER
        private conditionfunc CONDITION
    endglobals

    public function End takes unit u returns nothing
        local integer unitId = GetHandleId(u)
        local timer t= LoadTimerHandle(HASHTABLE, unitId, 1)
        local integer timerId = GetHandleId(t)
        local trigger trig = LoadTriggerHandle(HASHTABLE, unitId, 2)
        call DestroyEffect(LoadEffectHandle(HASHTABLE, unitId, 3))
        call FlushChildHashtable(HASHTABLE, timerId)
        call DestroyTimer(t)
        call TriggerClearConditions(trig)
        call DestroyTrigger(trig)
        call FlushChildHashtable(HASHTABLE, unitId)
        call UnitRemoveAbility(u,SPELL_ID)
        set t = null
    endfunction

    private function Elapsed takes nothing returns nothing
        call End(LoadUnitHandle(HASHTABLE, GetHandleId(GetExpiredTimer()), 0))
    endfunction

    private function OrderCheck takes nothing returns boolean
        local trigger trig = GetTriggeringTrigger()
        call DisableTrigger(trig)
        call IssueImmediateOrderById(GetTriggerUnit(), ORDER_ID)
        call EnableTrigger(trig)
        set trig = null
        return false
    endfunction

    public function Unit takes unit u, real dur, boolean stacks returns nothing
        local timer t
        local trigger trig
        local real stunRemain
        local integer unitId = GetHandleId(u)
        local integer i = 0
        if LoadBoolean(HASHTABLE,unitId,0) then
            set t = LoadTimerHandle(HASHTABLE, unitId, 1)
            set stunRemain = TimerGetRemaining(t) - TimerGetElapsed(t)
            call PauseTimer(t)
            if stacks then
                set stunRemain = stunRemain + dur
            elseif stunRemain < dur then
                set stunRemain = dur
            endif
            call TimerStart(t, stunRemain, false, function Elapsed)
        else
            call UnitAddAbility(u,SPELL_ID)
            call IssueImmediateOrder(u,"stop")
            call BlzUnitDisableAbility(u, SPELL_ID, false, false)
            call IssueImmediateOrderById(u, ORDER_ID)
            set trig = CreateTrigger()
            call TriggerRegisterUnitEvent(trig, u, EVENT_UNIT_ISSUED_ORDER )
            call TriggerAddCondition(trig, CONDITION)
            set t = CreateTimer()
            call SaveBoolean(HASHTABLE, unitId, 0, true)
            call SaveTimerHandle(HASHTABLE, unitId, 1, t)
            call SaveTriggerHandle(HASHTABLE, unitId, 2, trig)
            call SaveEffectHandle(HASHTABLE, unitId, 3, AddSpecialEffectTarget(EFFECT,u,EFFECT_POINT))
            call SaveUnitHandle(HASHTABLE, GetHandleId(t),0,u)
            call TimerStart(t, dur, false, function Elapsed)
        endif
        set t = null
        set trig = null
    endfunction

    public function SetRemaining takes unit u, real dur returns nothing
        local timer t= LoadTimerHandle(HASHTABLE, GetHandleId(u), 1)
        call PauseTimer(t)
        call TimerStart(t, dur, false, function Elapsed)
        set t = null
    endfunction

    function IsUnitStunned takes unit u returns boolean
        return LoadBoolean(HASHTABLE, GetHandleId(u),0)
    endfunction

    public function GetRemaining takes unit u returns real
        set TIMER = LoadTimerHandle(HASHTABLE, GetHandleId(u), 1)
        return TimerGetRemaining(TIMER) - TimerGetElapsed(TIMER)
    endfunction

    private function init takes nothing returns nothing
        set CONDITION = Condition( function OrderCheck)
    endfunction
endlibrary
Contents

Stun System 1.12 (Map)

Level 19
Joined
Aug 13, 2013
Messages
1,679
I can't test this in-game but...

"A simple stun system that uses a method no other system uses."

What method exactly?

"Has no limitation on which targets may be stunned and plays nice with default stun mechanics."

I don't think you're sure about this judging on the script,
I wouldn't dive through optimizations as I'm just curious;
ORDER_ID = 852239 <- kindly input a prefix or comment of its string equivalent.
I don't see any stomp or thunderbolt methods here so I guess you're ordering a unit to simulate a fake stun via infinite channel,
Which is not the same mechanics for original stun, in case I'm not wrong about this. However, If this is correct then it's limited. :]

Besides, we have dozens of this system already, each with bugs and limitations, as this can't be doable perfectly of bug-free yet.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
I can't test this in-game but...

"A simple stun system that uses a method no other system uses."

What method exactly?

"Has no limitation on which targets may be stunned and plays nice with default stun mechanics."

I don't think you're sure about this judging on the script,
I wouldn't dive through optimizations as I'm just curious;
ORDER_ID = 852239 <- kindly input a prefix or comment of its string equivalent.
I don't see any stomp or thunderbolt methods here so I guess you're ordering a unit to simulate a fake stun via infinite channel,
Which is not the same mechanics for original stun, in case I'm not wrong about this. However, If this is correct then it's limited. :]

Besides, we have dozens of this system already, each with bugs and limitations, as this can't be doable perfectly of bug-free yet.

It uses infinite channel. I've tested it quite extensively and have not found any issues thus far.

How does this stun system works internally?

Channel with infinite follow through time. If cast successfully, the system only uses one, non repeating timer per target.

If unable to cast, the target will attempt casts every period until able to do so or stun duration ends.
 
Last edited:
Level 19
Joined
Aug 13, 2013
Messages
1,679
I've tested it quite extensively and have not found any issues thus far.
Tested it or not, the logic of ordering a unit itself to simulate a stun-like mechanic to itself is already prone to fail at some cases.
Plus, this breaks the order rather than retaining it, it also blacks out the command card which is not the case for original stun...

I kinda disapprove the method you chose for stunning units, it would be good for pausing units though, which is already known.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
Tested it or not, the logic of ordering a unit itself to simulate a stun-like mechanic to itself is already prone to fail at some cases.
Plus, this breaks the order rather than retaining it, it also blacks out the command card which is not the case for original stun...

I kinda disapprove the method you chose for stunning units, it would be good for pausing units though, which is already known.
This system accounts for failed casts. But valid points beyond that.

Here I thought this was an original idea lol. I can make a list of pro/cons of using this method if the system won't be outright rejected. (And credit IcemanBo)
 
Last edited:
Level 19
Joined
Aug 13, 2013
Messages
1,679
This system accounts for failed casts.
While it's a good thing you've taken it into consideration, I don't think it would be good enough for the unit's inability to execute the order.
Take this for sleeping or silenced units for example, they couldn't execute an order from their state which would just generate an overhead.
A timer runs as they couldn't execute an order and would only be destroyed if the order is successful, otherwise if duration elapsed it'll linger.
Here I thought this was an original idea lol. I can make a list of pro/cons of using this method if the system won't be outright rejected.
It's a fine idea but not that reliable to replace or at least simulate a stun. Before making a public system make sure to check for the others.
You may stick to this and wait for the moderator's verdict or at least update it with exemplary features that would make it shine from the rest.

(These statements are just my point of view though. Just criticizing this as I'm just curious on what method you've done here in the first place)
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
While it's a good thing you've taken it into consideration, I don't think it would be good enough for the unit's inability to execute the order.
Take this for sleeping or silenced units for example, they couldn't execute an order from their state which would just generate an overhead.
A timer runs as they couldn't execute an order and would only be destroyed if the order is successful, otherwise if duration elapsed it'll linger.

It's a fine idea but not that reliable to replace or at least simulate a stun. Before making a public system make sure to check for the others.
You may stick to this and wait for the moderator's verdict or at least update it with exemplary features that would make it shine from the rest.

(These statements are just my point of view though. Just criticizing this as I'm just curious on what method you've done here in the first place)
Very fair points. You're right, I had not considered silence. Maybe I'll rework this with another method I was considering.

Edit: silence does not affect channel if it has universal cast enabled, I'll update this as soon as I can with the silence fix. (If this was fixed, another option is to enable the ability with the new natives)

I also have a fix to remove the additional timer. Also the timer at the moment does not linger, it is destroyed when the duration ends.
 
Last edited:
Level 19
Joined
Aug 13, 2013
Messages
1,679
silence does not affect channel if it has universal cast enabled
Yeah, I've also read that from somewhere else claiming it. I didn't tested it yet but you may try it here and then prove if it works. (I hope so)
Also the timer at the moment does not linger, it is destroyed when the duration ends.
Didn't notice you're destroying two timers even the other one is only created if a condition is met, otherwise you're destroying a non-existent.
 
Level 19
Joined
Aug 13, 2013
Messages
1,679
Destroying a null timer is less computationally taxing then checking a comparison then destruction imo. , it may not be for this scenario
It's not about performance, it's just a bad practice. I mean you already stated the obvious: "But seeing as missed casts may be rare"
Hence, the timer should only be destroyed if it's created, what's inside the script: destruction of it always while creation of it depends.

EDIT:

You can even merge the destruction of that periodic timer with an extra 'or' check inside the OrderCheck function by the way.
 
Last edited:
In my resource, back then at least I could not really verfiy Bribe's claim with silence, so I'm not sure, too. He didn't also provide demo code when I asked, so idk if someone has shown what exact scenario bugs.
(And credit IcemanBo)
You might read the linked thread for pro/contra which was stated, but not really need to credit me. Though thanks. : ) I also just saw Maker once doing it, and submitted the method as resource.
 
Level 19
Joined
Aug 13, 2013
Messages
1,679
Adding an or check to a 0.03125 timer increases computation unnecessarily versus passing a null to destroy timer once. Also if a stun is removed, a new instance (within the period) would potentially overwrite the pre-existing timer.
It wouldn't overwrite if the concatenated 'or' checks for the stun removal obviously, which makes your code shorter and logical.
You shouldn't worry much of the performance anyway, I don't even think such comparison impacts performance. (it's negligible)
Safety > Functionality > Performance (cause honestly it just looks like you're removing a unit that doesn't even exist or created)

But yeah I couldn't persuade you, it's your choice anyways as this is your resource. It's always up to the moderator in the end. :]

what exact scenario bugs.
Ordering a unit to cast an ability that's silenced wouldn't work, casting silence on a unit that's currently channeling also breaks it.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
Update 1.10:
Fixed silence issues.
Now it only uses a trigger to track when to apply the ability.
Added credits.
Added pros & cons.
Demo map now stun selected unit when pressing esc.

Edit:
Added fringe case fix: units that train, upgrade or research wouldn't be stunned. So on every stun 7 cancel orders are issues. (This probably explains why buildings generally can't be stunned afaik). I attempted a work around using BlzUnitDisableAbility and BlzUnitHideAbility but it appears to hard code lock abilities from being cast during these cases.

Edit 2:
I've been trying to break this and it seems to hold up amazingly well so far. It even works with morph, which is totally surprising.
 
Last edited:

MyPad

Spell Reviewer
Level 24
Joined
May 9, 2014
Messages
1,730
I attempted a work around using BlzUnitDisableAbility and BlzUnitHideAbility but it appears to hard code lock abilities from being cast during these cases.

Not only does the game disable most abilities a unit has when training another unit, it does so multiple times, corresponding to the number of units you force that particular unit to train. I remembered dealing with this scenario while creating my entries in techtree contests.

I can suggest two approaches, the one using BlzUnitDisableAbility and another using BlzPauseUnitEx.

The first approach:
Try placing BlzUnitDisableAbility inside the loop. When successful, this will actually interrupt the training progress of buildings, as far as my tests go.

The second approach:
Just use BlzPauseUnitEx to simulate a stun. From my tests, this respects shift-queue orders, which was quite surprising.
 

MyPad

Spell Reviewer
Level 24
Joined
May 9, 2014
Messages
1,730
Only problem with pauseunit is that it pauses buffs afaik

It's not the ordinary PauseUnit I am talking about. BlzPauseUnitEx can stop a unit in its tracks, while respecting shift-queue orders, and leaves ordinary buffs untouched.

Empirically, PauseUnit also disables all abilities a desired unit has, which breaks shift-queue orders, and also does the above. Internally, it must be calling BlzPauseUnitEx.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
It's not the ordinary PauseUnit I am talking about. BlzPauseUnitEx can stop a unit in its tracks, while respecting shift-queue orders, and leaves ordinary buffs untouched.

Empirically, PauseUnit also disables all abilities a desired unit has, which breaks shift-queue orders, and also does the above. Internally, it must be calling BlzPauseUnitEx.
So I attempted to stack BlzUnitDisableAbility and that didn't work. BlzPauseUnitEx sounds like it would work just fine, but that removes all the clever hackery of this system lol

Edit:
Turns out BlzUnitDisableAbility has to have both boolean fields (disable & hide, set to false, because hide also disables abilities? lol)

New version released with the fix (only one DisableAbility call is required even with multiple items in the queue)

So now the only flaw remaining in this system is order interruption, which doesn't seem like a big deal imo, considering its a stun.
 
Last edited:
Level 19
Joined
Aug 13, 2013
Messages
1,679
It's not the ordinary PauseUnit I am talking about. BlzPauseUnitEx can stop a unit in its tracks, while respecting shift-queue orders, and leaves ordinary buffs untouched.
That's interesting and didn't know that native even existed. In what patch it's introduced anyway?
Empirically, PauseUnit also disables all abilities a desired unit has, which breaks shift-queue orders
It's also notable that it pauses expiration timer of unit which is kinda minor but impacts gameplay.
I do also remember that it disrupts unit animations, particularly when it dies. (not sure about this)
So I attempted to stack BlzUnitDisableAbility and that didn't work. BlzPauseUnitEx sounds like it would work just fine, but that removes all the clever hackery of this system lol
I don't see this method as a clever way of stunning units, it's been used for pausing units throughout the years and here you just screwed those resources that uses this kind of technique as an alternative to at least remedy pause/unpause side-effects. Imagine another resource that utilizes this kind of method and then suddenly uses it on a stunned unit by this system. It would cause conflict for those that made this without lock-safe.

I would better call this a Pause System rather than a Stun System.

Besides, if BlzPauseUnitEx native yields perfectly the same as this method results then I think it's better to deprecate the method.
(which doesn't require ability object anymore and is much more robust as it doesn't require a unit to execute an order, and rendering it's safe)
So now the only flaw remaining in this system is order interruption, which doesn't seem like a big deal imo, considering its a stun.
The whole method is flawed in the first place, make sure to test this on illusion, polymorphed or even militia units.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
That's interesting and didn't know that native even existed. In what patch it's introduced anyway?

It's also notable that it pauses expiration timer of unit which is kinda minor but impacts gameplay.
I do also remember that it disrupts unit animations, particularly when it dies. (not sure about this)

I don't see this method as a clever way of stunning units, it's been used for pausing units throughout the years and here you just screwed those resources that uses this kind of technique as an alternative to at least remedy pause/unpause side-effects. Imagine another resource that utilizes this kind of method and then suddenly uses it on a stunned unit by this system. It would cause conflict for those that made this without lock-safe.

I would better call this a Pause System rather than a Stun System.

Besides, if BlzPauseUnitEx native yields perfectly the same as this method results then I think it's better to deprecate the method.
(which doesn't require ability object anymore and is much more robust as it doesn't require a unit to execute an order, and rendering it's safe)

The whole method is flawed in the first place, make sure to test this on illusion, polymorphed or even militia units.
There is a lot going right for this system to be used as a stun. There are no periodic checks that loop through affected units. This means it is orders of magnitude more efficient & more accurate.

Secondly I don't see how this system is flawed considering it is able to handle every scenario thus far.

Thirdly, using BlzPauseUnitEx is far, far more likely to conflict with other systems and resources than this spell cast. Additionally, this method is pause/unpause safe.

Fourthly, I'm going to be looking into the ability to cast certain spells w/ this method while under it's affects. (An ability for instance that can break a stun lock on cast) I already know for a fact that certain instant cast spells like berserk or divine shield would work... Effectively making it the only stun system which can have this mechanic.
 
Level 19
Joined
Aug 13, 2013
Messages
1,679
I don't see how this system is flawed considering it is able to handle every scenario thus far.
make sure to test this on illusion, polymorphed or even militia units.
Thirdly, using BlzPauseUnitEx is far, far more likely to conflict with other systems and resources than this spell cast. Additionally, this method is pause/unpause safe.
I don't know why you're claiming it would conflict to others as it's counter-based native.

BlzPauseUnitEx works just like stun (just make it timed + overhead effect and you're done)
*should work for every units.
*no more ability object.
*no more executing of order.
*no more checking of order.
*no more blacking of command card.
*no more interrupting of order.

A perfect candidate to make one's life easy.
Fourthly, I'm going to be looking into the ability to cast certain spells w/ this method while under it's affects. (An ability for instance that can break a stun lock on cast) I already know for a fact that certain instant cast spells like berserk or divine shield would work... Effectively making it the only stun system which can have this mechanic.
I don't know why you're going in that direction as you should be focusing on resolving the problems with your method.
The only options I could think of for this resource is to either rework it to Pause System or just use BlzPauseUnitEx native.

Otherwise, I wouldn't bother with it anymore if you don't consider these options as I've already explained it all.
 
Level 22
Joined
Jan 1, 2011
Messages
1,453
I'll try to explain it as best I can so you can understand. BlzPauseUnitEx and PauseUnit are widely used. Using either of these natives would cause collisions with other systems and maps that inherently use these natives already.

This method is NOT widely used, therefore collision potential is insignificant.

I don't even want to touch on the other "issues" because I think you need to acknowledge this first - and if you are unable to comprehend this, I would find it difficult to explain anything further.
 
Top