GUI Knockback 2.5D v4.2.5.0

Knockback 2D will knock a unit back - without interrupting orders - while also considering pathing checks for different collision sizes. It destroys trees and/or debris without destroying bridges and the user can fully configure it. It allows for deep-water movement if desired.

You specify the speed of the knockback indirectly by giving it a distance to travel and a time to travel it within. This uses the same formula as Berb's Knockback Lite.

If the unit hits impassible terrain, it will "bounce" in a form of a geometric deflection angle. This can be toggled on or off per knockback.

The demo map features a template where you can add on-loop behavior. To the knockback.

It is in GUI, meaning it is World Editor friendly. I hope that it is also easy to understand how it works as the variables are pretty straightforward:
- A unit to knock back (Knockback2DUnit), an angle (in degrees) to knock it back (Knockback2DAngle), a distance it will travel (Knockback2DDistance) and a length of time to complete the travel within (Knockback2DTime).


  1. Copy the Unit Indexer and Knockback 2D triggers into your map.
  2. There is one dummy created in the Config trigger that is a simple Undead Ghoul. It could just as well be a Peasant or Peon. Just make sure that the unit type created has the harvest ability and doesn't have some passive effects like Phoenix fire or immolation.


  • Knockback 2D Config
    • Events
      • Game - UnitIndexEvent becomes Equal to 3.00
    • Conditions
    • Actions
      • Set CenterPoint = (Point(K2DX, K2DY))
      • Set UnitIndexerEnabled = False
      • -------- --------
      • -------- Configure things below --------
      • -------- --------
      • -------- Set the timeout to be used throughout the system --------
      • -------- --------
      • Set K2DTimeout = (1.00 / 60.00)
      • -------- --------
      • -------- Robust Pathing at 0 is only safe for collision sizes 16 and lower, but requires only one SetItemPosition check per timeout. --------
      • -------- -------- 1 checks collision vertically and horizontally to momentum. Uses an extra 4 SetItemPosition checks pet timeout. --------
      • -------- -------- 2 checks collision diagonally with momentum. Uses a total of 9 SetItemPosition checks per timeout. --------
      • -------- In any case, if the unit only has size 16 or lower collision, only one SetItemPosition check will be used for it. --------
      • -------- If RobustPathing is set to 2 and the unit has 36 or less collision, it will only use the normal check of 5 SetItemPosition calls --------
      • -------- The only reason to use robustness above 1 is for visual continuity - it features potentially-less glitchy movement. --------
      • -------- --------
      • Set Knockback2DRobustPathing = 2
      • -------- --------
      • -------- Keep the friction between 0.00 and 1.00, At 0.00, friction keeps the unit at the same speed for the knockback --------
      • -------- 1.00 friction will be an evenly-distributed deceleration which sees the unit slow to a complete stop --------
      • -------- Friction outside of these bounds gives the knockback a boomerang-effect, so you are welcome to experiment. --------
      • -------- --------
      • Set Knockback2DDefaultFriction = 1.00
      • Set Knockback2DFriction = Knockback2DDefaultFriction
      • -------- --------
      • -------- Determine the default bouncing behavior of units. You can set this before knocking a unit back. --------
      • -------- --------
      • Set Knockback2DDefaultBounce = True
      • Set Knockback2DBounces = Knockback2DDefaultBounce
      • -------- --------
      • -------- Determine the default mechanics of whether a unit should be unable to move while knocked back --------
      • -------- --------
      • Set Knockback2DDefaultPause = False
      • Set Knockback2DPause = Knockback2DDefaultPause
      • -------- --------
      • -------- Determine if surrounding trees should be killed by default or not --------
      • -------- --------
      • Set Knockback2DDefaultKillTrees = True
      • Set Knockback2DKillTrees = Knockback2DDefaultKillTrees
      • -------- --------
      • -------- If so, how wide should the radius be? 128.00 should be the minimum if you use pathing robustness greater than 0. --------
      • -------- The minimum should be 64 if you use a robustness of 0. --------
      • -------- --------
      • Set Knockback2DDefaultDestRadius = 128.00
      • Set Knockback2DDestRadius = Knockback2DDefaultDestRadius
      • -------- --------
      • -------- The "attack" option below will destroy any valid debris, from trees to barrels to creep homes. --------
      • -------- If you just want to destroy trees, change the string to: harvest --------
      • -------- --------
      • Set Knockback2DTreeOrDebris = attack
      • -------- --------
      • -------- 0.50 gravity will have equal ascend and decline rate, 1.00 is instant descend, 0.67 is twice as fast, 0.75 is three times as fast. --------
      • -------- --------
      • Set Knockback2DDefaultGravity = 0.71
      • Set Knockback2DGravity = Knockback2DDefaultGravity
      • -------- --------
      • -------- Change the following to the default type of looping FX you want to have if you use Knockback Effects --------
      • -------- --------
      • Set Knockback2DDefaultFX = Abilities\Weapons\AncientProtectorMissile\AncientProtectorMissile.mdl
      • Set Knockback2DLoopFX = Knockback2DDefaultFX
      • -------- --------
      • -------- How frequently should the effects appear per unit? This can also be customized per-knockback --------
      • -------- --------
      • Set Knockback2DDefaultFXRate = 0.10
      • Set Knockback2DFXRate = Knockback2DDefaultFXRate
      • -------- --------
      • -------- Create an item to help verify pathing throughout the game --------
      • -------- --------
      • Item - Create Slippers of Agility +3 at CenterPoint
      • Set K2DItem = (Last created item)
      • -------- --------
      • -------- Create a harvest-capable unit to check if debris can be killed --------
      • -------- --------
      • Unit - Create 1 Ghoul for Neutral Passive at CenterPoint facing 0.00 degrees
      • Set K2DDebrisKiller = (Last created unit)
      • -------- --------
      • -------- End Configuration --------
      • -------- --------
      • Set UnitIndexerEnabled = True
      • Custom script: call RemoveLocation(udg_CenterPoint)
      • Game - Preload Knockback2DDefaultFX
      • Item - Hide K2DItem
      • Unit - Hide K2DDebrisKiller
      • Unit - Pause K2DDebrisKiller
      • Custom script: call UnitAddAbility(udg_K2DDebrisKiller, 'Aloc')
      • Set Radians_Turn = (Radians(360.00))
      • Set Radians_QuarterTurn = (Radians(90.00))
      • Set Radians_QuarterPi = (Radians(45.00))
      • Set K2DRegion = (Entire map)
      • Set K2DMaxX = (Max X of K2DRegion)
      • Set K2DMaxY = (Max Y of K2DRegion)
      • Set K2DMinX = (Min X of K2DRegion)
      • Set K2DMinY = (Min Y of K2DRegion)
      • Set K2DMaxDestRadius = (Knockback2DDefaultDestRadius x 2.00)
      • Custom script: call SetRect(udg_K2DRegion, 0.00, 0.00, udg_K2DMaxDestRadius, udg_K2DMaxDestRadius)
      • Set K2DItemsFound = False
      • Set K2DItemOffset = False
  • Knockback 2D
    • Events
    • Conditions
      • (Default movement speed of Knockback2DUnit) Not equal to 0.00
      • K2DOverride[(Custom value of Knockback2DUnit)] Equal to False
    • Actions
      • Custom script: local integer pdex = udg_UDex
      • Set UDex = (Custom value of Knockback2DUnit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • IsUnitBeingKnockedBack[UDex] Equal to True
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Knockback2DOverride Equal to False
            • Then - Actions
              • Set K2DAngle[UDex] = (Degrees(K2DAngle[UDex]))
              • Set Knockback2DAngle = ((Knockback2DAngle + K2DAngle[UDex]) x 0.50)
              • Set Knockback2DDistance = ((K2DDistanceLeft[UDex] + Knockback2DDistance) x 0.50)
              • Set Knockback2DTime = ((K2DTimeLeft[UDex] + Knockback2DTime) x 0.50)
            • Else - Actions
          • Trigger - Run Knockback 2D Destroy <gen> (ignoring conditions)
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • K2DNext[0] Equal to 0
        • Then - Actions
          • Custom script: call ExecuteFunc("StartKnockback2DTimer")
        • Else - Actions
      • Set IsUnitBeingKnockedBack[UDex] = True
      • Set K2DPrev[K2DNext[0]] = UDex
      • Set K2DNext[UDex] = K2DNext[0]
      • Set K2DNext[0] = UDex
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Knockback2DHeight Not equal to 0.00
        • Then - Actions
          • Set K2DHeight[UDex] = Knockback2DHeight
          • Custom script: if UnitAddAbility(udg_Knockback2DUnit, 'Amrf') then
          • Custom script: call UnitRemoveAbility(udg_Knockback2DUnit, 'Amrf')
          • Custom script: endif
          • Animation - Change Knockback2DUnit flying height to ((Default flying height of Knockback2DUnit) + Knockback2DHeight) at ((Knockback2DHeight - (Default flying height of Knockback2DUnit)) / (Knockback2DGravity x Knockback2DTime))
          • Set K2DHeightThreshold[UDex] = ((1.00 - Knockback2DGravity) x Knockback2DTime)
          • Set Knockback2DHeight = 0.00
        • Else - Actions
          • Set K2DHeight[UDex] = 0.00
      • Custom script: set udg_K2DX = GetUnitX(udg_Knockback2DUnit)
      • Custom script: set udg_K2DY = GetUnitY(udg_Knockback2DUnit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Knockback2DPause Equal to True
        • Then - Actions
          • Set K2DLastX[UDex] = K2DX
          • Set K2DLastY[UDex] = K2DY
        • Else - Actions
      • Set K2DAngle[UDex] = (Radians(Knockback2DAngle))
      • Set K2DCos[UDex] = (Cos(Knockback2DAngle))
      • Set K2DSin[UDex] = (Sin(Knockback2DAngle))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Knockback2DRobustPathing Greater than 0
        • Then - Actions
          • -------- --------
          • -------- Handle the pathing checker based on the unit's collision size --------
          • -------- --------
          • Custom script: if not IsUnitInRangeXY(udg_Knockback2DUnit, udg_K2DX + 17, udg_K2DY, 0) then
          • Set K2DRadius[UDex] = 0
          • Custom script: else
          • Custom script: if not IsUnitInRangeXY(udg_Knockback2DUnit, udg_K2DX + 25, udg_K2DY, 0) then
          • Set K2DRadius[UDex] = 8
          • Custom script: elseif not IsUnitInRangeXY(udg_Knockback2DUnit, udg_K2DX + 33, udg_K2DY, 0) then
          • Set K2DRadius[UDex] = 16
          • Custom script: elseif not IsUnitInRangeXY(udg_Knockback2DUnit, udg_K2DX + 49, udg_K2DY, 0) then
          • Set K2DRadius[UDex] = 32
          • Custom script: else
          • Set K2DRadius[UDex] = 48
          • Custom script: endif
          • Set Knockback2DAngle = ((Knockback2DAngle + 90.00) mod 360.00)
          • Set K2DCosH[UDex] = (Cos(Knockback2DAngle))
          • Set K2DSinH[UDex] = (Sin(Knockback2DAngle))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Knockback2DRobustPathing Equal to 2
              • K2DRadius[UDex] Greater than 16
            • Then - Actions
              • Set Knockback2DAngle = ((Knockback2DAngle + 45.00) mod 360.00)
              • Set K2DCosD1[UDex] = (Cos(Knockback2DAngle))
              • Set K2DSinD1[UDex] = (Sin(Knockback2DAngle))
              • Set Knockback2DAngle = ((Knockback2DAngle + 90.00) mod 360.00)
              • Set K2DCosD2[UDex] = (Cos(Knockback2DAngle))
              • Set K2DSinD2[UDex] = (Sin(Knockback2DAngle))
            • Else - Actions
          • Custom script: endif
        • Else - Actions
      • Set K2DDistanceLeft[UDex] = Knockback2DDistance
      • Set Knockback2DDistance = (((1.00 + Knockback2DFriction) x Knockback2DDistance) / Knockback2DTime)
      • Set K2DFriction[UDex] = ((Knockback2DDistance / Knockback2DTime) x ((1.00 - (1.00 - Knockback2DFriction)) x (K2DTimeout x K2DTimeout)))
      • Set K2DVelocity[UDex] = (Knockback2DDistance x K2DTimeout)
      • -------- --------
      • Set K2DKillTrees[UDex] = Knockback2DKillTrees
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Knockback2DKillTrees Equal to True
        • Then - Actions
          • -------- Square the radius so we don't have to use SquareRoot when comparing distance. --------
          • Set K2DDestRadius[UDex] = (Knockback2DDestRadius x Knockback2DDestRadius)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Knockback2DDestRadius x 2.00) Greater than K2DMaxDestRadius
            • Then - Actions
              • -------- Update the size of the enumerating rect to compensate for the desired extra radius. --------
              • Set K2DMaxDestRadius = (Knockback2DDestRadius x 2.00)
              • Custom script: call SetRect(udg_K2DRegion, 0.00, 0.00, udg_K2DMaxDestRadius, udg_K2DMaxDestRadius)
            • Else - Actions
          • Set Knockback2DDestRadius = Knockback2DDefaultDestRadius
        • Else - Actions
      • -------- --------
      • Set K2DAmphibious[UDex] = Knockback2DAmphibious
      • Set K2DBounce[UDex] = Knockback2DBounces
      • Set K2DCollision[UDex] = Knockback2DCollision
      • Set K2DFreeze[UDex] = False
      • Set K2DFXModel[UDex] = Knockback2DLoopFX
      • Set K2DFXRate[UDex] = Knockback2DFXRate
      • Set K2DFXTimeLeft[UDex] = Knockback2DFXRate
      • Set K2DImpact[UDex] = Knockback2DOnImpact
      • Set K2DOverride[UDex] = Knockback2DOverride
      • Set K2DPause[UDex] = Knockback2DPause
      • Set K2DSimple[UDex] = Knockback2DSimple
      • Set K2DSource[UDex] = Knockback2DSource
      • Set K2DTimeLeft[UDex] = Knockback2DTime
      • Set K2DUnbiasedCollision[UDex] = Knockback2DUnbiasedCollision
      • Set Knockback2DAmphibious = False
      • Set Knockback2DBounces = Knockback2DDefaultBounce
      • Set Knockback2DCollision = 0.00
      • Set Knockback2DFriction = Knockback2DDefaultFriction
      • Set Knockback2DFXRate = Knockback2DDefaultFXRate
      • Set Knockback2DGravity = Knockback2DDefaultGravity
      • Set Knockback2DKillTrees = Knockback2DDefaultKillTrees
      • Set Knockback2DLoopFX = Knockback2DDefaultFX
      • Custom script: set udg_Knockback2DOnImpact = null
      • Set Knockback2DOverride = False
      • Set Knockback2DPause = Knockback2DDefaultPause
      • Set Knockback2DSimple = False
      • Set Knockback2DSource = No unit
      • Set Knockback2DUnbiasedCollision = False
      • Custom script: set udg_UDex = pdex
JASS:
function K2DItemCheckXY takes real x, real y returns boolean
    call SetItemPosition(udg_K2DItem, x, y)
    return RAbsBJ(GetWidgetX(udg_K2DItem) - x +GetWidgetY(udg_K2DItem) - y) < 1
endfunction

function K2DItemCheckAxis takes real x, real y returns boolean
    local real x2 = x*udg_K2DRadius[udg_UDex]
    local real y2 = y*udg_K2DRadius[udg_UDex]
    set x = udg_K2DX + x2
    set y = udg_K2DY + y2
    if K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
        set x = udg_K2DX - x2
        set y = udg_K2DY - y2
        return K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
    endif
    return false
endfunction

function K2DItemCheck takes nothing returns boolean
    local boolean result = K2DItemCheckXY(udg_K2DX, udg_K2DY)
 
    //Only perform additional pathing checks if the unit has a larger collision.
    if result and udg_Knockback2DRobustPathing > 0 and udg_K2DRadius[udg_UDex] > 0 then

        //Check horizontal axis of unit to make sure nothing is going to collide
        set result = K2DItemCheckAxis(udg_K2DCosH[udg_UDex], udg_K2DSinH[udg_UDex])
     
        //Check vertical axis of unit to ensure nothing will collide
        set result = result and K2DItemCheckAxis(udg_K2DCos[udg_UDex], udg_K2DSin[udg_UDex])
     
        if result and udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then

            //Check diagonal axis of unit if more thorough pathing is desired
            set result = K2DItemCheckAxis(udg_K2DCosD1[udg_UDex], udg_K2DSinD1[udg_UDex])
            set result = result and K2DItemCheckAxis(udg_K2DCosD2[udg_UDex], udg_K2DSinD2[udg_UDex])
        endif
    endif
 
    //Reset item so it won't interfere with the map
    call SetItemPosition(udg_K2DItem, udg_K2DMaxX, udg_K2DMaxY)
    call SetItemVisible(udg_K2DItem, false)
 
    return result
endfunction

function K2DItemFilter takes nothing returns boolean
    //Check for visible items, temporarily hide them and add them to the filter.
    if IsItemVisible(GetFilterItem()) then
        call SetItemVisible(GetFilterItem(), false)
        return true
    endif
    return false
endfunction
function K2DItemCode takes nothing returns nothing
    //Perform the item-pathing check only once, then unhide those filtered items
    if not udg_K2DItemsFound then
        set udg_K2DItemsFound = true
        set udg_K2DItemOffset = K2DItemCheck()
    endif
    call SetItemVisible(GetEnumItem(), true)
endfunction

function K2DKillDest takes nothing returns nothing
    local real x
    local real y
    //Handle destruction of debris
    set bj_destRandomCurrentPick = GetEnumDestructable()
    if GetWidgetLife(bj_destRandomCurrentPick) > 0.405 and IssueTargetOrder(udg_K2DDebrisKiller, udg_Knockback2DTreeOrDebris, bj_destRandomCurrentPick) then
        set x = GetWidgetX(bj_destRandomCurrentPick) - udg_K2DX
        set y = GetWidgetY(bj_destRandomCurrentPick) - udg_K2DY
        if x*x + y*y <= udg_K2DDestRadius[udg_UDex] then
            call KillDestructable(bj_destRandomCurrentPick)
        endif
    endif
endfunction

function K2DEnumDests takes nothing returns nothing
    call MoveRectTo(udg_K2DRegion, udg_K2DX, udg_K2DY)
    if udg_K2DKillTrees[udg_UDex] then
        call SetUnitX(udg_K2DDebrisKiller, udg_K2DX)
        call SetUnitY(udg_K2DDebrisKiller, udg_K2DY)
        call EnumDestructablesInRect(udg_K2DRegion, null, function K2DKillDest)
    endif
endfunction

function Knockback2DCheckXY takes real x, real y returns boolean
    set udg_K2DX = x + udg_K2DVelocity[udg_UDex]*udg_K2DCos[udg_UDex]
    set udg_K2DY = y + udg_K2DVelocity[udg_UDex]*udg_K2DSin[udg_UDex]
    if udg_K2DSimple[udg_UDex] then
        //A "pull" effect or a missile system does not require complex pathing.
        if udg_K2DX <= udg_K2DMaxX and udg_K2DX >= udg_K2DMinX and udg_K2DY <= udg_K2DMaxY and udg_K2DY >= udg_K2DMinY then
            call K2DEnumDests()
            return true
        endif
        return false
    elseif udg_K2DFlying[udg_UDex] then
        return not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLYABILITY)
    elseif not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_WALKABILITY) then
        call K2DEnumDests()
        set udg_K2DItemOffset = false
        call EnumItemsInRect(udg_K2DRegion, Filter(function K2DItemFilter), function K2DItemCode)
        if udg_K2DItemsFound then
            //If items were found, the check was already performed.
            set udg_K2DItemsFound = false
        else
            //Otherwise, perform the check right now.
            set udg_K2DItemOffset = K2DItemCheck()
        endif
        return udg_K2DItemOffset
    endif
    return udg_K2DAmphibious[udg_UDex] and not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLOATABILITY)
