• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Stun Engine v.1.1

Stun Engine

Requirements
  • Any Unit Indexer
Known issues
  • Can't stun neutral passive, only neutral hostile.
  • Can't stun buildings.
JASS:
//Stun Engine v.1.1
//******************************************************************************************************
//*
//*     Apply or remove stuns to units. GUI friendly.
//*
//*     This systme only works if the Trigger has the excact name: Stun Engine
//*         --> Information: The intializer function InitTrig_Stun_Engine doesn't work for a different trigger name.
//*
//******************************************************************************************************
//*
//*     API
//*
//*         STE_DURATION       --> desired stun duration
//*         STE_TARGET         --> will get stunned.
//*         STE_ADD_STUN       --> set this to false to remove a stun
//*         STE_STACK          --> set this to true to stack stun times.
//*
//*     Execute the stun with
//*
//*         call TriggerEvaluate(udg_STE_TRIGGER) 
//*
//******************************************************************************************************
//*
//*             SETTINGS                           
//*      
    constant function StunEngineDummyId takes nothing returns integer
        return 'n000'
    endfunction
    
    constant function StunEngineAbility takes nothing returns integer
        return 'A000'
    endfunction
    
    constant function StunEngineBuff takes nothing returns integer
        return 'B000'
    endfunction
    
    constant function StunEngineAccuracy takes nothing returns real
        return 0.031250000
    endfunction
    //OrderId used for "stomp"
    constant function StunEngineAbilityOrder takes nothing returns integer
        return 852127
    endfunction
//*
//*          END OF SETTINGS                         
//*       
//*******************************************************************************************************        

     
    function StunEnginePeriodic takes nothing returns nothing
        local integer i = 0
        local integer index
        loop
            exitwhen i == udg_STE_MAX
            set index = GetUnitUserData(udg_STE_UNIT[i])
     
            if (0.00 < udg_STE_TRACK[index]) and (0 < GetUnitAbilityLevel(udg_STE_UNIT[i], StunEngineBuff())) then
                set udg_STE_TRACK[index] = udg_STE_TRACK[index] - StunEngineAccuracy()
            else
                call UnitRemoveAbility(udg_STE_UNIT[i], StunEngineBuff())
                set udg_STE_TRACK[index] = 0.00
                
                set udg_STE_MAX = udg_STE_MAX - 1
                set udg_STE_UNIT[i] = udg_STE_UNIT[udg_STE_MAX]
                set udg_STE_UNIT[udg_STE_MAX] = null
                set i = i - 1
     
                if (0 == udg_STE_MAX) then
                    call PauseTimer(udg_STE_TIMER)
                endif
     
            endif
            set i = i + 1
        endloop
    endfunction
     
    function EvaluateStunEngine takes nothing returns boolean
        local integer index = GetUnitUserData(udg_STE_TARGET)
        
        if (udg_STE_ADD_STUN) then
            if (0.00 < udg_STE_DURATION)  then
     
                if udg_STE_STACK_TIME then
                    set udg_STE_TRACK[index] = udg_STE_TRACK[index] + udg_STE_DURATION
                elseif (udg_STE_TRACK[index] < udg_STE_DURATION) then
                    set udg_STE_TRACK[index] = udg_STE_DURATION
                endif
     
                if (0 == GetUnitAbilityLevel(udg_STE_TARGET, StunEngineBuff())) then
                    call SetUnitX(udg_STE_CASTER, GetUnitX(udg_STE_TARGET))
                    call SetUnitY(udg_STE_CASTER, GetUnitY(udg_STE_TARGET))
                    call IssueImmediateOrderById(udg_STE_CASTER, StunEngineAbilityOrder())
                    call SetUnitPosition(udg_STE_CASTER ,2147483647,2147483647)
     
                    if (0 == udg_STE_MAX) then
                        call TimerStart(udg_STE_TIMER, StunEngineAccuracy(), true, function StunEnginePeriodic)
                    endif
     
                    set udg_STE_UNIT[udg_STE_MAX] = udg_STE_TARGET
                    set udg_STE_MAX = udg_STE_MAX + 1
                endif
            endif
        else
            call UnitRemoveAbility(udg_STE_TARGET, StunEngineBuff())
            set udg_STE_TRACK[index] = 0.00
        endif
            
        set udg_STE_TARGET = null
        set udg_STE_DURATION = 0.00
        return false
    endfunction

    function InitTrig_Stun_Engine takes nothing returns nothing
        set udg_STE_TRIGGER = CreateTrigger()
        call TriggerAddCondition(udg_STE_TRIGGER, Condition(function EvaluateStunEngine))
        
        set udg_STE_CASTER = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), StunEngineDummyId(), 0.,0.,0.)
        call UnitAddAbility(udg_STE_CASTER, StunEngineAbility())
        call UnitAddAbility(udg_STE_CASTER, 'Aloc')
        call SetUnitPosition(udg_STE_CASTER ,2147483647,2147483647)
    endfunction
