System Stun

Ayanami

칼리
Reaction score
288
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.
Trigger:
  • Stun
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stun
    • Actions
      • Custom script: call Stun.use(GetSpellTargetUnit(), 2.0, false)


This time, a more flexible stun. Stuns depending on the caster's agility and non-stacking.
Trigger:
  • 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.use(GetSpellTargetUnit(), udg_TempInt, false)


Basic ability that stuns a target for 2.0 seconds and stacks.
Trigger:
  • 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.use(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.
 

luorax

Invasion in Duskwood
Reaction score
67
I haven't tested it yet, but seems it has a little problem:

If you stun your unit for 3 secs, and after 0.5 sec you stun it for 1 sec, the stun will be removed after 1 sec, because the duration has been overwritten, however the unit has to keep stunned for 1,5 more secs. You should fix it, like this:

Change this:
JASS:

static method use takes unit u, real dur returns nothing
        local thistype this
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            set this = table[id]
        else    
            set this = thistype.allocate()
            set thistype(0).next.prev = this
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            set table[id] = this
        endif
        
        set this.u = u
        set this.dur = dur
        
        call IssueTargetOrder(caster, "firebolt", this.u)
        
        set u = null
    endmethod


To this:
JASS:

static method use takes unit u, real dur returns nothing
        local thistype this
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            set this = table[id]
        else    
            set this = thistype.allocate()
            set thistype(0).next.prev = this
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            set table[id] = this
        endif
        
        set this.u = u
        if this.dur < dur then
            set this.dur = dur
        endif
        
        call IssueTargetOrder(caster, "firebolt", this.u)
        
        set u = null
    endmethod


And also, why do you use firebolt outside? You don't have to use it again if the unit is already stunned. It should be something like this:
JASS:

static method use takes unit u, real dur returns nothing
        local thistype this
        local integer id = GetHandleId(u)
        
        if table.has(id) then
            set this = table[id]
        else    
            set this = thistype.allocate()
            set thistype(0).next.prev = this
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            set table[id] = this
            call IssueTargetOrder(caster, "firebolt", this.u)
            set this.u = u
        endif
        
        if this.dur < dur then
            set this.dur = dur
        endif
        
        set u = null
    endmethod
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
JASS:
if new>remaining then
    set new - remaining = extended
endif

Start the timer using remaining+extended.

JASS:

static method operator [] takes unit u returns real
    return TimerGetRemaining(thistype(table[GetHandleId(u)]).timer)
endmethod

This is better, don't you?

And also, you can use HandleTable instead, so that you don't have to use GetHandleId by yourself. :)
 

Ayanami

칼리
Reaction score
288
@ luorax

I always thought the newest stun would override the stun. Guess I was wrong xD

Will update soon. Thanks for the feedback.

EDIT
Updated to Version 1.1.0.0
 

tooltiperror

Super Moderator
Reaction score
231
Yeah, I think (as a site) we should move away from a bunch of small snippets and move to larger, standard scripts as WC3C does.

Status accomplishes this and does it in a standard way, so this is redundant.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
I came up with something like this. :)
JASS:
library Stun initializer OnInit requires Table, TimerUtils, DummyCaster, Alloc
	
	globals
	    private constant integer ABIL_ID = 'ASTN'
	    private constant integer BUFF_ID = 'BSTN'
    	    private HandleTable Data
	endglobals
	
	struct Stun extends array
	    private unit u
       	    private timer t
		
	    implement Alloc
		
		static method operator [] takes unit u returns real
			return TimerGetRemaining(thistype(Data<u>).t)
		endmethod
		
		private static method onExpired takes nothing returns nothing
			local thistype this=GetTimerData(GetExpiredTimer())
			call UnitRemoveAbility(this.u,BUFF_ID)
			call Data.flush(this.u)
			call this.deallocate()
		endmethod
		
		static method operator []= takes unit u, real r returns nothing
			local thistype this=Data<u>
			if this==0 then
				set this=thistype.allocate()
				set this.t=NewTimer()
				call SetTimerData(this.t)
				set this.u=u
				set Data<u>=this
				call UnitAddAbility(DUMMY,ABIL_ID)
				call IssueTargetOrder(DUMMY,&quot;firebolt&quot;,u)
			endif
			if TimerGetRemaining(this.t)&lt;r then
				call TimerStart(this.t,r,true,function thistype.onExpired)
			endif
		endmethod
	endstruct
	
	private function OnInit takes nothing returns nothing
		set Data=HandleTable.create()
	endfunction
	
endlibrary</u></u></u>


JASS:

Stun[unit] -&gt; remaining time.
set Stun[unit]=10.0 -&gt; Add 10 seconds of stun to the unit.
 

tooltiperror

Super Moderator
Reaction score
231
I suppose this does have its uses, and the issues with it seem to be resolved.

When only using one status modification, this should suffice, for things bigger, use Status.

Approved.
 

Dirac

22710180
Reaction score
147
This library should use TimerUtils instead of iterating stuns using a pseudo-T32 interface

Also it does not need table, you can perfectly store Handles of units substracting the minimum handle value. Table should be used only for array variables inside structs
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top