endfunction

function Knockback2DApplyAngle takes real angle returns nothing
    set angle = ModuloReal(angle, udg_Radians_Turn)
    set udg_K2DCos[udg_UDex] = Cos(angle)
    set udg_K2DSin[udg_UDex] = Sin(angle)
    set udg_K2DAngle[udg_UDex] = angle
    if udg_Knockback2DRobustPathing > 0 then
        set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
        set udg_K2DCosH[udg_UDex] = Cos(angle)
        set udg_K2DSinH[udg_UDex] = Sin(angle)
        if udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
            set angle = ModuloReal(angle + udg_Radians_QuarterPi, udg_Radians_Turn)
            set udg_K2DCosD1[udg_UDex] = Cos(angle)
            set udg_K2DSinD1[udg_UDex] = Sin(angle)
            set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
            set udg_K2DCosD2[udg_UDex] = Cos(angle)
            set udg_K2DSinD2[udg_UDex] = Sin(angle)
        endif
    endif
endfunction

function Knockback2DLooper takes nothing returns nothing
    local integer i = 0
    local unit u
    local real x
    local real y
 
    call PauseUnit(udg_K2DDebrisKiller, false)
 
    loop
        set i = udg_K2DNext[i]
        exitwhen i == 0
        set udg_UDex = i
        set udg_K2DTimeLeft[i] = udg_K2DTimeLeft[i] - udg_K2DTimeout
        set udg_K2DDistanceLeft[i] = udg_K2DDistanceLeft[i] - udg_K2DVelocity[i]
        set u = udg_UDexUnits[i]
     
        if udg_K2DTimeLeft[i] > 0.00 then
            if udg_K2DTimeLeft[i] < udg_K2DHeightThreshold[i] and udg_K2DHeightThreshold[i] != 0.00 then
                call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)/udg_K2DHeightThreshold[i])
                set udg_K2DHeightThreshold[i] = 0.00
            endif
            if udg_K2DPause[i] then
                set x = udg_K2DLastX[i]
                set y = udg_K2DLastY[i]
            else
                set x = GetUnitX(u)
                set y = GetUnitY(u)
            endif
         
            if not Knockback2DCheckXY(x, y) then
                if not udg_K2DFreeze[i] and IsTriggerEnabled(udg_K2DImpact[i]) and TriggerEvaluate(udg_K2DImpact[i]) then
                    call TriggerExecute(udg_K2DImpact[i])
                endif
                if udg_K2DBounce[i] then
                    call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
                    if not Knockback2DCheckXY(x, y) then
                        call Knockback2DApplyAngle(udg_K2DAngle[i] + bj_PI)
                        if not Knockback2DCheckXY(x, y) then
                            call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
                            set udg_K2DX = x
                            set udg_K2DY = y
                        endif
                    endif
                else
                    set udg_K2DX = x
                    set udg_K2DY = y
                    set udg_K2DFreeze[i] = true
                endif
            endif
            call SetUnitX(u, udg_K2DX)
            call SetUnitY(u, udg_K2DY)
            set udg_K2DLastX[i] = udg_K2DX
            set udg_K2DLastY[i] = udg_K2DY
            if udg_K2DFXModel[i] != "" then
                set udg_K2DFXTimeLeft[i] = udg_K2DFXTimeLeft[i] - udg_K2DTimeout
                if udg_K2DFXTimeLeft[i] <= 0.00 then
                    set udg_K2DFXTimeLeft[i] = udg_K2DFXRate[i]
                    if udg_K2DFlying[i] then
                        call DestroyEffect(AddSpecialEffectTarget(udg_K2DFXModel[i], u, "origin"))
                    else
                        call DestroyEffect(AddSpecialEffect(udg_K2DFXModel[i], udg_K2DX, udg_K2DY))
                    endif
                endif
            endif
            if udg_K2DCollision[i] >= 0.00 then
                set udg_Knockback2DSource = u
                call GroupEnumUnitsInRange(bj_lastCreatedGroup, udg_K2DX, udg_K2DY, 200.00, null)
                call GroupRemoveUnit(bj_lastCreatedGroup, u)
                loop
                    set udg_Knockback2DUnit = FirstOfGroup(bj_lastCreatedGroup)
                    exitwhen udg_Knockback2DUnit == null
                    call GroupRemoveUnit(bj_lastCreatedGroup, udg_Knockback2DUnit)
                 
                    if IsUnitInRange(udg_Knockback2DUnit, u, udg_K2DCollision[i]) and udg_K2DFlying[i] == IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_FLYING) and (not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_STRUCTURE)) and not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_DEAD) and (udg_K2DUnbiasedCollision[i] or IsUnitAlly(udg_Knockback2DUnit, GetOwningPlayer(u))) and TriggerEvaluate(gg_trg_Knockback_2D) then
                        set udg_Knockback2DAngle = bj_RADTODEG * Atan2(GetUnitY(udg_Knockback2DUnit) - udg_K2DY, GetUnitX(udg_Knockback2DUnit) - udg_K2DX)
                        set udg_Knockback2DDistance = udg_K2DDistanceLeft[i]
                        set udg_Knockback2DBounces = udg_K2DBounce[i]
                        set udg_Knockback2DCollision = udg_K2DCollision[i]
                        if udg_K2DHeight[i] != 0.00 then
                            set udg_Knockback2DHeight = GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)
                        endif
                        set udg_Knockback2DLoopFX = udg_K2DFXModel[I]
                        set udg_Knockback2DTime = udg_K2DTimeLeft[i]
                        set udg_Knockback2DUnbiasedCollision = udg_K2DUnbiasedCollision[i]
                        call TriggerExecute(gg_trg_Knockback_2D)
                        set udg_Knockback2DSource = u //in case of a recursive knockback
                    endif
                endloop
            endif
            set udg_K2DVelocity[i] = udg_K2DVelocity[i] - udg_K2DFriction[i]
        else
            call TriggerExecute(gg_trg_Knockback_2D_Destroy)
        endif
    endloop
    set u = null
 
    //Disable dummy after the loop finishes so it doesn't interfere with the map
    call PauseUnit(udg_K2DDebrisKiller, true)