Made, because the existing GUI Stun Systems weren't really satisfying or lacking features.

Keywords:
stun, stun system, StunSystem, buff, stomp, GUI
Contents

GUI Stun Engine (Map)

Reviews
00:11, 9th Mar 2014 PurgeandFire: Approved. A great stun system.

Moderator

M

Moderator

00:11, 9th Mar 2014
PurgeandFire: Approved. A great stun system.
 
Level 10
Joined
Aug 21, 2010
Messages
316
Primary it is designed for GUI-only users -> most are designed for GUI users

You made up your mind pretty quickly -> false

I bet you haven't even taken a look into the code itself -> true. Even if I looked, probably would not see anything new.

So the point is to make something new.
 
Is it bad if someone makes an old idea but in an better way? :s
____________________________

I don't like that nearly whole trigger is custom scripts, would look better in JASS.. but that's only cosmetics.

BPower, I would change one thing: Don't use /* and */ for comments ..so normal WE can not use it :p

But anyway, why not? Can be useful. :csmile:
 
Change this.
JASS:
function InitTrig_GUI_Stun_Engine takes nothing returns nothing
    set gg_trg_GUI_Stun_Engine = CreateTrigger(  )
    call TriggerAddAction( gg_trg_GUI_Stun_Engine, function Trig_GUI_Stun_Engine_Actions )
endfunction

To this
JASS:
function InitTrig_GUI_Stun_Engine takes nothing returns nothing
    call TriggerAddAction( CreateTrigger(), function Trig_GUI_Stun_Engine_Actions )
endfunction

Edit:
It would actually be better to use a timer rather than a trigger since you destroy it. You also never add an event so the actions will never fire.
Also have you tried simply running the actions at init ?
 
Level 10
Joined
Aug 21, 2010
Messages
316
Is it bad if someone makes an old idea but in an better way? :s
____________________________

I don't like that nearly whole trigger is custom scripts, would look better in JASS.. but that's only cosmetics.

BPower, I would change one thing: Don't use /* and */ for comments ..so normal WE can not use it :p

But anyway, why not? Can be useful. :csmile:


No, but still it's an old idea, so nothing new and why this is not JASS ?
 
No, but still it's an old idea, so nothing new

If it was not available in GUI then it is a good idea. Even if it is the same thing it is still useful.

You are basically saying that anything that is Jass / vJass should not be remade for GUIers because it has already been made. That is not a good way to look at any type of system. Systems are meant to make it easier for the user.
If you would not want something remade then we should get rid of the GUI unit indexer since there is a jass / vJass equivalent.
 
Level 10
Joined
Aug 21, 2010
Messages
316
If it was not available in GUI then it is a good idea. Even if it is the same thing it is still useful.

You are basically saying that anything that is Jass / vJass should not be remade for GUIers because it has already been made. That is not a good way to look at any type of system. Systems are meant to make it easier for the user.
If you would not want something remade then we should get rid of the GUI unit indexer since there is a jass / vJass equivalent.

Definitely, you do not understand what I'm saying.

