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

A more efficient Knocback

Level 18
Joined
Oct 18, 2007
Messages
930
Ok i decided to make a small, fast and efficient knockback :3

The reason im doing this is because the one used mostely here in hive is Silvenon's knockback. It needed some optimization and improvements, so i took the idea with the call and started making the code( from scratch ).

The end result was this, enjoy.
JASS:
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                                                                             ||
// ||                                         Knockback System v1.2b                                              ||
// ||                                              By Dynasti                                                     ||
// ||                                                                                                             ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                        Members of the Struct Data                                           ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                                                                             ||
// ||    u    -    The unit that is being draged.                                                                 ||
// ||    v    -    The Velocity the unit is traveling in                                                          ||
// ||    vd   -    The Velocity decreasement                                                                      ||
// ||    cos  -    The Cos value for the angle.                                                                   ||
// ||    sin  -    The Sin value for the angle.                                                                   ||
// ||    r    -    A real value used to kill destructables within a desired range                                 ||
// ||    s    -    A String used to call an effect.                                                               ||
// ||    e    -    Effect that is used for a trail effect.                                                        ||
// ||    i    -    Integer used for counting the effect spawning.                                                 ||
// ||    er   -    Integer used to tell the system when to create the effect, read below to understand more of it.||
// ||    p    -    A boolean used to determine if you want the unit to be paused or not while beign knocked       ||
// ||    nX   -    A Static value used for handling the unit ' u 's X.                                            ||
// ||    nY   -    A Static value used for handling the unit ' u 's Y.                                            ||
// ||    R    -    A Static rect used to kill nearby destructables                                                ||
// ||                                                                                                             ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                               Global Memebers                                               ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                                                                             ||
// ||    Interval(0.035) -    A constant value used for the looping function, lower intervals = Higher FPS.       ||
// ||    EffectRandom(4) -    A constant value used for creating the trail effects. Higher numbers will result in ||
// ||                         higher randomness of effects.(This does only apply if you want a random effectspawn ||
// ||    Index           -    An integer array used for an indexing system.                                       ||
// ||    Total           -    An integer used for an indexing system.                                             ||
// ||    Tim             -    A Timer used for the looping action.                                                ||
// ||    MAX_X           -    A Real used for checking if the knocked unit gets off bounds.                       ||
// ||    MAX_Y           -    A Real used for checking if the knocked unit gets off bounds.                       ||
// ||    MIN_X           -    A Real used for checking if the knocked unit gets off bounds.                       ||
// ||    MIN_Y           -    A Real used for checking if the knocked unit gets off bounds.                       ||
// ||                                                                                                             ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                             Tips'n tricks                                                   ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                                                                             ||
// ||    1. - If you dont want any trees/destructables to be destroyed in it's path then call the radius with 0   ||
// ||                                                                                                             ||
// ||    2. - On the call you need one integer called EffectInt and 2 strings called Effect and EffectAttach, If  ||
// ||         want a looping effect( an effect that is spawned at each interval ) then you do the call with the   ||
// ||         EffectInt being '1' and the Effect being "YourEffect" and dont forget to set the EffectAttach to "".||
// ||         If you want a constant effect that stays on the unit untill the end of the knockback, do the call   ||
// ||         with the EffectInt being '2' and the Effect being "YourEffect" and for the EffectAttach being       ||
// ||         "YourEffectAttach".                                                                                 ||
// ||         If you want both at the same time you call with EffectInt being '3'.                                ||
// ||                                                                                                             ||
// ||    3. - As i have experienced there are times when you need to pause the unit while knocking it and         ||
// ||         -sometimes not. That is why i have added a boolean to the call, called Pause.                       ||
// ||         It is really simple, just set it to false if you dont want the knocked unit to be paused and false  ||
// ||         for not.                                                                                            ||
// ||                                                                                                             ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                 The Call                                                    ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
// ||                                                                                                             ||
// ||    KnockbackEx function:                                                                                    ||
// ||        - unit Unit : The unit being knocked.                                                                ||
// ||        - real Distance : The distance the knoced unit is going to travlel.                                  ||
// ||        - real RadAngle : The angle the knocked unit is being knocked( In Radians ).                         ||
// ||        - real Time : The time it takes to knock the unit.                                                   ||
// ||        - real Radius : The radius to kill nearby destructables( for example trees ) on collision.           ||
// ||        - boolean Pause : If you want the knocked unit to be paused while knocked then call it with true.    ||
// ||        - integer EffectSpawn : This determines if you want the effect spawning to be random or not.         ||
// ||                                Set it to ' 0 ' for random spawning of effect. Else if it is greater than    ||
// ||                                ' 0 ' then the effect will be created every "EffectSpawn" interval.          ||
// ||                                Lets say it is set to ' 3 ', then the effect will be created every 3rd move. ||
// ||        - integer EffectInt : The effect integer( read above for more info ).                                ||
// ||        - string Effect : The path of the effect( read above for more info ).                                ||
// ||        - string EffectAttach : The path for the attaching of the Effect( read above for more info ).        ||
// ||                                                                                                             ||
// ||    Knockback function( Good for less complicated knockbacks):                                               ||
// ||        - unit Unit : The unit being knocked.                                                                ||
// ||        - real Distance : The distance the knoced unit is going to travlel.                                  ||
// ||        - real RadAngle : The angle the knocked unit is being knocked( In Radians ).                         ||
// ||        - real Time : The time it takes to knock the unit.                                                   ||
// ||        - boolean Pause : If you want the knocked unit to be paused while knocked then call it with true.    ||
// ||-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-||
library Knockback initializer Init

    globals
        private constant real Interval = 0.035
        private constant integer EffectRandom = 4 // 4 is 25% chance of the effect spawning( Looping effect )
        
        private integer array Index
        private integer Total = 0
        private timer Tim = CreateTimer()
        
        private real MAX_X
        private real MAX_Y
        private real MIN_X
        private real MIN_Y
        
        private boolexpr FilterDestructables
    endglobals

    private function DestructableFilter takes nothing returns boolean
        local destructable d = GetFilterDestructable()
        if GetWidgetLife(d) > .405 and not IsDestructableInvulnerable(d) then
            call KillDestructable(d)
        endif
        set d = null
        return false
    endfunction

    private keyword Data // If not here then the loop cannot be put over the struct( Why is because the struct needs the function for the timer )

    private function Loop takes nothing returns nothing
        local Data dat
        local integer i=0
        
        loop
            exitwhen i >= Total
            set dat = Index[i]
            
            if dat.v <= 0 then
                if dat.s != "" and dat.s != null then
                    if dat.e != null then
                        call DestroyEffect(dat.e)
                        set dat.e = null
                    endif
                    set dat.s = null
                elseif dat.e != null then
                    call DestroyEffect(dat.e)
                    set dat.e = null
                endif
                
                if dat.p then
                    call PauseUnit(dat.u, false)
                    set dat.p = false
                endif

                set dat.u = null
                
                call dat.destroy()
                
                set Total = Total - 1
                set Index[i] = Index[Total]
                
                set i = i - 1
            else
                set dat.nX = GetUnitX(dat.u) + dat.v * dat.cos
                set dat.nY = GetUnitY(dat.u) + dat.v * dat.sin

                if dat.nX < MAX_X and dat.nY < MAX_Y and dat.nX > MIN_X and dat.nY > MIN_Y and GetWidgetLife(dat.u) > .405 then
                    call SetUnitX(dat.u, dat.nX)
                    call SetUnitY(dat.u, dat.nY)
                    
                    if dat.r > 0 then // If the member ' r ' is greater than 0 it will destroy destructables in a desired range
                        set dat.R = Rect(dat.nX - dat.r, dat.nY - dat.r, dat.nX + dat.r, dat.nY + dat.r)
                        call EnumDestructablesInRect(dat.R, FilterDestructables, null)
                    endif

                    if dat.s != "" and dat.s != null then
                        if dat.er == 0 then
                            if GetRandomInt(1, EffectRandom) == EffectRandom then // Here is where the global random integer is used
                                call DestroyEffect(AddSpecialEffect(dat.s, dat.nX, dat.nY))
                            endif
                        elseif dat.i >= dat.er then
                            call DestroyEffect(AddSpecialEffect(dat.s, dat.nX, dat.nY))
                            set dat.i = 1
                        else
                            set dat.i = dat.i + 1
                        endif
                    endif

                    set dat.v = dat.v - dat.vd
                else
                    set dat.v = 0
                endif
            endif

            set i = i + 1
        endloop
        
        if Total == 0 then
            call PauseTimer(Tim)
        endif
    endfunction

    private struct Data
        unit u

        real v = 0
        real vd

        real cos
        real sin
        
        real r = 0

        string s
        effect e
        
        integer i = 1
        integer er = 0
        
        boolean p

        static real nX
        static real nY
        static rect R = Rect(0,0,0,0)

        static method Start takes unit u, real d, real a, integer t, real r, boolean p, integer er, integer i, string s, string s2 returns nothing
            local Data dat = Data.allocate()

            set dat.u = u
            set dat.er = er

            set dat.v = 2*d/(t+1)
            set dat.vd = dat.v/t

            set dat.cos = Cos(a)
            set dat.sin = Sin(a)
            
            if s != "" and s != null then
                if i == 1 then
                    set dat.s = s
                elseif i == 2 then
                    set dat.e = AddSpecialEffectTarget(s, u, s2)
                elseif i == 3 then
                    set dat.s = s
                    set dat.e = AddSpecialEffectTarget(s, u, s2)
                endif
            endif
            
            if p then
                call PauseUnit(u, true)
                set dat.p = p
            endif

            if r > 0 then
                set dat.r = r
            endif

            if Total == 0 then
                call TimerStart(Tim, Interval, true, function Loop)
            endif
            
            set Index[Total] = dat
            set Total = Total + 1
        endmethod
    endstruct
    
    function KnockbackEx takes unit Unit, real Distance, real RadAngle, real Time, real Radius, boolean Pause, integer EffectSpawn, integer EffectInt, string Effect, string EffectAttach returns nothing
        call Data.Start(Unit, Distance, RadAngle, R2I(Time/Interval), Radius, Pause, EffectSpawn, EffectInt, Effect, EffectAttach)
    endfunction
    
    function Knockback takes unit Unit, real Distance, real RadAngle, real Time, boolean Pause returns nothing
        call Data.Start(Unit, Distance, RadAngle, R2I(Time/Interval), 0, Pause, 0, 0, "", "")
    endfunction
    
    private function Init takes nothing returns nothing
        set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
        set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
        set MIN_X = GetRectMinX(bj_mapInitialPlayableArea)
        set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)

        set FilterDestructables = Condition(function DestructableFilter)
    endfunction
