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

KnockBack

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
You could say "oh come on, a lot of people have created KnockBack already", but there are several things which separate this one from others.
* Nearly fully made in GUI (except RemoveLocation and Set\GetUnitX\Y, which are not avaible in standart war3 GUI)
* MUI
* Leak-free
* Easy to edit or configure to be avaible for multiple unittypes with different parameters
* Just 3 triggers to copy (easy ha?)
* Does not reset current unit order. Yes, that's a dream for zombie shooter.
I've seen a couple of topics featuring different methods of knockback, which were made... not really good. And in addition each of them did reset unit's current action. So I've decided to make one, that will match my own needs perfectly.

Approach to storing units: First thing I've thought of was to create a trigger that will move individual unit and then self-destruct, but then I decided to stick to something simple as GUI.
So, this system uses 'dynamic' arrays of global variables. Each array stores one of parameters (which are Unit, Angle, Remaining steps, and Knockback power). Array size grows when multiple units are knocked back at the same time (array size will grow to 100 only if you have 100 units being moved by KnockBack at the same time). Once the unit is moved (and array element is not needed anymore), it is being marked as inactive. A new unit will take place of one of inactive array elements, or increase total array size by one and write into the new element.

Approach to moving units: Each tick (0.01 seconds) system cycles through the array in search for active units, and moves them.
Since none of accessible unit movement commands preserve current unit order, I've used SetUnitX & SetUnitY to move unit. Once the unit was moved the specified number of times, array element status is set to inactive, and system will not do any calculations until a new unit takes it's place.

Usage: to add a different knockback (for other unittype), just copy KnockBack trigger, and change variables. Everything is well commented.

Overall, I expect this to turn out as a usefull resource for many users.
Description will be edited later to look more like English.

Code:
Variables (name\type)
zzBoolean - Boolean
zzReal - Real
zzX - Real
zzY - Real
zzUnit - Unit
knockCount - Integer
knockZi - Boolean Array
knockAngle - Real Array
knockPower - Real Array
knockUnit - Unit Array
knockTodo - Integer Array
knockShould - Integer Array
JASS:
//For all ye JASS' lovers.
function Trig_KnockBackJadd takes real kAng, real kPow, unit kUni, integer kStep returns nothing
    local integer i = 1
    local integer j = 0
    set udg_zzBoolean = false
    loop
        exitwhen i > udg_knockCount
        if not udg_knockZi[i] then
            set udg_zzBoolean = true
            set j = i
            set i = udg_knockCount + 1
        endif
        set i = i + 1
    endloop
    if not udg_zzBoolean then
        set udg_knockCount = udg_knockCount + 1
        set j = udg_knockCount
    endif
    set udg_knockAngle[j] = kAng
    set udg_knockPower[j] = kPow
    set udg_knockUnit[j] = kUni
    set udg_knockTodo[j] = kStep
    set udg_knockShould[j] = kStep
    set udg_knockZi[j] = true
    call EnableTrigger( gg_trg_KnockBackMainJ )
endfunction

function Trig_KnockBackJ_Conditions takes nothing returns boolean
    return GetUnitTypeId(GetAttacker()) == 'hrif'
endfunction

function Trig_KnockBackJ_Actions takes nothing returns nothing
    // Knockback angle:
    set udg_zzReal = Atan2(GetUnitY(GetAttackedUnitBJ())-GetUnitY(GetAttacker()),GetUnitX(GetAttackedUnitBJ())-GetUnitX(GetAttacker()))
    call Trig_KnockBackJadd(udg_zzReal,15,GetAttackedUnitBJ(),12)
endfunction

//===========================================================================
function InitTrig_KnockBackJ takes nothing returns nothing
    set gg_trg_KnockBackJ = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_KnockBackJ, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_KnockBackJ, Condition( function Trig_KnockBackJ_Conditions ) )
    call TriggerAddAction( gg_trg_KnockBackJ, function Trig_KnockBackJ_Actions )
