• 🏆 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!

StunSystem

Status
Not open for further replies.
Level 17
Joined
Mar 21, 2011
Messages
1,597
Hi, this is a stun system that I made for my map. I posted it here to hopefully get some feedback from you guys :) There are many things to improve for sure.

functions:
StunUnit(unit u, real time, integer type, string animation)

Stun types can be:
- STUN_TYPE_NORMAL // Will apply the "better" stun. That means if the current stun will disappear in 1 second and a new stun would stun for 2 seconds, it will take the 2 seconds
- STUN_TYPE_COVER // Will always overwrite the current stun, no matter which is "better"
- STUN_TYPE_STACK // Will add the stun time to the currently existing stun
- STUN_TYPE_ULTIMATE // Will always overwrite any stun and cannot get overwritten by any stun (except for other stuns by this type), it also cannot be reduced by the stun reduction

RemoveStun(unit u) // removes the stun, does not work for STUN_TYPE_ULTIMATE

IsUnitStunned(unit u) // returns boolean

AddStunReduction(unit u, real value) // Adds a percentage stun reduction, that means that stuns will last for value% shorter/longer. (Does not work on STUN_TYPE_ULTIAMTE)

AddStunReductionTimed(unit u, real value, real time) // Same as before, just timed

I hope you can give me tips on how to improve it ;)

JASS:
library StunSystem initializer Init requires TimerUtils, RegisterPlayerUnitEvent

    globals
        constant integer    STUN_TYPE_NORMAL    = 0
        constant integer    STUN_TYPE_COVER     = 1
        constant integer    STUN_TYPE_STACK     = 2
        constant integer    STUN_TYPE_ULTIMATE  = 3
        constant string     STUN_EFFECT         = "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget.mdl"
        
        private boolean array IsUnitAffectedByUlt
        
        private unit    array   StunnedUnit
        private real    array   StunDuration
        private effect  array   StunEffect
        
        private unit array ReductionUnit
        private real array ReductionDuration
        private real array Reduction
        private real array ReductionValue
        private integer Index = 0
        
        private unit Dummy
    endglobals
    
    private function ClearStun takes unit u returns nothing
        local integer id = GetUnitUserData(u)
        call UnitRemoveAbility(u, 'Bstn')
        set StunDuration[id] = 0
        call DestroyEffect(StunEffect[id])
        set StunEffect[id] = null
        set IsUnitAffectedByUlt[id] = false
    endfunction
    
    private function OnDeath takes nothing returns boolean
        call ClearStun(GetTriggerUnit())
        return false
    endfunction

    function RemoveStun takes unit u returns nothing
        if IsUnitAffectedByUlt[GetUnitUserData(u)] == false then
            call ClearStun(u)
        endif
    endfunction
    
    function IsUnitStunned takes unit u returns boolean
        if StunDuration[GetUnitUserData(u)] > 0 then
            return true
        else
            return false
        endif
    endfunction
    
    function AddStunReduction takes unit u, real r returns nothing
        local integer id = GetUnitUserData(u)
        set Reduction[id] = Reduction[id] + r
    endfunction
    
    private function StunReductionTimed takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer loopIndex = 1
        loop
            exitwhen loopIndex > Index
            set ReductionDuration[loopIndex] = ReductionDuration[loopIndex] - 0.05
            if ReductionDuration[loopIndex] <= 0 then
                call AddStunReduction(ReductionUnit[loopIndex], ReductionValue[loopIndex])
                set ReductionUnit[loopIndex] = ReductionUnit[Index]
                set ReductionUnit[Index] = null
                set ReductionValue[loopIndex] = ReductionValue[Index]
                set ReductionDuration[loopIndex] = ReductionDuration[Index]
                set Index = Index - 1
                set loopIndex = loopIndex - 1
                if Index == 0 then
                    call ReleaseTimer(t)
                endif
            endif
            set loopIndex = loopIndex + 1
        endloop
        set t = null
    endfunction
    
    function AddStunReductionTimed takes unit u, real r, real t returns nothing
        call AddStunReduction(u, r)
        set Index = Index + 1
        set ReductionUnit[Index] = u
        set ReductionValue[Index] = -r
        set ReductionDuration[Index] = t
        if Index == 1 then
            call TimerStart(NewTimer(), 0.05, true, function StunReductionTimed)
        endif
    endfunction
    
    private function StunTimed takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetTimerData(t)
        set StunDuration[id] = StunDuration[id] - 0.05
        if StunDuration[id] <= 0 then
            call ClearStun(StunnedUnit[id])
            call ReleaseTimer(t)
        endif
        set t = null
    endfunction
    
    function StunUnit takes unit u, real r, integer i, string s returns nothing
        local integer id = GetUnitUserData(u)
        local real time
        if i != 3 then
            if Reduction[id] < 1.00 and IsUnitAffectedByUlt[id] == false then
                call SetUnitPosition(Dummy, GetUnitX(u), GetUnitY(u))
                call IssueTargetOrder(Dummy, "thunderbolt", u)
                set time = r - (r * Reduction[id])
                set StunnedUnit[id] = u
                if StunDuration[id] <= 0 then
                    call TimerStart(NewTimerEx(id), 0.05, true, function StunTimed)
                endif
                if i == 1 then
                    call ClearStun(u)
                    set StunDuration[id] = time
                    set StunEffect[id] = AddSpecialEffectTarget(s, u, "overhead")
                elseif i == 2 then
                    set StunDuration[id] = StunDuration[id] + time
                else
                    if StunDuration[id] < time then
                        call ClearStun(u)
                        set StunDuration[id] = time
                        set StunEffect[id] = AddSpecialEffectTarget(s, u, "overhead")
                    endif
                endif
            endif
        else
            call ClearStun(u)
            set StunEffect[id] = AddSpecialEffectTarget(s, u, "overhead")
            call SetUnitPosition(Dummy, GetUnitX(u), GetUnitY(u))
            call IssueTargetOrder(Dummy, "thunderbolt", u)
            set StunnedUnit[id] = u
            set StunDuration[id] = r
            set IsUnitAffectedByUlt[id] = true
            call TimerStart(NewTimerEx(id), 0.05, true, function StunTimed)
        endif
    endfunction
    
    private function Init takes nothing returns nothing
        call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
        set Dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'Ustd', 0, 0, 0.00)
    endfunction
    
