• 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.

[System] Stun

JASS:
library StunSystem uses Table

//********************************************************************************
//              Stun - Version 1.2.0.0 - By iAyanami aka Ayanami
//********************************************************************************
//
//    Stun:
//         - An easy to use system that stuns units
//         - Able to keep track of the remaining stun duration
//
//    Requirements:
//         - JASS NewGen
//         - Table
//
//    Functions:
//         - Stun.apply takes unit whichUnit, real duration, boolean stack returns nothing
//           * whichUnit is the target to be stunned
//           * duration is the duration of the stun
//           * stack is to determine if the stun should be a stacking one or not
//           * true - the stun will stack, the duration of the stun will add up with the previous duration
//           * false - the stun will not stack, the unit will be stunned for the longer stun duration
//
//         - Stun.getDuration takes unit whichUnit returns real
//           * whichUnit is the target to check
//           * returns the remaining stun duration
//
//         - Stun.stop takes unit whichUnit returns nothing
//           * removes stun from the target
//
//    How to import:
//         - Copy the whole "Stun" Trigger Folder into your map
//         - Save the map. Close and re-opem the map.
//         - You should have a new unit (Stun Dummy), ability (Stun (System)) and buff (Stun (System)) created
//         - Read through the Configuration part of the code
//
//    Credits:
//         - Bribe for Table
//
//********************************************************************************
//                                CONFIGURABLES
//********************************************************************************

globals
    // timer period. lower the value, the more accurate but might cause decrease in
    // performance
    private constant real PERIOD = 0.03125

    // raw code of ability "Stun (System)"
    private constant integer ABILID = 'ASTN'
    
    // raw code of buff "Stun (System)"
    private constant integer BUFFID = 'BSTN'
    
    // raw code of unit "Stun Dummy"
    private constant integer STUNID = 'sTUN'
endglobals

//********************************************************************************
//                                     CODE
//********************************************************************************

// initialization
module Init
    private static method onInit takes nothing returns nothing
        set table = Table.create()
        set caster = CreateUnit(Player(13), STUNID, 0, 0, 0)
        
        call UnitAddAbility(caster, ABILID)
    endmethod
endmodule

struct Stun extends array
    private unit u
    private real dur

    private thistype next
    private thistype prev
    
    private static Table table
    private static timer t = CreateTimer()
    private static unit caster
    private static integer count = 0
    
    // remove the stun and deallocate
    private method destroy takes nothing returns nothing
        call UnitRemoveAbility(this.u, BUFFID)
        
        if this.next != 0 then
            set this.next.prev = this.prev
        endif
            
        set this.prev.next = this.next
        set this.dur = 0
        set this.prev = thistype(0).prev
        set thistype(0).prev = this
        
        if thistype(0).next == 0 then
            call PauseTimer(t)
        endif
            
        call table.remove(GetHandleId(this.u))
    endmethod
    
    // iterating through all instances every PERIOD
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        
        loop
            set this = this.next
            exitwhen this == 0
            if this.dur <= 0 or IsUnitType(this.u, UNIT_TYPE_DEAD) or GetUnitTypeId(this.u) == 0 then
                call this.destroy()
            else
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod
    
    // immediately removes stun for the specified unit
    // ex: call Stun.stop(whichTarget)
    static method stop takes unit u returns nothing
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            call thistype(table[id]).destroy()
        endif
    endmethod
    
    // gets the duration left for stun, not stunned units always return 0
    // ex: local real r = Stun.getDuration(whichTarget)
    static method getDuration takes unit u returns real
            return thistype(table[GetHandleId(u)]).dur
    endmethod
    
    // stunning specified target and to see if the stun is a stacking one or not
    // ex: call Stun.apply(whichTarget, 5.0, false)
    static method apply takes unit u, real dur, boolean b returns nothing
        local thistype this
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            set this = table[id]
        else    
            if thistype(0).prev == 0 then
                set count = count + 1
                set this = count
            else
                set this = thistype(0).prev
                set thistype(0).prev = thistype(0).prev.prev
            endif
            
            if thistype(0).next == 0 then
                call TimerStart(t, PERIOD, true, function thistype.iterate)
            else
                set thistype(0).next.prev = this
            endif
            
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            set table[id] = this
            set this.u = u
            set this.dur = 0
            
            call IssueTargetOrder(caster, "firebolt", this.u)
        endif
        
        if b and dur > 0 then
            set this.dur = this.dur + dur
        else
            if this.dur < dur then
                set this.dur = dur
            endif
        endif
    endmethod

    implement Init
endstruct

endlibrary

Examples:

Basic ability that stuns a target for 2.0 seconds and non-stacking.
  • Stun
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stun
    • Actions
      • Custom script: call Stun.apply(GetSpellTargetUnit(), 2.0, false)
This time, a more flexible stun. Stuns depending on the caster's agility and non-stacking.
  • Stun
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stun
    • Actions
      • Set TempInt = (Agility of (Triggering unit) (Include bonuses))
      • Custom script: call Stun.apply(GetSpellTargetUnit(), udg_TempInt, false)
Basic ability that stuns a target for 2.0 seconds and stacks.
  • Stun
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stun
    • Actions
      • Set TempInt = (Agility of (Triggering unit) (Include bonuses))
      • Custom script: call Stun.apply(GetSpellTargetUnit(), udg_TempInt, true)
Closing:
I'm open to any more possible functions that can be implemented into this system. Do suggest some. Thanks!

Changelogs:
Version 1.2.0.0:
- Removed Alloc
- Renamed .get to .getDuration
- Renamed .use to .apply
- Made a check to start/pause timer accordingly, rather than letting it run forever
- Added a GetUnitTypeId(this.u) == 0 check
- Used a module initializer
Version 1.1.0.0:
- Fixed minor bugs
- Renamed library to StunSystem
- Added a boolean parameter to Stun.use function to allow user to specify if the stun is a stacking one or not
- Added a new function, Stun.stop which removes stun from the specified unit
Version 1.0.0.0:
- Release.
 