But it does not matter.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Edit:
It would actually be better to use a timer rather than a trigger since you destroy it. You also never add an event so the actions will never fire.
Also have you tried simply running the actions at init ?
Good idea. The Map init trigger just initializes the 4 configurable variables nothing more.
Edit: Afaik you can't prevent the creation of the init trigger. I'll just leave it as a minor flaw.
No, but still it's an old idea, so nothing new and why this is not JASS ?
I see no reason to make it JASS, in fact there wouldn't even be a real benefit in doing so.
Most ideas in wc3 modding are "old" and I'm fully aware this might face rejection.
On the other side I tried to optimized the code to a maximum, while including all features of a stun system.
In the end GUI Stun Engine will probably be the best GUI stun-system in our spell data base, not only because
of the constructive criticism of members like deathismyfriend.

What bothers me is the use of Unit Indexer, while I'm using it frequently many users might prefer a hashtable instead. Any opinions on that?
 
Last edited:
Level 4
Joined
Jan 13, 2014
Messages
9
Is it bad if someone makes an old idea but in an better way? :s
____________________________

I don't like that nearly whole trigger is custom scripts, would look better in JASS.. but that's only cosmetics.

BPower, I would change one thing: Don't use /* and */ for comments ..so normal WE can not use it :p

But anyway, why not? Can be useful. :csmile:

Plus whats wrong with a few duplicates of the same system type? What if you don't like something about one system, perhaps the other has something different to offer.

Options... Options... Options!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
SetUnitPosition checks for pathability, hence the unit will be place at the edge of the map.
The disadvantage of SetUnitPosition towards SetUnitX/Y is, that it's according to TriggerHappys latest benchmarks a ~90% slower operation.

In a previous release I used SetUnitPosition only once, called GetUnitX/Y and stored those into variables from then on you could savely use SetUnitX/Y.
I wanted to get rid of the global x and y real variable that's why I changed it back.

Although the stun dummy has no visible model it's still a neat move to put it to the edge of the map, because wc3 hides units outside the screen to save perfomance.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
Y U no set the duration of the stun ability to 0.00? it's permanent
and your dummy unit, why you don't use DUMMY.mdl? so you dont need to hide it from map..

okay, let me wrap it into a nice list:
- set the dummy ability duration to 0.00 instead of 3600.00. it's permanent I didn't notice it's warstomp :D
- use dummy.mdl is better so you don't need to hide the dummy from the map
- use SetUnitX/Y than SetUnitPosition, put the dummy in the middle of the map, if you use dummy.mdl and give it locust. it's safe.

I think just that.. very useful and I hope other moderator can review it for you..

EDIT:
JASS:
            if (0 < udg_STE_TRACK[index]) and (0 < GetUnitAbilityLevel(udg_STE_UNIT[i], StunEngineBuff())) then
                set udg_STE_TRACK[index] = udg_STE_TRACK[index] - StunEngineAccuracy()
            else
                call UnitRemoveAbility(udg_STE_UNIT[i], StunEngineBuff())
isn't that wise to store those fcs into local first? if there are a lot of units in the loop, it will improve the performance..
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Y U no set the duration of the stun ability to 0.00? it's permanent
That's not correct.
The stun duration is always 99999. You have to remove the buff in order to stop the stun.
and your dummy unit, why you don't use DUMMY.mdl? so you dont need to hide it from map..
Completly optional, it's a stun- not a missle system
use SetUnitX/Y than SetUnitPosition, put the dummy in the middle of the map, if you use dummy.mdl and give it locust. it's safe.
Units off screen won't be rendered.
The SetUnitPosition perfomance loss is not noticeable at all in this case.
In case you use the dummy.mdl, you don't have to move the unit at all, but then it might not be off screen.
isn't that wise to store those fcs into local first? if there are a lot of units in the loop, it will improve the performance..
Yeah .... no.

Spoken gernerally:
Declaration and reading a local vs reading an array.... Show me a benchmark in what you can even detect a difference.
Needless to say the loop isn't bigger than ~500, which is already ludacris because noone stuns 500 units at once.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I don't want to get rid of SetUnitPosition. It does what it is supposed to do.

Let me set up a realistic scenario, in which you stun 30 units at once.
The stopwatch native will not print out a reliable result for 30 SetUnitPosition function calls vs. 30 SetUnitX/Y function calls. Again this is a stun and not a missile system.