endfunction

//===========================================================================
function StartKnockback2DTimer takes nothing returns nothing
    call TimerStart(udg_K2DTimer, udg_K2DTimeout, true, function Knockback2DLooper)
endfunction
function InitTrig_Knockback_2D_System takes nothing returns nothing
endfunction

  • Knockback 2D Destroy
    • Events
      • Game - UnitIndexEvent becomes Equal to 2.00
    • Conditions
      • IsUnitBeingKnockedBack[UDex] Equal to True
    • Actions
      • -------- --------
      • -------- This trigger destroys any knockback; you can execute it yourself by first setting UDex to the custom value --------
      • -------- --------
      • Set IsUnitBeingKnockedBack[UDex] = False
      • Set K2DNext[K2DPrev[UDex]] = K2DNext[UDex]
      • Set K2DPrev[K2DNext[UDex]] = K2DPrev[UDex]
      • Set K2DPrev[UDex] = 0
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • K2DNext[0] Equal to 0
        • Then - Actions
          • Countdown Timer - Pause K2DTimer
        • Else - Actions
      • Set Knockback2DUnit = UDexUnits[UDex]
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • K2DHeight[UDex] Not equal to 0.00
        • Then - Actions
          • Animation - Change Knockback2DUnit flying height to (Default flying height of Knockback2DUnit) at 0.00
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • K2DFreeze[UDex] Not equal to True
          • (K2DImpact[UDex] is on) Equal to True
        • Then - Actions
          • Trigger - Run K2DImpact[UDex] (checking conditions)
        • Else - Actions
  • On Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • IsDamageSpell Equal to False
          • (Owner of DamageEventSource) Equal to Player 1 (Red)
        • Then - Actions
          • -------- --------
          • -------- There are four required variables when issuing a knockback --------
          • -------- --------
          • -------- 1. Knockback2DAngle -------- this is the direction angle the unit is knocked back (in degrees) --------
          • -------- 2. Knockback2DTime -------- this is how long the unit will be knocked back (in seconds) --------
          • -------- 3. Knockback2DDistance -------- this is how far the unit will be knocked back --------
          • -------- 4. Knockback2DUnit -------- this is the unit being knocked back --------
          • -------- --------
          • -------- When all four variables are set, you can run the Knockback 2D trigger, ignoring conditions --------
          • -------- --------
          • Set CenterPoint = (Position of DamageEventSource)
          • Set TargetPoint = (Position of DamageEventTarget)
          • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • Custom script: call RemoveLocation(udg_TargetPoint)
          • Set Knockback2DTime = 0.90
          • Set Knockback2DDistance = 500.00
          • Set Knockback2DUnit = DamageEventTarget
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of DamageEventSource) Not equal to (Owner of DamageEventTarget)
            • Then - Actions
              • -------- --------
              • -------- There are optional variables you can consider when issuing a knockback --------
              • -------- If they are not set, "0.00, false, null" or their default states assigned in the Knockback 2D Config trigger will be used --------
              • -------- --------
              • -------- Knockback2DAmphibious -------- Allows the unit to pass over deep water, but it is more performance-intensive --------
              • -------- -------- If you aren't ToolOrDie from TheHiveWorkshop, you probably won't use this --------
              • -------- Knockback2DBounces -------- if the unit will bounce off of walls or remain stunned there. --------
              • -------- --------
              • -------- Knockback2DCollision -------- You can specify the collision radius of units to be adjacently knocked back if you want --------
              • -------- -------- A value of 0 will require the units' collision sizes to overlap to knock back --------
              • -------- -------- A value greater than 0 will allow that much space between the units to still permit the knockback --------
              • -------- -------- A value less than 0 will disable collision checking altogether. --------
              • -------- --------
              • -------- Knockback2DDestRadius -------- If debris is to be killed, how far away must it be? --------
              • -------- Knockback2DFriction -------- How quickly the unit will slow down from its initial speed. --------
              • -------- Knockback2DFX -------- What kind of special effect will occasionally be played on the ground of the unit --------
              • -------- Knockback2DFXRate -------- How often should the effect appear? --------
              • -------- Knockback2DGravity -------- If you want to specify a custom gravity for each knockback, you can --------
              • -------- Knockback2DHeight -------- How high you want the unit to go during the knockback --------
              • -------- Knockback2DKillTrees -------- If you want surrounding trees to die, this is for you --------
              • -------- Knockback2DOnImpact -------- this trigger is run when the unit hits a wall or structure. --------
              • -------- -------- If it is bouncing, it can hit multiple walls and fire this trigger multiple times --------
              • -------- -------- The trigger will not run if it is off, so you can use this if you only want it to run on the first bounce --------
              • -------- Knockback2DOverride -------- Set this to true to prevent future knockbacks from interrupting this one --------
              • -------- Knockback2DPause -------- confines the unit to its knockback velocity (no running to change course) --------
              • -------- Knockback2DSimple -------- Only checks to make sure unit movement is within the map bounds --------
              • -------- Knockback2DSource -------- If you need to store this unit to deal damage on-impact or within the timer loop --------
              • -------- Knockback2DUnbiasedCollision -------- Should adjacent knockbacks consider alliances or be neutral? --------
              • -------- --------
              • Set Knockback2DAmphibious = True
              • Set Knockback2DBounces = False
              • Set Knockback2DCollision = 16.00
              • Set Knockback2DDestRadius = 128.00
              • Set Knockback2DGravity = 0.67
              • Set Knockback2DHeight = 137.00
              • Set Knockback2DKillTrees = False
              • Set Knockback2DOnImpact = On Impact <gen>
              • Set Knockback2DOverride = True
              • Set Knockback2DPause = True
              • Set Knockback2DSimple = True
              • Set Knockback2DSource = DamageEventSource
              • Set Knockback2DUnbiasedCollision = False
            • Else - Actions
              • -------- Allied units shouldn't allow collision with each other, but for the sake of the demo let's make it interesting --------
              • Set Knockback2DCollision = 32.00
              • -------- Also make it so that enemies will bounce with these experiments --------
              • Set Knockback2DUnbiasedCollision = True
              • Set Knockback2DLoopFX = Abilities\Weapons\FaerieDragonMissile\FaerieDragonMissile.mdl
              • Set Knockback2DFXRate = 0.20
          • -------- --------
          • -------- When all variables are set, run the Knockback 2D trigger, checking conditions if you want to be safe --------
          • -------- --------
          • Trigger - Run Knockback 2D <gen> (checking conditions)
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • Tauren_Shockwave[(Custom value of DamageEventSource)] Equal to True
                  • Tauren_WarStomp[(Custom value of DamageEventSource)] Equal to True
            • Then - Actions
              • Set CenterPoint = (Position of DamageEventSource)
              • Set TargetPoint = (Position of DamageEventTarget)
              • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
              • Custom script: call RemoveLocation(udg_CenterPoint)
              • Custom script: call RemoveLocation(udg_TargetPoint)
              • Set Knockback2DTime = 0.50
              • Set Knockback2DDistance = 75.00
              • Set Knockback2DUnit = DamageEventTarget
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Tauren_WarStomp[(Custom value of DamageEventSource)] Equal to True
                • Then - Actions
                  • -------- For War Stomp, add a height factor --------
                  • Set Knockback2DHeight = 250.00
                  • Set Knockback2DDistance = 100.00
                • Else - Actions
                  • Set Knockback2DDistance = 300.00
              • Trigger - Run Knockback 2D <gen> (ignoring conditions)
            • Else - Actions
      • -------- Deleting the next line will re-enable damage in the map --------
      • Set DamageEventAmount = 0.00
  • On Spell Cast
    • Events
      • Unit - A unit owned by Player 1 (Red) Starts the effect of an ability
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Ability being cast) Equal to Shockwave
        • Then - Actions
          • Set Tauren_Shockwave[(Custom value of (Triggering unit))] = True
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Ability being cast) Equal to War Stomp
            • Then - Actions
              • Set Tauren_WarStomp[(Custom value of (Triggering unit))] = True
            • Else - Actions
  • On Spell Stop
    • Events
      • Unit - A unit owned by Player 1 (Red) Stops casting an ability
    • Conditions
    • Actions
      • Unit - Reset ability cooldowns for (Triggering unit)
      • Unit - Set mana of (Triggering unit) to 100.00%
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Ability being cast) Equal to Shockwave
        • Then - Actions
          • -------- In case the unit had other damaging spells, we don't want those spells to trigger an unwanted knockback --------
          • Set Tauren_Shockwave[(Custom value of (Triggering unit))] = False
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Ability being cast) Equal to War Stomp
            • Then - Actions
              • -------- Same goes for here --------
              • Set Tauren_WarStomp[(Custom value of (Triggering unit))] = False
            • Else - Actions



  • 1.0.0.0 - Release
  • 1.0.0.1 - Changed IssuePointOrder to IssuePointOrderById.
    Now uses 1 trigger instead of 2.
  • 1.1.0.0 - Added a "bounce" functionality to the knockback.
    Made an add-on to support knocking back adjecent units (collateral knockback)
  • 1.2.0.0 - Added an additional flavor, "Knockback2DPause", which can be set only during map initialization. I also added a SetUnitPropWindow for when units are not paused, so that they appear motionless aside from the knockback.
  • 2.0.0.0 - Added five new, optional variables: height, pause, bounce, kill trees and an on-impact trigger. I will elaborate more on what these do in a future post.
  • 2.0.1.0 - Added a Gravity configurable that determines how quickly the unit will descend after it reaches its peak.
  • 2.0.2.0 - Gravity can now be configured per-knockback
  • 2.1.0.0 - You can now specify looping FX strings per-knockback. Changed the addons to custom script as they were mostly custom script anyway.
  • 3.0.0.0 - Split the one monolithic trigger into four smaller and more manageable ones. Units will no longer potentially pass through buildings if they had a collision size greater than 16.
  • 3.1.0.0 - Updated the Knockback 2D and Knockback 2D System triggers to add an option "Knockback2DSimple". When this boolean is true, all normal pathing is ignored except to make sure the unit is within the world bounds.
  • 4.0.0.0 - Everything has been overhauled.
    • No custom object editor data is used. Inferno was really only useful on my demo map, but it had issues like ignoring structures, pathing blockers and actually not considering collision sizes. That's right, despite that I had all the object data set properly to make it recognize various collision sizes, it was not doing that. Also, the custom Flame Strike ability had a delay on it which I could not get rid of no matter what I did. The ability could be cast at most per 0.02 seconds, and when you set its Object Editor duration to 0.00 instead of 0.01, the game crashes.
    • Real collision is used now.
      Code:
      //Each [ ] represents a SetItemPosition call on that space
      
      //Collision when pathing robustness is set to 0 or the unit's collision size is
      //set to 16 is simply one SetItemPosition call.
      
      //With robustness set to 1 or the unit has 32 collision:
         [ ]
      [ ][ ][ ]
         [ ]
         
      //With robustness set to 2 and the unit has 48 collision:
            [ ]
        [ ]     [ ]
      [ ]   [ ]   [ ]
        [ ]     [ ]
            [ ]
      
      //At 64 collision, the white spaces just become more pronounced.
      
      //As you can see, knocking back a unit with more than 32 collision size and
      //robustness set to 2 requires quite a few SetItemPosition calls. I am not using
      //SetUnitPosition with a dummy due to potential "enter/leave region" events in a
      //user's map.
    • The friction can now be customized per-knockback. I also fixed the default friction as I had improperly copied Berb's formula used in Knockback Lite - that problem is fixed now.
    • There is a dummy harvest unit created to sample if a destructable is debris or a tree (the user can change the issued order from attack to harvest if they only want to kill trees).
    • The Knockback 2D System trigger has been converted into optimized JASS so I could make the system run more smoothly.
    • The two addons have been merged into the Knockback2D System JASS script.
    • You can configure the rate at which special effects spawn under a unit being knocked back.
  • 4.1.0.0 - After being notified by Arad MNK that distance wasn't working properly, I found a pretty serious thing I've neglected the whole time this resource has been around. The friction I had been using was nearly 0 because I didn't copy Berb's formula properly, so the unit wasn't visibly slowing down. Therefore, the unit was always going too far. This update corrects that issue. You can change the friction to 0 to go back to the way it was before.
  • 4.2.0.0 - Friction can now be customized between 0 and 1 without adverse effects. Greatly improved the fluidity of secondary knockbacks by averaging the angle, distance and time of the new knockback with those of the first. A new option, Knockback2DUnbiasedCollision, has been added to remove alliance checking from the built-in knockback collider.
  • 4.2.1.0 - Added a way to customize the destructable death radius and it can also be done per-knockback. Also added a fix so that items don't get displaced when unhidden.
  • 4.2.2.0 - Flying units no longer knock back ground units and ground units no longer knock back flying units.
  • 4.2.3.0 - Flying units now display special effect art attached to their "origin". Simplified the pathing check for flying units to only check for air unit pathability.
  • 4.2.3.1 - Fixed an issue where Knockback2DOverride would break all subsequent attempts to knockback the unit running Knockback 2D <gen> (checking conditions) instead of ignoring them.
  • 4.2.4.0 - Knockback collision now tracks Knockback2DSource
  • 4.2.5.0 - Fixed an issue with large maps not working correctly. Just update the JASS script to apply this fix. The map isn't updated yet but I've updated the posted script.