endlibrary
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
I made a quick look since you're asking for feedback.
  • If you're gonna submit it, there's a high chance it won't get approved because you're using storm bolt (non-instant because of projectile) and it lacks documentation.
  • You should probably mention you need a Unit Indexer to use this.
  • function IsUnitStunned should directly return.
  • AddStunReductionTimed will become a trigger evaluation because of how your functions are ordered.
  • Use SetUnitX/Y
  • Use a linked list instead? Just a personal preference compared to dynamic indexing.
That's all I found by skimming your code (took less than a minute) so I probably missed more
 
Level 18
Joined
Nov 21, 2012
Messages
835
in private function StunTimed takes nothing returns nothing
I think you have to check for buff on target, not only StunDuration[id] <= 0. When use RemoveAllBuffs - stun should be removed.
Its up to personal preferences but its also possible to add targets to group, enum, pause timer when group is empty, so you need only one timer w/o TimerUtils Btw good idea with Stun_types.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
If you're gonna submit it, there's a high chance it won't get approved because you're using storm bolt (non-instant because of projectile) and it lacks documentation.
I think there are already enough stun systems ;)

AddStunReductionTimed will become a trigger evaluation because of how your functions are ordered.
why? i dont get it :D

I think you have to check for buff on target, not only StunDuration[id] <= 0. When use RemoveAllBuffs - stun should be removed.
yeah, i forgot about that :)


thanks for the help ;)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
First of all I recommend that you avoid magic numbers in your code.
if i != 3 then should preferably if i != STUN_TYPE_ULTIMATE then.
It's easier to read and easier to modify if you want to make an update.

Make the stun duration accurate. You are using one timer per stun,
therefore there is no need to substract 0.05 per timeout.
--> call TimerStart(t, StunDuration, false, function StunExpires)
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
Make the stun duration accurate. You are using one timer per stun,
therefore there is no need to substract 0.05 per timeout.
--> call TimerStart(t, StunDuration, false, function StunExpires)

i am using 1 timer per unit, that's why i made it periodic. to add time to the current stun if the stun type is STUN_TYPE_STACK. or is there another way to do that?
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
Level 21
Joined
Mar 27, 2012
Messages
3,232
Storm Bolt with speed set to 0 is effectively instant. Even if it doesn't apply in the same code line that's an irrelevant problem because if you have an "IsUnitStunned" function then you can make it consider that.
War Stomp on the other hand is always area and I am not aware of a way to make it not be like that on units that have ghost(since they can stack).
 
Status
Not open for further replies.
Top