endlibrary


I dont need credits for making this, i did this only for the purpose of having a good knockback sys.
 
Last edited:
Level 11
Joined
Nov 4, 2007
Messages
337
This is bugged!
All knockback systems except mine are bugged!
You loop thorugh the knockbacks and if a knockback is finished, you decrease Total.
But:
You have knockbacks 1-4
knockback 2 finishes.
So knockback 4 stops.
wtf?
Thats just bugged!


Solution that I use:
JASS:
function PushUp_Structs takes integer fromwhere returns nothing
    local integer i = fromwhere
        loop
    set i = i + 1
    exitwhen i > Total
    set Knockbackdata[i-1] = Knockbackdata[i]
        endloop
    set Total = Total - 1
endfunction

So the following first numbers are the knockbacks.
And the number after '=' is the new number of the knockback
--- means knockback ended

1 = 1
2 = ---
3 = 2
4 = 3

1 = 1
2 = ---
3 = 2

1 = 1
2 = ---

1 = ---
 
Level 14
Joined
Nov 18, 2007
Messages
816
no, they are not. Your method works, but is not efficient.

JASS:
//  1   2   3   4   5   // initial arrangement
//  -   2   3   4   5   // knockback 1 ends
//  5   2   3   4   -   // move knockback 5 to knockback 1's place and decrease the total number of knockbacks running
//  5   -   3   4   -   // knockback 2 ends
//  5   4   3   -   -   // repeat
//  5   4   -   -   -   // knockback 3 ends
THINK before you claim something is bugged. This is actually the best way to do it.
 