Keywords:
Knockback, Berb, Berbanog, Unit Indexer
Contents

Ice Map (Map)

Reviews
10:13, 12th Dec 2015: BPower: The previous given rating ( in 2011 ) does no longer match with the current state of the Knockback2D code. I very recommend this resource to everyone, not only GUI'ers, who need a Knockback system for his/her map...
Level 2
Joined
Apr 20, 2011
Messages
20
Hmm I can't get this system working for my triggers... I know I imported the system correctly, because when I copied your OnDamage trigger into my map, all my units knockbacked properly. But when I tried to use knockback in my triggers, it doesn't work. Is it something wrong with my trigger? o_O


  • Shockwave
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shockwave
    • Actions
      • Set ShockwavePt = (Position of (Triggering unit))
      • Set ShockwavePt2 = (Target point of ability being cast)
      • Unit - Create 1 Shockwave Dummy for (Triggering player) at ShockwavePt facing ShockwavePt2
      • Unit - Set level of Shockwave Damage (Units) for (Last created unit) to (Level of (Ability being cast) for (Triggering unit))
      • Unit - Add a (5.00 + (Real((Level of (Ability being cast) for (Triggering unit))))) second Generic expiration timer to (Last created unit)
      • Set Knockback2DAngle = (Angle from ShockwavePt to ShockwavePt2)
      • Custom script: call RemoveLocation (udg_ShockwavePt)
      • Custom script: call RemoveLocation (udg_ShockwavePt2)
      • Set Knockback2DTime = (5.00 + (Real((Level of (Ability being cast) for (Triggering unit)))))
      • Set Knockback2DDistance = ((Real((Level of (Ability being cast) for (Triggering unit)))) x (2250.00 + (125.00 x (Real((Level of (Ability being cast) for (Triggering unit)))))))
      • Set Knockback2DUnit = (Last created unit)
      • Trigger - Run Knockback 2D <gen> (ignoring conditions)
 