Attachments

  • Stun.w3x
    65.9 KB · Views: 489
Last edited:
set u = null

Parameters don't need to be nulled.

----

Also, I didn't test it, but your system might break under certain circumstances. For example, consider this situation:

Unit gets stunned for 6 seconds. Immediately afterward, the same unit gets stunned for 1 second. The stun ends up lasting only 1 second.

You'll probably have to do something like:
JASS:
if this.dur < dur then
    set this.dur = dur
endif

Or something along those lines.

EDIT: Sorry I accidentally used ljass tags instead of jass tags. :p
 
Last edited:
set u = null

Parameters don't need to be nulled.

----

Also, I didn't test it, but your system might break under certain circumstances. For example, consider this situation:

Unit gets stunned for 6 seconds. Afterward, the same unit gets stunned for 1 second. The stun ends up lasting only 1 second.

You'll probably have to do something like:
if this.dur < dur then set this.dur = dur endif

Or something along those lines.

I always used to think that a new stun would override the old one. Guess I was wrong @_@. Will fix it.

EDIT
Updated to Version 1.1.0.0

EDIT
Just want to ask, does this belong here or would the Spell Submission Section be better?
 
Last edited:
If you set "dur" to 0 from within the destroy method, you can shorten this:

JASS:
    static method get takes unit u returns real
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            return thistype(table[id]).dur
        else
            return 0.
        endif
    endmethod

To this:

JASS:
    static method get takes unit u returns real
        return thistype(table[GetHandleId(u)]).dur
    endmethod

Also, 0.03125 is more practical for the timer.

This still needs to have the GetUnitTypeId == 0 check to compliment the UNIT_TYPE_DEAD.

The timer should not *always* be running.

The initializer should be a module.

You don't need to require Alloc from this. If you recycle using the "prev" variable then you can also eliminate an array that Alloc generates.

Stun.get -> Stun.getDuration

Stun.use -> Stun.apply
 
Epic script indeed :)
But I must report small error, in your examples you use wrong method.
Instead something likecall Stun.use(GetSpellTargetUnit(), udg_TempInt, true)
there should be call Stun.apply(GetSpellTargetUnit(), udg_TempInt, true)
It isn't big deal but still it can make trouble to some people :)
 
Design implementation of this is incorrect.

Needs to be rewritten. The design implementation of my own Stun resource is also incorrect, so both should be graveyarded in light of a good implementation.


The good implementation is a core that takes effects and applies them using a dummy caster. From here, the effect resources are just simple Lua scripts or maps with objects in them =). As there are only a limited number of effects, one thread can be created that contains all of them similar to j4l's Status resource (but better of course).


I don't have time to code this now, so whoever gets it coded first wins (unless ofc their implementation is bad, in which case I will overtake you with my own and tell you that you have been pwned, fuahahhaa). Let the best one win and let this one and mine rest in peace in the graveyard where they belong.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
the first one is bad because it uses hook in AutoIndex and stuff, while Nes's UnitIndexer uses check for Defend
the second one has like 600 other things that if you just want to stun units are totally useless, but it seems fine overall
 
Level 2
Joined
Jul 18, 2010
Messages
12
sorry for my question please answer me.

When I downloaded a Jass , vJass map and testing it,I can play the map[Includ Stun System T T ],if i testing in Wolrd Editor , UMSWE the game will neviagate me to main screen of Wc3
I would like many system that Jass Vjass but i can use this Please Help Me.
:goblin_cry::vw_wtf:



[Sorry again for my English i'm verry of verry weak]
 
Once again, awesome resource.
I'm using it in my new secret project and it works flawlessly so far.

I have edited code a little, and doing so I realized you could do same.
If interested you can add few lines of code that could be used displaying floating texts etc.

Now there are many systems related to ft as well so I just suggest empty function that people can use to handle them.

JASS:
static method ThisNewFunction takes unit target, real duration returns nothing
     //CreateAdvancedTextTag(...
     //bla bla bla etc
     //or maybe can be used for just handling special effect on stun effect and so on
     //may be useful with some damage systems or something as well
endmethod


// static method apply takes unit u, real dur, boolean b returns nothing
// ...
     call IssueTargetOrder(caster, "firebolt", this.u)
     call ThisNewFunction(u, dur)
// ...
Just an suggestion.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Personally I dislike the idea of allowing users to change the code within the stun library.
I think it would be better to fire an Event.
Of course if you know 100% what you are doing, a simple function call is much faster.

I agree Stun is very good, aslong as you use a small clock interval (~0.03125).
However if you want to achieve a maximum of timer fidelity, you have to create one timer per unit coupled with any unit indexer.

You could add function IsUnitStunned takes unit who returns boolean
 
Last edited:
and pausing a unit produces lots of undesirable effects you know...

for a fast example: pause a unit and cast impale on it (make sure that the pause is of enough time for the impale to take effect)

Or add a timed life to a unit and pause it, the timed life will be paused too. You wouldn't want the stun to do that don't you?
 
You don't see a problem??? How come does a stun that pauses buff timers, delays effects of some spells (Impale for example), etc IS NOT A PROBLEM???

for example you can issue it an order in short intervals for a duration

So you'd better sacrifice in-game performance just to get rid of object data when the object data method is far more useful?
 
I'm not sure if there are any more recent implementations that is stable, so I think I will select this for my stun needs for now. Looks simple enough and with 1.31+ vJass support, the whole fest of vJass needs JNGP is over. If there are newer implementations with more reliable results, I think it might be a good idea to provide links.
 
Top