Last edited:
Level 18
Joined
Oct 18, 2007
Messages
930
no, they are not. Your method works, but is not efficient.

JASS:
//  1   2   3   4   5   // initial arrangement
//  -   2   3   4   5   // knockback 1 ends
//  5   2   3   4   -   // move knockback 5 to knockback 1's place and decrease the total number of knockbacks running
//  5   -   3   4   -   // knockback 2 ends
//  5   4   3   -   -   // repeat
//  5   4   -   -   -   // knockback 3 ends
THINK before you claim something is bugged. This is actually the best way to do it.

Thx for explaining it for him ;)
 
Level 14
Joined
Nov 18, 2007
Messages
816
strings always leak, because theyre saved somewhere in a table. Reusing a string wont leak again, but lots of different strings will cause a bit more memory usage. You cant stop strings from leaking.

And using the return bug on strings will return their table index.

I wouldnt worry about strings. You cant do anything against it and the leak is very small (iirc 4 byte for each unique string (i think there might be collisions as well, in which case they dont leak)).
 
Last edited:
Level 8
Joined
Aug 6, 2008
Messages
451
Ye, string leak is not so bad. Only things that generate lots of strings are I2S(H2I(something)) and maybe some save&load codes, and it still doesnt hurt.

Anyways, I think this system needs somekind of pathability check function. Maybe a unit with 0 duration windwalk or some item.

edit. Oh yeah. And you should prevent units from sliding to deep water too.

Anyways, It looks like a decent knockback system sketch thingie. Keep working with it.
 
Top