@Opaque, try putting a game message in the trigger, if the game message doesn't show, then the ability being cast is the problem. By "not working" is it not doing a single thing at all? Have you copied the object editor ability from the map and made sure its levels are set to Peasant/Knight/Infernal/Centaur like I have in my map?

@Berb, only a computer needs 0x to understand that what you're giving it is hex, in fact if you use a programming calculator it doesn't print the results with an 0x prefix, it just prints the D0100.
 
Level 2
Joined
Apr 20, 2011
Messages
20
The ability works fine, because it executes all the functions as it should, except for the knockback part. But I'm sure I implemented your system properly, because when I copied your OnDamage trigger, the knockback worked fine.
 
Level 10
Joined
Sep 3, 2009
Messages
458
so uh I apparently... I cant get this to work. I've done the installation just as you said. I did notice that when I cast my spell (basically pushes target unit back) It shows the SFX on the unit but it doesn't get knocked back. Any help? I've been on this for about an hour now :/
 
Level 7
Joined
Jan 22, 2013
Messages
293
Hello, I am requesting to the owner of this system for permission to improve system to allows new configerations for customizing features for users of this knockback.

New True/Falses:
1> Allow_Freewill_Knockback
2> Allow_Rebound
3> Allow_KillTree
4> Allow_UnitBumping
5> Allow_IgnorePathing