Also missile resources could ( should in my mind) use SetUnitPosition when they push a used Dummy unit back into the stack.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you should state at the top that it should be placed in "GUI Trigger" called Stun Engine otherwise it wont work

evaluating trigger is good, but then you are missing things like StopStunDuration, PauseStunDuration etc etc that could be useful, if you dont want to use many many flag variables

also currently afaik(prove me wrong if its not true) you only allow it to stun only 1 unit per trigger evaluation, that is kind of costly dont you think?

you could add group variable, if you want to keep it the way it is that it will check and at the end of run clear and if it is empty, just perform stuff with STE_TARGET, otherwise enumerate the group

The above is optional obviously

but isnt 0.00 seconds stun permanent? like, forever, unless you remove the buff or somehow dispel it?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
ANd btw if you're using debug keyword go use vJASS XD !
mmh :)
you should state at the top that it should be placed in "GUI Trigger" called Stun Engine otherwise it wont work
Indeed, that's important information.
evaluating trigger is good, but then you are missing things like StopStunDuration, PauseStunDuration etc etc that could be useful, if you dont want to use many many flag variables
I'm not sure if I got that one. Do you mean storing the remaining stun duration?
also currently afaik(prove me wrong if its not true) you only allow it to stun only 1 unit per trigger evaluation, that is kind of costly dont you think?
A simple function call is of course much faster than TriggerEvalute, however you are forced to put the code into the map header, which I personally dislike.

I did some stress tests back then and they were all fine.

but isnt 0.00 seconds stun permanent? like, forever, unless you remove the buff or somehow dispel it?
if (0.00 < udg_STE_DURATION) then --> you can't add duration values of 0 or smaller.

I still prefer iAyanami's one.
Yes I do use a modified version of that one too. Because I'm using UnitIndexer I didn't need the table and used an array instead.

Furthermore I made another stun library, with extreme timer accuracy (One timer per unit).

This one is made for people who don't use the JNGP.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
Yes, and your stun system also needs such buff checking too. It's one easy-to-occur bug. And you should explain to user that your stun engine does not work for magic immune, and I guess you can hit neutral units if you use Player(15) for the dummy caster. Sorry to suggest them here since you never response to my suggestions there.
I will move my suggestions here.

And my previous post has been updated:
JASS:
                    call SetUnitX(udg_STE_CASTER, GetUnitX(udg_STE_TARGET))
                    call SetUnitY(udg_STE_CASTER, GetUnitY(udg_STE_TARGET))
                    call IssueImmediateOrderById(udg_STE_CASTER, StunEngineAbilityOrder())
                    call SetUnitPosition(udg_STE_CASTER ,2147483647,2147483647)
You should do the whole actions after applying the buff (put that block before the whole actions). Then check whether the target has successfully buffed or not:
if GetUnitAbilityLevel(u, BUFF_ID) > 0 then
To prevent useless actions if the target is invulnerable or somehow un-targetable.

EDIT:
Have found that this checking always returns true no matter what. I think unit type checking is the best solution, basically, war stomp ability does not work only for buildings and magic immune
 
Last edited:

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
Yes, and your stun system also needs such buff checking too. It's one easy-to-occur bug. And you should explain to user that your stun engine does not work for magic immune, and I guess you can hit neutral units if you use Player(15) for the dummy caster. Sorry to suggest them here since you never response to my suggestions there.
I will move my suggestions here.

And my previous post has been updated:
JASS:
                    call SetUnitX(udg_STE_CASTER, GetUnitX(udg_STE_TARGET))
                    call SetUnitY(udg_STE_CASTER, GetUnitY(udg_STE_TARGET))
                    call IssueImmediateOrderById(udg_STE_CASTER, StunEngineAbilityOrder())
                    call SetUnitPosition(udg_STE_CASTER ,2147483647,2147483647)
You should do the whole actions after applying the buff (put that block before the whole actions). Then check whether the target has successfully buffed or not:
if GetUnitAbilityLevel(u, BUFF_ID) > 0 then
To prevent useless actions if the target is invulnerable or somehow un-targetable.

EDIT:
Have found that this checking always returns true no matter what. I think unit type checking is the best solution, basically, war stomp ability does not work only for buildings and magic immune
 
Top