endfunction
JASS:
function Trig_KnockBackMainJ_Actions takes nothing returns nothing
    local integer i = 1
    loop
        exitwhen i > udg_knockCount
        if udg_knockZi[i] then
            set udg_zzUnit = udg_knockUnit[i]
            set udg_zzReal = I2R(udg_knockTodo[i]) / udg_knockShould[i]
            set udg_zzReal = (udg_zzReal * udg_zzReal) * udg_knockPower[i]
            set udg_zzX = GetUnitX(udg_zzUnit) + Cos(udg_knockAngle[i]) * udg_zzReal
            set udg_zzY = GetUnitY(udg_zzUnit) + Sin(udg_knockAngle[i]) * udg_zzReal
            if not IsTerrainPathable(udg_zzX, udg_zzY, PATHING_TYPE_WALKABILITY) then
                call SetUnitX(udg_zzUnit,udg_zzX)
                call SetUnitY(udg_zzUnit,udg_zzY)
            endif
            set udg_knockTodo[i] = udg_knockTodo[i] - 1
            if udg_knockTodo[i] <= 0 then
                set udg_knockZi[i] = false
                if i == udg_knockCount then
                    set udg_knockCount = udg_knockCount - 1
                endif
            endif
        elseif i == udg_knockCount then
            set udg_knockCount = udg_knockCount - 1
            if udg_knockCount <= 0 then
                call DisableTrigger(GetTriggeringTrigger())
            endif
        endif
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_KnockBackMainJ takes nothing returns nothing
    set gg_trg_KnockBackMainJ = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_KnockBackMainJ, 0.03 )
    call TriggerAddAction( gg_trg_KnockBackMainJ, function Trig_KnockBackMainJ_Actions )
endfunction
Edit 1: Fixed map description and one plain mistake
Edit 2: Improved physics and included a pure JASS version of spell
Edit 3: Added JASS version into code block in the post.
Edit 4: Added preview (youtube video)
C&C are welcome.

Keywords:
knockback, knock, back, awesome, mui, spell, easy, to, edit
Contents

KnockBack (Map)

Reviews
12th Dec 2015 IcemanBo: Too long time as NeedsFix. Rejected. 21:38, 15th Oct 2010 The_Reborn_Devil: We already have many knockback systems and this doesn't add anything new, in fact it got less functionality than many others. Your JASS...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long time as NeedsFix. Rejected.

21:38, 15th Oct 2010
The_Reborn_Devil:

We already have many knockback systems and this doesn't add anything new, in fact it got less functionality than many others. Your JASS version could also be optimized a lot. If you want to have a JASS version you should at least have a JASS version of the KnockBackMain trigger as well.


Status: Rejected until updated
Rating: N/A

PM me or another mod once you've updated this with more functionality and a better and more optimized JASS version (or no JASS version at all). Have a nice day!
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
You need collision detection, units go through other units, trees, cliffs, out of playable map area and so on.

The loop period could be 0.03 instead of 0.01.

The loop should be turned off when no units are being knocked back.

You never reduce the knockCount so the loop runs from 1 to 3 for example even though the is only one unit being knocked back.

You could make the knocback speed be reduced with each loop, to simulate friction.
 
Updated.
Maker, added collision checking via IsTerrainPathable.
Added "square" speed curve.
Set loop period to 0.03.
KnockCount is automatically reduced and loop is disabled if KnockCount == 0.
Anachron, added a JASS version, which consists of 2 triggers. I hope you're pleased. (formatting guidelines may be not preserved because triggers were recoded from scratch through normal WE)
Mainy, look once more.
 
looking...

Review: Interesting and simple, nice lightweight system/spell/thing

The GUI code is well considering it uses an index system rather than hashtables.
I love that doesn't reset the unit order:)
Overall it's nice but i have a question:
JASS:
if not IsTerrainPathable(udg_zzX, udg_zzY, PATHING_TYPE_WALKABILITY) then
Why is that in JASS in the gui version? Can you not do that in GUI?
 
Thanks, Zeatherann.
Common.j function was preffered over it's GUI version because then I'd need to add two more lines, one for creating and assigning a new location, and one for removing it. And recycling a lot of locations 33 times per second does not look like effective way.
Mainy, done. JASS version and variable notes are now avaible in the description.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
This aint JASS, this is converted GUI, even if you make them with user defined globals you can still merge the triggers.