The System was not user friendly and always forced people to use all effects of the knockback, not just what they wanted so I decided to modify that issue.

1. True/False option for a singular unit in the knockback index that allows a unit to attack, move around and cast spells while being knocked back
2. True/False option for a singular unit in the knockback index that allows you to control what units are allowed to rebound off of un-pathable locations
3. True/False option on if you want to destroy trees when knocked into them (including if you rebound off of them)
4. True/False option for a singular unit in the knockback index that allows control over if a unit causes a chain reaction bumper car effect with the knockback system towards units.
5. True/False if that singular unit is allowed to knockback over cliffs and un-pathables.
 
Last edited:
Ok, new update after several years.

ToolorDie needed a way for units to be knocked over water if they have amphibious pathing. The system uses a custom inferno to check pathing, which always returns false over deep water. I made a configurable boolean Knockback2DPause which is effective for the WHOLE map and cannot be toggled true or false as long as there are any active knockbacks. To be safe, I recommend leaving it as false unless you need that unique capability that ToolorDie requires.

I do not have a way to allow the unit to bounce while the pause method is used, but I will add one in a future update if it is requested.

I updated the default timeout to run at 60fps instead of 30, but you can change it back if you want.

Units that are knocked back will now have their prop window locked, so the effect is much smoother than it used to be.

Knockback2D 1.2.0.0 is now live.

EDIT: In the next 24 hours I will release version 2.0.0.0 which will use an item to check pathing in addition to the inferno, this way units don't run into buildings any more. I will also update it to give the user control over whether the knockback will bounce, and add an event if it doesn't so the user can decide if they want on-impact effects (like Poppy hitting units into a wall in LoL). I will also give a pseudo-3d effect that if the user wants to enable they can. It will only affect the flying height, not the pathing, so the unit will still bounce/stop when it hits a wall.
 
Last edited:
Ok, it took a bit longer than I had originally planned, but I have now released version 2.0!

Everything can now be customized per-knockback, so if you have one knockback that needs the unit to be stunned, and another that needs it to not interrupt channeling spells, now you have it.

A unit can now be paused and still bounce off of walls.

If a unit hits a wall and is not supposed to bounce, that unit will still be "stunned" for the duration of the knockback.

A unit can have its flying height set to a value that will peak in the middle of the knockback. Pretty simple, but makes this officially 2.5D (not 3D since I am not checking for 3D pathing).

You can now determine whether or not the knockback should kill trees or just simply stop when it hits them.

You also can now set a trigger to run when the unit hits an impassible object. The trigger will fire multiple times if a knockback bounces multiple times before expiring. If the trigger is disabled, this event will not fire, meaning if you only wanted it to run for one bounce you can take advantage of this.

I am proud to present this to the community! Again, please report any bugs. I have tested it pretty heavily, but nothing is for certain.
 
Well, I found previous version of your knockback very usefull.
There are already implemented in my map mini-stuns (for some knockback based spells), also I use disable unit movement (call SetUnitPropWindow(u, 0.00)), and pausing all units (for arena battles). So Im afraid to not interfere with all new abilitites in 2.5Dver.
Dont get me wrong but it would be usefull to get, as you said:
Keep it simple
simple knockback system with this feature when knockbaced unit will knockback other units on his path. Something like minimalistic version but fast and smooth.
You mb remember I wrote in other post that I found infinite stunned paladin, and inferno. I though because of bug in my map, but today I tested and it is wrong fields in Object editor in your K2DPathAbil. It's Data-Duration: 0.00
When Stats-CastingTime:3600.00 expires then spell is finished and we have on map infinite stunned paladin for example. Solution is set Data-Duration: for 3.00
so if it happends, summoned unit will exists 2sec (cause of Data-ImpactDelay: 1.00)

So maybe you consider to upload this minimalistic version (previous one with fixed OE fields). I got previous ver on my PC, but maybe other users wants;]
 
The object editor data did not change, so there's nothing to be reverted as far as that goes.

The PauseUnit feature is unnecessary, same with the SetUnitPropWindow, so you can disable those function calls if you don't want them. Don't use the PauseUnit option, anyway, as I only made that for Toolordie so he can get the feature he needed in his specific map.

Why would a unit have its movement disabled with SetUnitPropWindow in your map, if I may ask? There are other ways to disable a unit, for example an infinite-duration Channel ability.
 
There's spell "Nibiru" where I use SetUnitPropWindow(u, 0)
Victims around are moved in 0,03sec loop to the center point. Units can't move, teleport or cast spells. After few seconds when dmg is applied I call EnableUnitMovement (it is SetUnitPropWindow(u, GetUnitDefaultPropWindow(u) * bj_DEGTORAD)).
Anyway I have your previous version, and modified OE field I wrote. Will make tests today but for now theres no reason for me to switch to new 2.5 ver.
 
If you want the unit to not move, attack or cast spells, just add a high-duration channel ability to it and order it to cast it.

http://www.hiveworkshop.com/forums/jass-resources-412/snippet-suspendunit-266681/

And about the error you were talking about with permastun summons, you already found out that your entire map was bugged because of a cached order ID and not this system. It is impossible for this system to let the dummy caster actually drop an inferno, because even if you let an hour pass without a knockback, the caster is paused.
 
Level 1
Joined
Jan 23, 2015
Messages
160
He did it. I'll be gosh darned, Mr. Bribe did it.

Relied on his original system for a combat system I had in mind, I had the idea to further the excitement by making it possible to fight over water (had spare animations on models) and I asked Bribe to do it. He tried a few possibilities he could think of to incorporate fighting over water, we thought it couldn't be done, yet here it is ladies and gentlemen.

This is Crouching Briber Hidden Dragon!!
 
I am unsure how to get this system to work with an ability. It works fine when the hero can knockback on every hit. But I would like the knockback only to occur when an ability is used.

Can someone post a mini tutorial how to do so?

I am not super smart, but I can usually figure this stuff out. But, this time, I can't. I'm getting some ideas and I may figure it out eventually, but it would be nice if someone could just explain this. What do I do to set the event that causes the knockback. I get that it is on all damage, but how to I adjust that to be only spell damage or only damage from specific spells. I don't want every spell to knock back. I'm sorry for being such a noob, but I really like this system and I want to use it. Also, how do I turn normal damage back on?

BTW, this is awesome stuff Bribe!
 
The OnDamage trigger is just an example. You only need KnockBack 2D trigger for this to work.

To make it work with a specific spell, you would use the event "a unit starts the effect of an ability" and check if the ability being cast is the same as your ability. The Knockback2D unit is the target of the ability being cast, and the distance, angle and time are up to you.
 
The OnDamage trigger is just an example. You only need KnockBack 2D trigger for this to work.

To make it work with a specific spell, you would use the event "a unit starts the effect of an ability" and check if the ability being cast is the same as your ability. The Knockback2D unit is the target of the ability being cast, and the distance, angle and time are up to you.

Wow, thanks for the fast response. I like the detect damage system too though. For example, I might like to make a flame strike that knocks units back if they enter the flame after it is already going, or I might want to have it knock a unit back with a projectile.

Also, to be clear for future members, to activate the knockback, you would put your spell in an event at the top of the "OnDamage" trigger, like so:
  • On Damage
    • Events
      • Unit - A Unit Starts the Effect of an Ability
    • Conditions
      • (Ability Being Cast) Equal to MyNewAbility
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Then - Actions
          • -------- --------
          • -------- There are four required variables when issuing a knockback --------
          • -------- --------
          • -------- 1. Knockback2DAngle -------- this is the direction angle the unit is knocked back (in degrees) --------
          • -------- 2. Knockback2DTime -------- this is how long the unit will be knocked back (in seconds) --------
          • -------- 3. Knockback2DDistance -------- this is how far the unit will be knocked back --------
          • -------- 4. Knockback2DUnit -------- this is the unit being knocked back --------
          • -------- --------
          • -------- When all four variables are set, you can run the Knockback 2D trigger, ignoring conditions --------
          • -------- --------
          • Set CenterPoint = (Position of DamageEventSource)
          • Set TargetPoint = (Position of DamageEventTarget)
          • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • Custom script: call RemoveLocation(udg_TargetPoint)
          • Set Knockback2DTime = 0.90
          • Set Knockback2DDistance = 150.00
          • Set Knockback2DUnit = DamageEventTarget
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of DamageEventSource) Not equal to (Owner of DamageEventTarget)
            • Then - Actions
              • -------- --------
              • -------- There are new (2015 September), optional variables to consider when issuing a knockback --------
              • -------- If they are not set, their default states assigned in the Knockback 2D main trigger will be used --------
              • -------- --------
              • -------- Knockback2DBounces -------- if the unit will bounce off of walls or remain stunned there. --------
              • -------- Knockback2DKillTrees -------- If you want surrounding trees to die, this is for you --------
              • -------- Knockback2DHeight -------- How high you want the unit to go during the knockback --------
              • -------- Knockback2DOnImpact -------- this trigger is run when the unit hits a wall. --------
              • -------- -------- If it is bouncing, it can hit multiple walls and fire this trigger multiple times --------
              • -------- -------- The trigger will not run if it is off, so you can use this if you only want it to run on the first bounce --------
              • -------- Knockback2DPause -------- features more precise pathing if true, but interrupts channeling spells. --------
              • -------- Knockback2DSource -------- If you need to store this unit to deal damage on-impact or within the timer loop --------
              • -------- --------
              • Set Knockback2DBounces = False
              • Set Knockback2DHeight = 100.00
              • Set Knockback2DKillTrees = True
              • Set Knockback2DOnImpact = On Impact <gen>
              • Set Knockback2DPause = True
              • Set Knockback2DSource = DamageEventSource
            • Else - Actions
          • -------- --------
          • -------- When all variables are set, run the Knockback 2D trigger, ignoring conditions --------
          • -------- --------
          • Trigger - Run Knockback 2D <gen> (ignoring conditions)
        • Else - Actions
      • Set DamageEventAmount = 0.00
Do I have that correct?
 
Last edited:
The DamageEventX and IsDamageSpell stuff works only from actual damage trigger. With the event you have, you want to use stuff like "target point of ability being cast", "casting unit" and "target unit of ability being cast"

Thank you again. I want to make sure I have this right.
For war stomp, do I do it, like this?

  • Set CenterPoint = (Target of ability being cast)
  • Set TargetPoint = (Target point of ability being cast)

Also, how do I get the damage detection system to work with it? So, for example, an event like:
  • Events
    • Unit - A unit is damaged
and a condition:
  • conditions
    • Source of damage equal to (udg_Custom_Ability)
I have tried to make spells based on shockwave, but it is difficult to make the knockback move forward, spreading the units apart in a cone. The damage system would allow much more precision with much less effort.