Remove the GetAttackedUnitBJ as it returns GetTriggerUnit() which you should use instead.

Anachron didn't tell you to make it in JASS, he just pointed that the JASS is better although harder for GUIers if you don't know how to call a function.

Instead in your indexing for disabling/enabling a trigger, let it run on a normal user defined global timer which you start when there are instances and pause if there are none.

JASS:
function Trig_KnockBackJ_Conditions takes nothing returns boolean
    if ( not ( GetUnitTypeId(GetAttacker()) == 'hrif' ) ) then
        return false
    endif
    return true
endfunction

function Trig_KnockBackJ_Actions takes nothing returns nothing
    // Knockback angle:
    set udg_zzReal = Atan2(GetUnitY(GetAttackedUnitBJ())-GetUnitY(GetAttacker()),GetUnitX(GetAttackedUnitBJ())-GetUnitX(GetAttacker()))
    call Trig_KnockBackJadd(udg_zzReal,15,GetAttackedUnitBJ(),12)
endfunction

//===========================================================================
function InitTrig_KnockBackJ takes nothing returns nothing
    set gg_trg_KnockBackJ = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_KnockBackJ, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_KnockBackJ, Condition( function Trig_KnockBackJ_Conditions ) )
    call TriggerAddAction( gg_trg_KnockBackJ, function Trig_KnockBackJ_Actions )
endfunction

This above is just a waste, use a seperate trigger to show us the example.

I think the GUI version is alright, meh just because you use SetUnitPos (that's why the orders get interrupted).

Good job but the JASS version is a little meh :p
 
This aint JASS, this is converted GUI, even if you make them with user defined globals you can still merge the triggers.

Remove the GetAttackedUnitBJ as it returns GetTriggerUnit() which you should use instead.

Anachron didn't tell you to make it in JASS, he just pointed that the JASS is better although harder for GUIers if you don't know how to call a function.

Instead in your indexing for disabling/enabling a trigger, let it run on a normal user defined global timer which you start when there are instances and pause if there are none.

JASS:
function Trig_KnockBackJ_Conditions takes nothing returns boolean
    if ( not ( GetUnitTypeId(GetAttacker()) == 'hrif' ) ) then
        return false
    endif
    return true
endfunction

function Trig_KnockBackJ_Actions takes nothing returns nothing
    // Knockback angle:
    set udg_zzReal = Atan2(GetUnitY(GetAttackedUnitBJ())-GetUnitY(GetAttacker()),GetUnitX(GetAttackedUnitBJ())-GetUnitX(GetAttacker()))
    call Trig_KnockBackJadd(udg_zzReal,15,GetAttackedUnitBJ(),12)
endfunction

//===========================================================================
function InitTrig_KnockBackJ takes nothing returns nothing
    set gg_trg_KnockBackJ = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_KnockBackJ, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_KnockBackJ, Condition( function Trig_KnockBackJ_Conditions ) )
    call TriggerAddAction( gg_trg_KnockBackJ, function Trig_KnockBackJ_Actions )
endfunction

This above is just a waste, use a seperate trigger to show us the example.

I think the GUI version is alright, meh just because you use SetUnitPos (that's why the orders get interrupted).

Good job but the JASS version is a little meh :p
Well, you're right somewhere. Blizzard-based functions are not a thing to leave in this case.
JASS:
function Trig_KnockBackJ_Conditions takes nothing returns boolean
    return GetUnitTypeId(GetAttacker()) == 'hrif'
endfunction
^ That's how a condition should look like. Edited code in the description.
Preview for ones that dont have time to download this just to see if it fits their need: http://www.youtube.com/watch?v=uoaFFcTmhm0
(or check the end of description for embed video)
 
Yea, didn't really screen the JASS version because this is ment to be a GUI knockback.

I have one suggestion: use x and y along with a vx and vy so you can 'add' kockbacks a bit better, just a thought. You have it so the knockbacks are added, but each time it's a new entry into the index, with vx and vy as reals you wouldn't need to add to the index.

Will say for GUI this is pretty awesome, hands down. 4/5 +votes for approval
 
Top