I was able to make it work with a spell based on Storm Bolt like this:

  • Then - Actions
    • Set CenterPoint = (Position of (Casting unit)) *
    • Set TargetPoint = (Position of (Target unit of ability being cast)) *
    • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
    • Custom script: call RemoveLocation(udg_CenterPoint)
    • Custom script: call RemoveLocation(udg_TargetPoint)
    • Set Knockback2DTime = 0.50
    • Set Knockback2DDistance = 300.00
    • Set Knockback2DUnit = (Target unit of ability being cast) *
*indicates the changes to make a spell targeting a unit to run the system.
 
Last edited:
I have updated the map to add a few things

1) Gravity can now be specified per-knockback
2) The On Impact trigger in the map now taunts the knocked back unit to attack the knockback source in order to get it attacking again ASAP.
3) Since I had so many requests for a unit to be knocked back when hit by shockwave, I've added that to the map as well. When the ability effect starts, I set a boolean to let the On Damage trigger know that spell damage is coming from shockwave. When it stops, I set that boolean back to false.
 
Level 17
Joined
Dec 11, 2014
Messages
2,004
Knockback 2.5D

XD

Anyway I'm using this ;D


Just an idea that you can dynamically change the Effect in-game, not in the Initialization. I can do some small changes so I Can do that, but it makes my knockback effect change when my unit was already knockbacked, so maybe having these options:

Changing the Knockback effect In-game so that the Previous effect would still stay with the already knockbacked units;
Changing an Effect WHILE a Unit is being knockbacked.

I know, this may require some Indexing, but it seems doable :D


Either ways, 5/5!
 
Huge update to the code in the system! Split the one giant trigger into four smaller ones to make things easier to ubderstand. Along the way, I made some good performance improvements and some necessary sacrifices.

When Knockback2DPauseUnit is true, it applies the setunitpropwindow/cargo hold trick. If you want the unit's abilities to be disabled, use a dummy unit to stun or silence it. With Knockback2DPauseUnit set to false, the knockback behaves exactly like it did in version 1.0 (using SetUnitX/Y instead of SetUnitPosition).

A new boolean, Knockback2DAmphibious, has been added to allow units to move over deep water if desired, so naga fighters, naval vessels and Dragonball Z characters can fight above otherwise-illegal ground. Toolordie in particular should finally have the system working with this addition.

Previously, stopping a knockback was a chore for non-JASS fluent users. Now, just run the trigger Knockback 2D Destroy <gen> after setting the UDex variable to the custom value of the unit whose knockback you want to stop.

In all, the system is more efficient than version 2, and accurately detects building collision with as little performance degradation as possible.

Edit: I found a bug :( ... Easy to fix, though! If you use the amphibious option, scroll slightly down the Knockback 2D System - you will see an if/else block with four conditions. Change the top condition to "TempBool Equal to True" instead of not equal.

Edit 2: Fixed the bug. If there are any issues, please let me know.
 
Last edited:
Optional update! I will be using this update in at least one spell I plan to release soon, so nothing urgent.

3.1.0.0 - Updated the Knockback 2D and Knockback 2D System triggers to add an option called "Knockback2DSimple". When this boolean is true, all normal pathing is ignored except to make sure the unit is within the world bounds.

Edit: I'm going to be adding another feature to adjust the size of (or disable) collision when using Knockback 2D Collider. I will also change the FX type when over water for the Knockback 2D FX trigger. Any additional features you guys want?
 
Last edited:
The system has been updated to 4.0.0.0! Please read the change log here to see most of the things that I've changed.

To add to what's there, I have added a "Knockback2DOverride" boolean which prevents a unit from being knocked back by an additional knockback while it's being knocked back by that more important one.

Please use (Checking conditions) when running Knockback2D to ensure that it gets checked properly.
 
After seeing the cool new Meteor Strike by KILLCIDE, I have updated this to version 4.2.0.0. This way, the second phase of the meteor can be moved using Knockback 2.5D instead of needing to code all that movement into his system.

You can specify the friction of the knockback to a value between 0 and 1 (outside those bounds will result in a kind of boomerang-style movement which users can experiment with). 0 is frictionless and ideal for missiles or ice maps. 1 is a gradual deceleration to a natural stop.

The knockback collision now also uses much more realistic deflection. Instead of a secondary-applied knockback completely overwriting the first, the distance/time/angle are averaged between the existing knockback and the new one.

Edit: Also inspired by KILLCIDE's Meteor Strike, I have made it so you can customize the destructable death radius per-knockback.
 
Last edited:
Level 3
Joined
Dec 19, 2014
Messages
34
I loved the demo for this and wanted to use this in my own map, but every attempt has resulted in a compiling error that disables all of my other triggers. Is there something I must obviously be doing wrong that would cause this?
 
Level 3
Joined
Dec 19, 2014
Messages
34
I can copy into a new map without getting disabled triggers, so we gotta find out what's different on your setup.


- Have you selected "automatically create unknown variables when pasting trigger data"?

Yup.
- Did you also copy the Unit Indexer trigger?
Yup. Copied over everything except the test triggers for the demo. (Also tried keeping the triggers for the demo, no luck there either.)

What compile errors are you getting?
313 errors, all of which appear to be "Expected a reserved type or handle type." The errors kick in when I attempt to test the map and it systematically disables every trigger. My map is a pretty simple altered melee too, so nothing fancy.

Thanks for your help!
 
Level 3
Joined
Dec 19, 2014
Messages
34
I am up to date and can login to Battle.net. The error occurs even when I copy the triggers into a blank map. I've attached the error message.
 

Attachments

  • KnockBackError.png
    KnockBackError.png
    155.2 KB · Views: 101
Hmm.. if I had to guess, you copied the Knockback first and then the Unit Indexer (or put Unit Indexer below it in the category before copying). There seems to be a weird error when that happens, and I'll likely just remove the indexer event from Knockback 2D to compensate. Meanwhile, the fix should be to go into Variable Editor, delete UnitIndexEvent and create a new variable called UnitIndexEvent as a real type, non array. Then, any variables which got disabled from Knockback 2D and Unit Indexer will need that re-assigned (or you could copy Unit Indexer again and just re-assign Knockback's event).
 
Level 3
Joined
Dec 19, 2014
Messages
34
Great! That did the trick. One last thing though: New damage during a knockback causes the game to freeze up (I have to use task manager to shut off WC3 during the test.) I tried turning on Knockback2DOveride to prevent this, but that just makes it so that each unit can only be knocked back the very first time they are attacked and never again. Do you know what my mistake in installation must be?
 
Level 3
Joined
Dec 19, 2014
Messages
34
Sure, here's the map (dunno how to add attachments to PMs). Thanks so much for checking this problem out for me, I hope the issue isn't something idiotic on my end (I just started using the WE a month and a half ago). Careful with testing it though, I've had to power down my computer just to get out of the freeze this bug causes.
 

Attachments

  • Taylor Town 1.7.w3x
    350.3 KB · Views: 94
Level 3
Joined
Dec 19, 2014
Messages
34
In the Knockback 2D trigger, enable the line where the trigger "Knockback 2D Destroy" is run.

That's a problem with importing this system. I will eventually make a patch to correct that issue, but for any future users, that is the fix.

That fixed it and it looks fantastic. Thank you so much for helping me through these issues!
 
TreeKiller should get harvest ability in case user chooses only "harvest" and not "attack" as string.
As you use a default ghoul as TreeKiller it can't get ensured else that he can harvest at all.

And in case the user chooses "attack" as string, it would also be unable to destroy
something if the MovementSpeed of unit was changed to "0" or the unit has no movement type. (in object editor)

The cases are probably more rarely, especially the second, but it's a potential bug.
 
Top