Chaosy
Tutorial Reviewer
- Joined
- Jun 9, 2011
- Messages
- 13,239
I find the image confusing. Perhaps it's just me.
function OnHitExample takes nothing returns nothing
// avaiable global variables inside this callback function, read-only:
// (unit) kb_unit = main unit connected with this function
// (unit) kb_obstacle_unit = unit who was hit by kb_unit (if a unit was hit)
// (integer) kb_hitCount = counts all hits done by kb_unit
// (integer) kb_callback use to determine 2 'events' type: KB_HIT_OBSTACLE_UNIT or //KB_HIT_THE_WALL
// KB_HIT_THE_WALL = structure or doodad or cliff or tree, if KB_HIT_OBSTACLE_UNIT it refers to //kb_obstacle_unit
// don't use "waits" here
if kb_callback == KB_HIT_OBSTACLE_UNIT and kb_hitCount == 1 then
call DisplayTextToForce(GetPlayersAll(), GetUnitName(kb_unit) + " hits the " + GetUnitName(kb_obstacle_unit))
endif
endfunction
function KB_DestroyKnockbackOnUnit takes unit u returns nothing
orfunction KB_DestroyAllKnockbacks takes nothing returns nothing
library KnockbackSimple initializer Init uses IsDestructableTree, TerrainPathability // by ZibiTheWand3r3r v.1.00
// Features:
// Simple to use knockback: one function call, configurable behavior (16 possible versions), supports diffrent collision sizes
// callback function, can destroy knockbacks any time, system is for ground units only
// *****************************
// Requires:
// library IsDestructableTree * by BPower:
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isdestructabletree-248054/
// library TerrainPathability * by Rising_Dusk
// http://www.wc3c.net/showthread.php?t=103862
// GUI Unit Event v2.2.1.0 * by Bribe
// http://www.hiveworkshop.com/threads/gui-unit-event-v2-2-1-0.201641/
// *****************************
// How to use:
// function KnockBack takes:
// (code) onHitCode = code that will be executed when unit bounce/hits other unit/obstacles (structure, cliff, doodad, tree)
// set to "null" if not needed, (onHitCode is executed as trigger action)
// (unit) u = knockbacked unit
// (real) distance = how far unit will be moved
// (real) duration = how long knockback exists [sec]
// (real) angle = knockback angle [in radians!]
// (boolean) interruptSpells = if "true" it's interrupts orders/channeling spells on unit u, when knockback starts
// (boolean) killTrees = if "true" unit(s) will destroy trees if unit travels with >minimum speed, kb_minSpeedToKillTree
// (boolean) disableMovement = if "true" unit won't be able to move during knockback, it's using SetUnitPropWindow
// technique, so if you already using this in your map it may interfere. In this case set this boolean to false.
// (integer) kbType see table below, avaiable 4 types: KB_TYPE_NO_BOUNCE ,
// KB_TYPE_SLIDE , KB_TYPE_STOP_ON_OBSTACLES , KB_TYPE_NORMAL
// (integer) bounceTypeFiltr see table below, avaiable 4 filters: KB_FILTR_BOUNCE_NONE ,
// KB_FILTR_BOUNCE_ENEMY_ONLY , KB_FILTR_BOUNCE_ALLIED_ONLY , KB_FILTR_BOUNCE_ALL
// (string) effects - special effect played on unit(s) every given distance (variable effectOccursDistance),
// if null, no effect will be played
// (real) effectOccursDistance - special effect declared above will be played every [effectOccursDistance] distance
// *****************************
// (code) onHitCode example:
// function OnHitExample takes nothing returns nothing
// avaiable global variables inside this callback function, read-only:
// (unit) kb_unit = main unit connected with this function
// (unit) kb_obstacle_unit = unit who was hit by kb_unit (if a unit was hit)
// (integer) kb_hitCount = counts all hits done by kb_unit
// (integer) kb_callback use to determine 2 'events' type: KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
// KB_HIT_THE_WALL = structure or doodad or cliff or tree, if KB_HIT_OBSTACLE_UNIT it refers to kb_obstacle_unit
// don't use "waits" here
// if kb_callback == KB_HIT_OBSTACLE_UNIT and kb_hitCount == 1 then
// call DisplayTextToForce(GetPlayersAll(), GetUnitName(kb_unit) + " hits the " + GetUnitName(kb_obstacle_unit))
// endif
// endfunction
// *****************************
// knockback types / bounce types description
// kbType: KB_TYPE_NO_BOUNCE: stops on obstacles /structures/cliffs/doodads
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore alied units pathing on my way, stops on first enemy unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore enemy units pathing on my way, stops on first allied unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// stops on first unit found on my way
// kb_type: KB_TYPE_SLIDE: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way, don't bounce me from other units, and *don't bounce any units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- enemies only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- allied only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// ignore all units pathing on my way, don't bounce me from other units, but bounce all units on my way
// --> if 2 knockbacked units meet, KB_TYPE_SLIDE has priority and it *may* overwrite existing knockback <---
// --> if KB_TYPE_SLIDE will bounce other units it will be done with angle 90 degrees <--
// kb_type: KB_TYPE_STOP_ON_OBSTACLES: don't bounce me on obstacles structures/cliffs/doodads/trees and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
// kb_type: KB_TYPE_NORMAL: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
//
private function KB_FilterOutAbility takes integer abiId returns nothing
set kb_filterOutAbiInteger = kb_filterOutAbiInteger + 1
set kb_filterOutAbility[kb_filterOutAbiInteger] = abiId
endfunction
//*****************************************************************
// **************** BEGIN OF USER CONFIGURABLES **********************
private function KB_AdvancedUserConfigurables takes nothing returns nothing
// these will apply >globaly< to knockback system:
set kb_minSpeedToKillTree = 500.00 // minimum unit speed that allow to destroy tree, only positive values allowed
set kb_reduceSpeedOnHitFactor = 20.00 // allowed range: 0 ... 100
// (very high values of kb_reduceSpeedOnHitFactor may look ugly and cause unit to stop instantly)
// following settings will filter out units with abilities "ghost" "ghosts visible" and with buff "windwalk"
// so units with these abilities will not be bounced, as they already can be walked over
call KB_FilterOutAbility('Agho')
call KB_FilterOutAbility('Aeth')
call KB_FilterOutAbility('BOwk')
// you can add more abilities/buffs here like 3 above
// call KB_FilterOutAbility('YourAbiId')
endfunction
// **************** END OF USER CONFIGURABLES ************************
//*****************************************************************
globals
group kb_ug
real array kb_distance
real array kb_time
real array kb_angle
real array kb_d1
real array kb_d2
real array kb_sin
real array kb_cos
boolean array kb_killTrees
boolean array kb_interruptSpells
boolean array kb_disableMovement
integer array kb_knockbackType
integer array kb_bounceFiltrType
string array kb_effect
real array kb_effectOccursDistance
real array kb_lastDistanceEffect
real array kb_collision
real array kb_distanceLeft
real array kb_timeLeft
trigger array kb_trg_onHit
trigger kb_trg_loop
integer array kb_hitCounter // for callback function
integer kb_hitCount = 0 // for callback function
unit kb_unit = null // for callback function
unit kb_obstacle_unit = null // for callback function
player kb_player
rect kb_rect
group kb_group
timer kb_timer
real kb_minSpeedToKillTree = 500.00
real kb_reduceSpeedOnHitFactor = 10.00
real kb_reduceSpeedOnHit = 1.00
constant real KB_INTERVAL = 0.03125
constant real KB_STEPS_PER_SEC = 1.00 / KB_INTERVAL
boolexpr array kb_filtr
constant integer KB_FILTR_BOUNCE_NONE = 0
constant integer KB_FILTR_BOUNCE_ENEMY_ONLY = 2
constant integer KB_FILTR_BOUNCE_ALLIED_ONLY = 3
constant integer KB_FILTR_BOUNCE_ALL = 4
constant integer KB_TYPE_NO_BOUNCE = 0
constant integer KB_TYPE_SLIDE = 10
constant integer KB_TYPE_STOP_ON_OBSTACLES = 20
constant integer KB_TYPE_NORMAL = 30
constant integer KB_HIT_OBSTACLE_UNIT = 101
constant integer KB_HIT_THE_WALL = 102
integer kb_callback
integer kb_filterOutAbiInteger = 0
integer array kb_filterOutAbility
endglobals
//-----------------------------------------------------------
//x, y, = centerX, centerY
//-----------------------------------------------------------
function KB_IsPointWalkableForSize takes real x, real y, real whatSize returns boolean //x, y, = centerX, centerY
if whatSize <= 16.00 then //one item check:
return IsTerrainWalkable(x, y)
endif // for larger units: 5 item checks
return IsTerrainWalkable(x, y) and IsTerrainWalkable(x+32.00, y) and IsTerrainWalkable(x-32.00, y) and IsTerrainWalkable(x, y+32.00) and IsTerrainWalkable(x, y-32.00)
endfunction
//------------------------------------------------------------
//------------------------------------------------------------
function KB_GetBounceAngle takes real angle, real x, real y, integer id returns real
local real newAngleVer1 = ( - angle )
local real newAngleVer2 = ( - bj_PI - angle ) // for negative angles
if angle>0.00 then
set newAngleVer2 = (bj_PI - angle)
endif
//++++
if KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer1), y+kb_d1[id] * Sin(newAngleVer1), kb_collision[id]) then
return newAngleVer1
elseif KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer2), y+kb_d1[id] * Sin(newAngleVer2), kb_collision[id]) then
return newAngleVer2
else //3rd version - reverse direction:
set newAngleVer1 = (angle + bj_PI) // for negative angle
if angle>0.00 then
set newAngleVer1 = (angle - bj_PI)
endif
endif
return newAngleVer1
endfunction
//------------------------------------------------------------
//-----------------------------x/y = obstacleUnit X/Y
function KB_GetBounceAngle90 takes real angle, real x, real y, real unitX, real unitY returns real
local real newAnglePlus = ModuloReal((angle + (bj_PI/2.00)), (2.00*bj_PI)) // +90
local real newAngleMinus = ModuloReal((angle - (bj_PI/2.00)), (2.00*bj_PI)) // -90
local real x1 = x+32.00 * Cos(newAnglePlus)
local real y1 = y+32.00 * Sin(newAnglePlus)
local real x2 = x+32.00 * Cos(newAngleMinus)
local real y2 = y+32.00 * Sin(newAngleMinus)
//compare distances
if ((x1-unitX) * (x1-unitX) + (y1-unitY) * (y1-unitY)) > ((x2-unitX) * (x2-unitX) + (y2-unitY) * (y2-unitY)) then
return newAnglePlus
endif
return newAngleMinus
endfunction
//-------------------------------------------------------------------------------
//-------------Zwiebelchen:-------------------------------------------
// The difference between GroupEnumUnitsInRange and IsUnitInRangeXY is, that the former will only enumerate units
// whose coordinate center is inside the radius.
// The IsUnitInRange natives will already return true if only a part of the unit is inside the radius.
//--------------------------------------------------------
// KB_GetUnitCollisionSize checks up to 64 unit collision size // returns values from 8 ..to.. 64
//--------------------------------------------------------
function KB_GetUnitCollisionSize takes unit u, real unitX, real unitY returns real
local real i=0.00
local real size=64.00
loop
exitwhen i==64.00
if not IsUnitInRangeXY(u, (unitX+i), unitY, 0.00) then
set size = (i-1.00)
exitwhen true
endif
set i=i+1.00
endloop
if size<8.00 then
return 8.00
endif
return size
endfunction
//------------------------------------------------------------
function IsUnitAlive takes unit u returns boolean
return not (GetUnitTypeId(u) == 0 or IsUnitType(u, UNIT_TYPE_DEAD))
endfunction
//------------------------------------------------------------
// DisableUnitMovement /* v1.0.0.1
// * Disables unit movement. They can still turn, but will stay in
// * place. It simulates an ensnare-like effect, except that it will
// * not ground units, it does not have buffs, does not interrupt
// * channeled casts and appears to have no downsides.
// * Full credits to WaterKnight for discovering this technique.
//--------------------------------------------------------------------
function DisableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, 0)
endfunction
//--------------------------------------------------------------------
function EnableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u) * bj_DEGTORAD)
endfunction
//------------------------------------------------------------
//------------------------------------------------------------
function KBFiltr_1 takes unit u returns boolean
local integer a=1
local integer id = GetUnitUserData(u)
local boolean isKnockbacked = IsUnitInGroup(u, kb_group)
if IsUnitAlive(u) and (not IsUnitType(u, UNIT_TYPE_FLYING)) and (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsUnitVisible(u, kb_player)) then
// also filters out units who sliding: (don't count as obstacle) and with allied/enemy filter
if not (isKnockbacked and kb_knockbackType[id] == KB_TYPE_SLIDE) then
if not (isKnockbacked and kb_knockbackType[id] == KB_TYPE_NO_BOUNCE and kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE) then
if not (IsUnitAlly(u, kb_player) and isKnockbacked and kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_ENEMY_ONLY) then
if not (IsUnitEnemy(u, kb_player) and isKnockbacked and kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_ALLIED_ONLY) then
loop
exitwhen a>kb_filterOutAbiInteger
if GetUnitAbilityLevel(u, kb_filterOutAbility[a]) > 0 then
return false
endif
set a=a+1
endloop
return true
endif
endif
endif
endif
endif
return false
endfunction
//--------
//--------
function KBFiltr_groundNotStruc takes nothing returns boolean
return KBFiltr_1(GetFilterUnit())
endfunction
// set kb_player before confirm this 2 checks!
function KBFiltr_groundNotStrucAlliedOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitAlly(GetFilterUnit(), kb_player)
endfunction
function KBFiltr_groundNotStrucEnemyOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), kb_player)
endfunction
//------------------------------------------------------------------------
function KB_OnRemoveFromGame takes nothing returns nothing
call GroupRemoveUnit(kb_group, udg_UDexUnits[udg_UDex])
//clean:
if kb_trg_onHit[udg_UDex] != null then
call DestroyTrigger(kb_trg_onHit[udg_UDex])
set kb_trg_onHit[udg_UDex] = null
endif
set kb_effect[udg_UDex] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//----------------------------------------------------------------------------
function KB_DestroyAllEnum takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetUnitUserData(u)
call GroupRemoveUnit(kb_group, u)
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
call EnableUnitMovement(u)
set kb_effect[id] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
set u=null
endfunction
//----------------------------
function KB_DestroyAllKnockbacks takes nothing returns nothing
call ForGroup(kb_group, function KB_DestroyAllEnum)
endfunction
//----------------------------
function KB_DestroyKnockbackOnUnit takes unit u returns nothing
local integer id = GetUnitUserData(u)
call GroupRemoveUnit(kb_group, u)
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
call EnableUnitMovement(u)
set kb_effect[id] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//=======================================================
function KB_CheckAllowedTypes takes integer kbType, integer bounceTypeFiltr returns boolean
if kbType == KB_TYPE_NO_BOUNCE or kbType == KB_TYPE_SLIDE or kbType == KB_TYPE_STOP_ON_OBSTACLES or kbType == KB_TYPE_NORMAL then
if bounceTypeFiltr == KB_FILTR_BOUNCE_NONE or bounceTypeFiltr == KB_FILTR_BOUNCE_ENEMY_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALLIED_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALL then
return true
endif
endif
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Knockback error: wrong integer pass to function")
return false
endfunction
//-----------------------------------------------------------------------------------------
//-----main function --------------------------------------------------------------------
function KnockBack takes code onHitCode, unit u, real distance, real duration, real angle, boolean interruptSpells, boolean killTrees, boolean disableMovement, integer kbType, integer bounceTypeFiltr, string effects, real effectOccursDistance returns nothing
local integer id = GetUnitUserData(u)
local integer q = R2I(duration / KB_INTERVAL) // (Number of steps)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
if GetUnitDefaultMoveSpeed(u) == 0.00 or (not KB_CheckAllowedTypes(kbType, bounceTypeFiltr)) then //protection
return
endif
set kb_effect[id] = null
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
if onHitCode != null then
set kb_trg_onHit[id] = CreateTrigger()
call TriggerAddAction(kb_trg_onHit[id], onHitCode)
endif
set kb_distance[id] = distance
set kb_distanceLeft[id] = distance
set kb_time[id] = duration
set kb_timeLeft[id] = duration
set kb_hitCounter[id] = 0
set kb_angle[id] = angle
set kb_sin[id] = Sin(angle)
set kb_cos[id] = Cos(angle)
set kb_d1[id] = 2 * kb_distance[id] / (q + 1) // the "bit" in "moving a unit a bit a time"
set kb_d2[id] = kb_d1[id] / q // will decrease d1 with each execution
set kb_killTrees[id] = killTrees
set kb_interruptSpells[id] = interruptSpells
set kb_disableMovement[id] = disableMovement
set kb_knockbackType[id] = kbType
set kb_bounceFiltrType[id] = bounceTypeFiltr
if kb_interruptSpells[id] then
call SetUnitPosition(u, x, y)
endif
if kb_disableMovement[id] then
call DisableUnitMovement(u)
endif
if effects != null then
set kb_effect[id] = effects
endif
set kb_lastDistanceEffect[id] = kb_distance[id]
set kb_effectOccursDistance[id] = effectOccursDistance
set kb_collision[id] = KB_GetUnitCollisionSize(u, x, y)
if FirstOfGroup(kb_group)==null then
call TimerStart(kb_timer, KB_INTERVAL, true, null)
endif
call GroupAddUnit(kb_group, u)
endfunction
// KBLOOP part =============================================
function KB_BounceEvent takes unit u, integer id, integer eventType returns nothing
if kb_trg_onHit[id] != null then
set kb_callback = eventType // KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
set kb_unit = u
set kb_hitCounter[id] = kb_hitCounter[id] + 1
set kb_hitCount = kb_hitCounter[id]
call TriggerExecute(kb_trg_onHit[id])
endif
endfunction
//------------------------------------------------------------------------------------------------
// use this for bounce from structures/cliffs/doodads ONLY:
//------------------------------------------------------------------------------------------------
function KB_BounceUnit takes unit u, integer id, real unitX, real unitY returns nothing
set kb_angle[id] = KB_GetBounceAngle(kb_angle[id], unitX, unitY, id) //change angle
set kb_sin[id] = Sin(kb_angle[id])
set kb_cos[id] = Cos(kb_angle[id])
call SetUnitX(u, unitX + kb_d1[id] * kb_cos[id])
call SetUnitY(u, unitY + kb_d1[id] * kb_sin[id])
set kb_d1[id] = kb_d1[id] * kb_reduceSpeedOnHit // reducing distance kb_d1
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event
endfunction
//------------------------------------------------------------------------------------------------
//-this for better visual effect --
//-KB_BounceEvent is not included here inside this func -----------
function KB_BounceUnitReverseWithMinSpeed takes unit u, integer id returns nothing
local real angle = (kb_angle[id] + bj_PI)
if kb_angle[id]>0.00 then // reverse direction:
set angle = (kb_angle[id] - bj_PI)
endif // initiate new kb, so old one will be ended:
call KnockBack(null, u, 25.00, 0.30, angle, false, kb_killTrees[id], kb_disableMovement[id], KB_TYPE_SLIDE, KB_FILTR_BOUNCE_NONE, null, 1.00)
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_TwoUnitsHit takes unit mainUnit, integer id, unit obstacleUnit, integer obstacleId returns nothing
local integer kb1 = kb_knockbackType[id]
local integer kb2
if IsUnitInGroup(obstacleUnit, kb_group) then
set kb2 = kb_knockbackType[obstacleId]
if kb2 == KB_TYPE_NO_BOUNCE then
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
if kb2 == kb1 then // both "no-bounce"
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
call KB_BounceUnitReverseWithMinSpeed(obstacleUnit, obstacleId)
endif
elseif (kb2 == KB_TYPE_NORMAL or kb2 == KB_TYPE_STOP_ON_OBSTACLES) and (kb1 == KB_TYPE_NORMAL or kb1 == KB_TYPE_STOP_ON_OBSTACLES or kb1 == KB_TYPE_SLIDE) then
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
endif
else // obstacle unit *not* in kb_group:
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
if kb1 == KB_TYPE_NO_BOUNCE then
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
endif
endif
endfunction
//-------------------------------------------------------------------------------
// kb_u primary unit, unitX = GetUnitX(kb_u)
// bouncePrimaryUnit - if "true" will bounce kb_u in opposite direction to new unit on way
// bounceNewUnit -if "true" will bounce new unit in opposite direction to kb_u unit
function KB_CheckUnitsAroundAndBounce takes unit kb_u, integer kb_id, real unitX, real unitY, real targetX, real targetY, integer filtrNr, boolean bouncePrimaryUnit, boolean bounceNewUnit returns boolean
local unit obst_u
local integer obst_id
local real obstX
local real obstY
local integer foundUnits=0
local real angle
local integer t
local integer b
local real Tx
local real Ty
local real c
set kb_player = GetOwningPlayer(kb_u)
call GroupEnumUnitsInRange(kb_ug, targetX, targetY, 150.00, kb_filtr[filtrNr]) // alive+ground+not structure
loop
set obst_u = FirstOfGroup(kb_ug)
exitwhen obst_u == null
set obstX = GetUnitX(obst_u)
set obstY = GetUnitY(obst_u)
set c = KB_GetUnitCollisionSize(obst_u, obstX, obstY) //pick bigger collision:
if c < kb_collision[kb_id] then
set c = kb_collision[kb_id]
endif
if obst_u != kb_u and IsUnitInRange(obst_u, kb_u, c) then
set foundUnits = 1
set obst_id = GetUnitUserData(obst_u)
call KB_TwoUnitsHit(kb_u, kb_id, obst_u, obst_id)
// if bounce primary unit:
if bouncePrimaryUnit then
set angle = Atan2(unitY - obstY, unitX - obstX) // reverse angle
set Tx = unitX+kb_d1[kb_id] * Cos(angle)
set Ty = unitY+kb_d1[kb_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, kb_collision[kb_id]) then
call SetUnitX(kb_u, Tx)
call SetUnitY(kb_u, Ty)
set kb_angle[kb_id] = angle //change angle
set kb_sin[kb_id] = Sin(kb_angle[kb_id])
set kb_cos[kb_id] = Cos(kb_angle[kb_id])
set kb_d1[kb_id] = kb_d1[kb_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
else // not walkable
set kb_d1[kb_id] = 0.00 // end knockback
endif
else // if not bounce, then move for slide type only:
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then
call SetUnitX(kb_u, targetX)
call SetUnitY(kb_u, targetY)
endif
endif
// if bounce obstacle unit:
if bounceNewUnit then
set angle = Atan2(obstY - unitY, obstX - unitX) // reverse angle
set c = KB_GetUnitCollisionSize(obst_u, obstX, obstY)
if IsUnitInGroup(obst_u, kb_group) then
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // hit by sliding unit, initiate new slide, slide overwrites existing one
if kb_timeLeft[kb_id] * kb_distanceLeft[kb_id] > 1.00 then //don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if kb_bounceFiltrType[kb_id] == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, kb_distanceLeft[kb_id], kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
else // hit by non-sliding unit, update existing kb:
set Tx = obstX+kb_d1[obst_id] * Cos(angle)
set Ty = obstY+kb_d1[obst_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, c) then
call SetUnitX(obst_u, Tx)
call SetUnitY(obst_u, Ty)
set kb_angle[obst_id] = angle //change angle
set kb_sin[obst_id] = Sin(kb_angle[obst_id])
set kb_cos[obst_id] = Cos(kb_angle[obst_id])
set kb_d1[obst_id] = kb_d1[obst_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
endif
endif
else // unit NOT in kb_group, initial new kb:
if kb_timeLeft[kb_id] * kb_distanceLeft[kb_id] > 1.00 then //don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if b == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // if obstacle hits by sliding unit, change angle to 90
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
endif
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, kb_distanceLeft[kb_id], kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
endif
endif
//bounce obstacle unit:end
endif
call GroupRemoveUnit(kb_ug, obst_u)
endloop
call GroupClear (kb_ug)
set obst_u=null
return foundUnits > 0
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_MoveUnit takes unit u, integer id, real unitX, real unitY, real targetX, real targetY returns nothing
local boolean isWalkable = KB_IsPointWalkableForSize(targetX, targetY, kb_collision[id])
if kb_knockbackType[id] == KB_TYPE_NO_BOUNCE then // no bounce at all
if not isWalkable then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
else // walkable
if kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
else // 3 other filters:
if (not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, false)) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
endif
endif
//---------------------------------SLIDE:-------------------------------------------------
elseif kb_knockbackType[id] == KB_TYPE_SLIDE then
if not isWalkable then
call KB_BounceUnit(u, id, unitX, unitY) //fires hit event
else
if kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
else // 3 other filters:
if not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, true) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
endif
endif
//--------------------------------- KB_TYPE_NORMAL / KB_TYPE_STOP_ON_OBSTACLES -------------------
elseif kb_knockbackType[id] == KB_TYPE_NORMAL or kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
if not isWalkable then
if kb_knockbackType[id] == KB_TYPE_NORMAL then
call KB_BounceUnit(u, id, unitX, unitY)
elseif kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
endif
else //walkable:---
if kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then
if not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, KB_FILTR_BOUNCE_ALL, true, false) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
else // 3 other filters
if not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], true, true) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
endif
endif
endif
endfunction
//-----------------------------------------------------------------
function KB_KillTree takes nothing returns nothing
call KillTree(GetEnumDestructable())
endfunction
//------------------------------------------------------------------
function KB_ShowEffect takes unit u, integer id returns nothing
if kb_effect[id] != null and (kb_distanceLeft[id] < (kb_lastDistanceEffect[id] - kb_effectOccursDistance[id])) then
call DestroyEffect(AddSpecialEffectTarget(kb_effect[id], u, "origin"))
set kb_lastDistanceEffect[id] = kb_distanceLeft[id]
endif
endfunction
//------------------------------------------------------------------
function KB_LoopEnum takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetUnitUserData(u)
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local real targetX = unitX + kb_d1[id] * kb_cos[id]
local real targetY = unitY + kb_d1[id] * kb_sin[id]
if (not IsUnitAlive(u)) or kb_d1[id] <= 0.00 then
call KB_DestroyKnockbackOnUnit(u)
set u=null
return
endif
if kb_killTrees[id] and ((kb_d1[id]*KB_STEPS_PER_SEC) > kb_minSpeedToKillTree) then
call MoveRectTo(kb_rect, targetX, targetY)
call EnumDestructablesInRect(kb_rect, null, function KB_KillTree)
endif
call KB_ShowEffect(u, id)
call KB_MoveUnit(u, id, unitX, unitY, targetX, targetY)
set kb_d1[id] = kb_d1[id] - kb_d2[id] // decrease speed
set kb_distanceLeft[id] = kb_distanceLeft[id] - kb_d1[id] // for additional kb-ed units
set kb_timeLeft[id] = kb_timeLeft[id] - KB_INTERVAL // for additional kb-ed units
set u=null
endfunction
//--------------------------------------------------------------------------
function KB_Loop takes nothing returns boolean
call ForGroup(kb_group, function KB_LoopEnum)
return false
endfunction
//=======================================================
//=======================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
set kb_ug = CreateGroup()
set kb_group = CreateGroup()
set kb_timer = CreateTimer()
set kb_rect = Rect(0.0, 0.0, 256.0, 256.0)
set kb_trg_loop = CreateTrigger()
call TriggerRegisterTimerExpireEvent(kb_trg_loop, kb_timer) //timer kb_timer expires
call TriggerAddCondition(kb_trg_loop, Condition(function KB_Loop))
//----------------------------------------------------
call KB_AdvancedUserConfigurables()
if kb_minSpeedToKillTree < 0.00 then
set kb_minSpeedToKillTree = 0.00
endif
if kb_reduceSpeedOnHitFactor < 0.00 then
set kb_reduceSpeedOnHitFactor = 0.00
elseif kb_reduceSpeedOnHitFactor > 100.00 then
set kb_reduceSpeedOnHitFactor = 100.00
endif
set kb_reduceSpeedOnHit = 1.00 - (kb_reduceSpeedOnHitFactor / 100.00) // for kb_d1, range: 0.00 ... 1.00
//----------------------------------------------------
set kb_filtr[KB_FILTR_BOUNCE_NONE] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALL] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALLIED_ONLY] = Condition(function KBFiltr_groundNotStrucAlliedOnly)
set kb_filtr[KB_FILTR_BOUNCE_ENEMY_ONLY] = Condition(function KBFiltr_groundNotStrucEnemyOnly)
call TriggerRegisterVariableEvent( t, "udg_UnitIndexEvent", EQUAL, 2.00 ) // remove unit detection
call TriggerAddAction(t, function KB_OnRemoveFromGame)
set t=null
endfunction
endlibrary
library KnockbackSimple initializer Init uses IsDestructableTree, TerrainPathability // by ZibiTheWand3r3r v.1.02
// Features:
// Simple to use knockback: one function call, configurable behavior (16 possible versions), supports diffrent collision sizes
// callback function, can destroy knockbacks any time, system is for ground units only
// *****************************
// Requires:
// library IsDestructableTree * by BPower:
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isdestructabletree-248054/
// library TerrainPathability * by Rising_Dusk
// http://www.wc3c.net/showthread.php?t=103862
// GUI Unit Event v2.2.1.0 * by Bribe
// http://www.hiveworkshop.com/threads/gui-unit-event-v2-2-1-0.201641/
// *****************************
// How to use:
// function KnockBack takes:
// (code) onHitCode = code that will be executed when unit bounce/hits other unit/obstacles (structure, cliff, doodad, tree)
// set to "null" if not needed, (onHitCode is executed as trigger action)
// (unit) u = knockbacked unit
// (real) distance = how far unit will be moved
// (real) duration = how long knockback exists [sec]
// (real) angle = knockback angle [in radians!]
// (boolean) interruptSpells = if "true" it's interrupts orders/channeling spells on unit u, when knockback starts
// (boolean) killTrees = if "true" unit(s) will destroy trees if unit travels with >minimum speed, kb_minSpeedToKillTree
// (boolean) disableMovement = if "true" unit won't be able to move during knockback, it's using SetUnitPropWindow
// technique, so if you already using this in your map it may interfere. In this case set this boolean to false.
// (integer) kbType see table below, avaiable 4 types: KB_TYPE_NO_BOUNCE ,
// KB_TYPE_SLIDE , KB_TYPE_STOP_ON_OBSTACLES , KB_TYPE_NORMAL
// (integer) bounceTypeFiltr see table below, avaiable 4 filters: KB_FILTR_BOUNCE_NONE ,
// KB_FILTR_BOUNCE_ENEMY_ONLY , KB_FILTR_BOUNCE_ALLIED_ONLY , KB_FILTR_BOUNCE_ALL
// (string) effects - special effect played on unit(s) every given distance (variable effectOccursDistance),
// if null, no effect will be played
// (string) effectAttachPoint - atachment point for special effect, example: "origin"
// (real) effectOccursDistance - special effect declared above will be played every [effectOccursDistance] distance
// *****************************
// (code) onHitCode example:
// function OnHitExample takes nothing returns nothing
// avaiable global variables inside this callback function, read-only:
// (unit) kb_unit = main unit connected with this function
// (unit) kb_obstacle_unit = unit who was hit by kb_unit (if a unit was hit)
// (integer) kb_hitCount = counts all hits done by kb_unit
// (integer) kb_callback use to determine 2 'events' type: KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
// KB_HIT_THE_WALL = structure or doodad or cliff or tree, if KB_HIT_OBSTACLE_UNIT it refers to kb_obstacle_unit
// don't use "waits" here
// if kb_callback == KB_HIT_OBSTACLE_UNIT and kb_hitCount == 1 then
// call DisplayTextToForce(GetPlayersAll(), GetUnitName(kb_unit) + " hits the " + GetUnitName(kb_obstacle_unit))
// endif
// endfunction
// *****************************
// knockback types / bounce types description
// kbType: KB_TYPE_NO_BOUNCE: stops on obstacles /structures/cliffs/doodads
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore alied units pathing on my way, stops on first enemy unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore enemy units pathing on my way, stops on first allied unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// stops on first unit found on my way
// kb_type: KB_TYPE_SLIDE: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way, don't bounce me from other units, and *don't bounce any units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- enemies only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- allied only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// ignore all units pathing on my way, don't bounce me from other units, but bounce all units on my way
// --> if 2 knockbacked units meet, KB_TYPE_SLIDE has priority and it *may* overwrite existing knockback <---
// --> if KB_TYPE_SLIDE will bounce other units it will be done with angle 90 degrees <--
// kb_type: KB_TYPE_STOP_ON_OBSTACLES: don't bounce me on obstacles structures/cliffs/doodads/trees and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
// kb_type: KB_TYPE_NORMAL: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
//
private function KB_FilterOutAbility takes integer abiId returns nothing
set kb_filterOutAbiInteger = kb_filterOutAbiInteger + 1
set kb_filterOutAbility[kb_filterOutAbiInteger] = abiId
endfunction
//*****************************************************************
// **************** BEGIN OF USER CONFIGURABLES **********************
private function KB_AdvancedUserConfigurables takes nothing returns nothing
// these will apply >globaly< to knockback system:
set kb_minSpeedToKillTree = 500.00 // minimum unit speed that allow to destroy tree, only positive values allowed
set kb_reduceSpeedOnHitFactor = 20.00 // allowed range: 0 ... 100
// (very high values of kb_reduceSpeedOnHitFactor may look ugly and cause unit to stop instantly)
// following settings will filter out units with abilities "ghost" "ghosts visible" and with buff "windwalk"
// so units with these abilities will not be bounced, as they already can be walked over
call KB_FilterOutAbility('Agho')
call KB_FilterOutAbility('Aeth')
call KB_FilterOutAbility('BOwk')
// you can add more abilities/buffs here like 3 above
// call KB_FilterOutAbility('YourAbiId')
endfunction
// **************** END OF USER CONFIGURABLES ************************
//*****************************************************************
globals
group kb_ug
real array kb_distance
real array kb_time
real array kb_angle
real array kb_d1
real array kb_d2
real array kb_sin
real array kb_cos
boolean array kb_killTrees
boolean array kb_interruptSpells
boolean array kb_disableMovement
integer array kb_knockbackType
integer array kb_bounceFiltrType
string array kb_effect
string array kb_effectAttachPoint
real array kb_effectOccursDistance
real array kb_lastDistanceEffect
real array kb_collision
real array kb_distanceLeft
real array kb_timeLeft
trigger array kb_trg_onHit
trigger kb_trg_loop
integer array kb_hitCounter // for callback function
integer kb_hitCount = 0 // for callback function
unit kb_unit = null // for callback function
unit kb_obstacle_unit = null // for callback function
player kb_player
rect kb_rect
group kb_group
timer kb_timer
real kb_minSpeedToKillTree = 500.00
real kb_reduceSpeedOnHitFactor = 10.00
real kb_reduceSpeedOnHit = 1.00
constant real KB_INTERVAL = 0.03125
constant real KB_STEPS_PER_SEC = 1.00 / KB_INTERVAL
constant real KB_PI_DIVIDED_BY_2 = bj_PI / 2.00
constant real KB_PI_MULTIPLIED_BY_2 = bj_PI * 2.00
boolexpr array kb_filtr
constant integer KB_FILTR_BOUNCE_NONE = 0
constant integer KB_FILTR_BOUNCE_ENEMY_ONLY = 2
constant integer KB_FILTR_BOUNCE_ALLIED_ONLY = 3
constant integer KB_FILTR_BOUNCE_ALL = 4
constant integer KB_TYPE_NO_BOUNCE = 0
constant integer KB_TYPE_SLIDE = 10
constant integer KB_TYPE_STOP_ON_OBSTACLES = 20
constant integer KB_TYPE_NORMAL = 30
constant integer KB_HIT_OBSTACLE_UNIT = 101
constant integer KB_HIT_THE_WALL = 102
integer kb_callback
integer kb_filterOutAbiInteger = 0
integer array kb_filterOutAbility
endglobals
//-----------------------------------------------------------
//x, y, = centerX, centerY
//-----------------------------------------------------------
function KB_IsPointWalkableForSize takes real x, real y, real whatSize returns boolean //x, y, = centerX, centerY
if whatSize <= 16.00 then //one item check:
return IsTerrainWalkable(x, y)
endif // for larger units: 5 item checks
return IsTerrainWalkable(x, y) and IsTerrainWalkable(x+32.00, y) and IsTerrainWalkable(x-32.00, y) and IsTerrainWalkable(x, y+32.00) and IsTerrainWalkable(x, y-32.00)
endfunction
//------------------------------------------------------------
//------------------------------------------------------------
function KB_GetBounceAngle takes real angle, real x, real y, integer id returns real
local real newAngleVer1 = ( - angle )
local real newAngleVer2 = ( - bj_PI - angle ) // for negative angles
if angle>0.00 then
set newAngleVer2 = (bj_PI - angle)
endif
//++++
if KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer1), y+kb_d1[id] * Sin(newAngleVer1), kb_collision[id]) then
return newAngleVer1
elseif KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer2), y+kb_d1[id] * Sin(newAngleVer2), kb_collision[id]) then
return newAngleVer2
else //3rd version - reverse direction:
set newAngleVer1 = (angle + bj_PI) // for negative angle
if angle>0.00 then
set newAngleVer1 = (angle - bj_PI)
endif
endif
return newAngleVer1
endfunction
//------------------------------------------------------------
//-----------------------------x/y = obstacleUnit X/Y
function KB_GetBounceAngle90 takes real angle, real x, real y, real unitX, real unitY returns real
local real newAnglePlus = ModuloReal((angle + KB_PI_DIVIDED_BY_2), KB_PI_MULTIPLIED_BY_2) // +90
local real newAngleMinus = ModuloReal((angle - KB_PI_DIVIDED_BY_2), KB_PI_MULTIPLIED_BY_2) // -90
local real x1 = x+32.00 * Cos(newAnglePlus)
local real y1 = y+32.00 * Sin(newAnglePlus)
local real x2 = x+32.00 * Cos(newAngleMinus)
local real y2 = y+32.00 * Sin(newAngleMinus)
//compare distances
if ((x1-unitX) * (x1-unitX) + (y1-unitY) * (y1-unitY)) > ((x2-unitX) * (x2-unitX) + (y2-unitY) * (y2-unitY)) then
return newAnglePlus
endif
return newAngleMinus
endfunction
//-------------------------------------------------------------------------------
//-------------Zwiebelchen:-------------------------------------------
// The difference between GroupEnumUnitsInRange and IsUnitInRangeXY is, that the former will only enumerate units
// whose coordinate center is inside the radius.
// The IsUnitInRange natives will already return true if only a part of the unit is inside the radius.
//--------------------------------------------------------
// KB_GetUnitCollisionSize checks up to 64 unit collision size // returns values from 8 ..to.. 64
//--------------------------------------------------------
function KB_GetUnitCollisionSize takes unit u, real unitX, real unitY returns real
local real i=0.00
local real size=64.00
loop
exitwhen i==64.00
if not IsUnitInRangeXY(u, (unitX+i), unitY, 0.00) then
set size = (i-1.00)
exitwhen true
endif
set i=i+1.00
endloop
if size<8.00 then
return 8.00
endif
return size
endfunction
//------------------------------------------------------------
function IsUnitAlive takes unit u returns boolean
return not (GetUnitTypeId(u) == 0 or IsUnitType(u, UNIT_TYPE_DEAD))
endfunction
//------------------------------------------------------------
// DisableUnitMovement /* v1.0.0.1
// * Disables unit movement. They can still turn, but will stay in
// * place. It simulates an ensnare-like effect, except that it will
// * not ground units, it does not have buffs, does not interrupt
// * channeled casts and appears to have no downsides.
// * Full credits to WaterKnight for discovering this technique.
//--------------------------------------------------------------------
function DisableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, 0)
endfunction
//--------------------------------------------------------------------
function EnableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u) * bj_DEGTORAD)
endfunction
//------------------------------------------------------------
function KBFiltr_1 takes unit u returns boolean
local integer a=1
local integer id = GetUnitUserData(u)
local boolean isKb = IsUnitInGroup(u, kb_group) // is knockbacked?
if IsUnitAlive(u) and (not IsUnitType(u, UNIT_TYPE_FLYING)) and (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsUnitVisible(u, kb_player)) then
if not (isKb and ((kb_knockbackType[id]==KB_TYPE_SLIDE) or (kb_knockbackType[id]==KB_TYPE_NO_BOUNCE and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_NONE))) then
if not (isKb and ((IsUnitAlly(u, kb_player) and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_ENEMY_ONLY) or (IsUnitEnemy(u, kb_player) and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_ALLIED_ONLY))) then
loop
exitwhen a>kb_filterOutAbiInteger
if GetUnitAbilityLevel(u, kb_filterOutAbility[a]) > 0 then
return false
endif
set a=a+1
endloop
return true
endif
endif
endif
return false
endfunction
//--------
function KBFiltr_groundNotStruc takes nothing returns boolean
return KBFiltr_1(GetFilterUnit())
endfunction
// set kb_player before confirm this 2 checks!
function KBFiltr_groundNotStrucAlliedOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitAlly(GetFilterUnit(), kb_player)
endfunction
function KBFiltr_groundNotStrucEnemyOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), kb_player)
endfunction
//------------------------------------------------------------------------
function KB_OnRemoveFromGame takes nothing returns nothing
call GroupRemoveUnit(kb_group, udg_UDexUnits[udg_UDex])
//clean:
if kb_trg_onHit[udg_UDex] != null then
call DestroyTrigger(kb_trg_onHit[udg_UDex])
set kb_trg_onHit[udg_UDex] = null
endif
set kb_effect[udg_UDex] = null
set kb_effectAttachPoint[udg_UDex] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//----------------------------------------------------------------------------
function KB_DestroyKnockbackOnUnit takes unit u returns nothing
local integer id = GetUnitUserData(u)
call GroupRemoveUnit(kb_group, u)
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
call EnableUnitMovement(u)
set kb_effect[id] = null
set kb_effectAttachPoint[id] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//----------------------------------------------------------------------------
function KB_DestroyAllEnum takes nothing returns nothing
call KB_DestroyKnockbackOnUnit(GetEnumUnit())
endfunction
//----------------------------
function KB_DestroyAllKnockbacks takes nothing returns nothing
call ForGroup(kb_group, function KB_DestroyAllEnum)
endfunction
//----------------------------
//=======================================================
function KB_CheckAllowedTypes takes integer kbType, integer bounceTypeFiltr returns boolean
if kbType == KB_TYPE_NO_BOUNCE or kbType == KB_TYPE_SLIDE or kbType == KB_TYPE_STOP_ON_OBSTACLES or kbType == KB_TYPE_NORMAL then
if bounceTypeFiltr == KB_FILTR_BOUNCE_NONE or bounceTypeFiltr == KB_FILTR_BOUNCE_ENEMY_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALLIED_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALL then
return true
endif
endif
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Knockback error: wrong integer pass to function")
return false
endfunction
//-----------------------------------------------------------------------------------------
//-----main function --------------------------------------------------------------------
function KnockBack takes code onHitCode, unit u, real distance, real duration, real angle, boolean interruptSpells, boolean killTrees, boolean disableMovement, integer kbType, integer bounceTypeFiltr, string effects, string effectAttachPoint, real effectOccursDistance returns nothing
local integer id = GetUnitUserData(u)
local integer q = R2I(duration / KB_INTERVAL) // (Number of steps)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
if GetUnitDefaultMoveSpeed(u) == 0.00 or (not KB_CheckAllowedTypes(kbType, bounceTypeFiltr)) then //protection
return
endif
set kb_effect[id] = null
set kb_effectAttachPoint[id] = null
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
if onHitCode != null then
set kb_trg_onHit[id] = CreateTrigger()
call TriggerAddAction(kb_trg_onHit[id], onHitCode)
endif
set kb_distance[id] = distance
set kb_distanceLeft[id] = distance
set kb_time[id] = duration
set kb_timeLeft[id] = duration
set kb_hitCounter[id] = 0
set kb_angle[id] = angle
set kb_sin[id] = Sin(angle)
set kb_cos[id] = Cos(angle)
set kb_d1[id] = 2 * kb_distance[id] / (q + 1) // the "bit" in "moving a unit a bit a time"
set kb_d2[id] = kb_d1[id] / q // will decrease d1 with each execution
set kb_killTrees[id] = killTrees
set kb_interruptSpells[id] = interruptSpells
set kb_disableMovement[id] = disableMovement
set kb_knockbackType[id] = kbType
set kb_bounceFiltrType[id] = bounceTypeFiltr
if kb_interruptSpells[id] then
call SetUnitPosition(u, x, y)
endif
if kb_disableMovement[id] then
call DisableUnitMovement(u)
endif
if effects != null then
set kb_effect[id] = effects
endif
set kb_effectAttachPoint[id] = effectAttachPoint
set kb_lastDistanceEffect[id] = kb_distance[id]
set kb_effectOccursDistance[id] = effectOccursDistance
set kb_collision[id] = KB_GetUnitCollisionSize(u, x, y)
if FirstOfGroup(kb_group)==null then
call TimerStart(kb_timer, KB_INTERVAL, true, null)
endif
call GroupAddUnit(kb_group, u)
endfunction
// KBLOOP part =============================================
function KB_BounceEvent takes unit u, integer id, integer eventType returns nothing
if kb_trg_onHit[id] != null then
set kb_callback = eventType // KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
set kb_unit = u
set kb_hitCounter[id] = kb_hitCounter[id] + 1
set kb_hitCount = kb_hitCounter[id]
call TriggerExecute(kb_trg_onHit[id])
endif
endfunction
//------------------------------------------------------------------------------------------------
// use this for bounce from structures/cliffs/doodads ONLY:
//------------------------------------------------------------------------------------------------
function KB_BounceUnit takes unit u, integer id, real unitX, real unitY returns nothing
set kb_angle[id] = KB_GetBounceAngle(kb_angle[id], unitX, unitY, id) //change angle
set kb_sin[id] = Sin(kb_angle[id])
set kb_cos[id] = Cos(kb_angle[id])
call SetUnitX(u, unitX + kb_d1[id] * kb_cos[id])
call SetUnitY(u, unitY + kb_d1[id] * kb_sin[id])
set kb_d1[id] = kb_d1[id] * kb_reduceSpeedOnHit // reducing distance kb_d1
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event
endfunction
//------------------------------------------------------------------------------------------------
//-this for better visual effect --
//-KB_BounceEvent is not included here inside this func -----------
function KB_BounceUnitReverseWithMinSpeed takes unit u, integer id returns nothing
local real angle = (kb_angle[id] + bj_PI)
if kb_angle[id]>0.00 then // reverse direction:
set angle = (kb_angle[id] - bj_PI)
endif // initiate new kb, so old one will be ended:
call KnockBack(null, u, 25.00, 0.30, angle, false, kb_killTrees[id], kb_disableMovement[id], KB_TYPE_SLIDE, KB_FILTR_BOUNCE_NONE, null, null, 1.00)
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_TwoUnitsHit takes unit mainUnit, integer id, unit obstacleUnit, integer obstacleId returns nothing
local integer kb1 = kb_knockbackType[id]
local integer kb2
if IsUnitInGroup(obstacleUnit, kb_group) then
set kb2 = kb_knockbackType[obstacleId]
if kb2 == KB_TYPE_NO_BOUNCE then
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
if kb2 == kb1 then // both "no-bounce"
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
call KB_BounceUnitReverseWithMinSpeed(obstacleUnit, obstacleId)
endif
elseif (kb2 == KB_TYPE_NORMAL or kb2 == KB_TYPE_STOP_ON_OBSTACLES) and (kb1 == KB_TYPE_NORMAL or kb1 == KB_TYPE_STOP_ON_OBSTACLES or kb1 == KB_TYPE_SLIDE) then
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
endif
else // obstacle unit *not* in kb_group:
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
if kb1 == KB_TYPE_NO_BOUNCE then
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
endif
endif
endfunction
//-------------------------------------------------------------------------------
// kb_u primary unit, unitX = GetUnitX(kb_u)
// bouncePrimaryUnit - if "true" will bounce kb_u in opposite direction to new unit on way
// bounceNewUnit -if "true" will bounce new unit in opposite direction to kb_u unit
function KB_CheckUnitsAroundAndBounce takes unit kb_u, integer kb_id, real unitX, real unitY, real targetX, real targetY, integer filtrNr, boolean bouncePrimaryUnit, boolean bounceNewUnit returns boolean
local unit obst_u
local integer obst_id
local real obstX
local real obstY
local integer foundUnits=0
local real angle
local integer t
local integer b
local real Tx
local real Ty
local real c
set kb_player = GetOwningPlayer(kb_u)
call GroupEnumUnitsInRange(kb_ug, targetX, targetY, 150.00, kb_filtr[filtrNr]) // alive+ground+not structure
loop
set obst_u = FirstOfGroup(kb_ug)
exitwhen obst_u == null
set obstX = GetUnitX(obst_u)
set obstY = GetUnitY(obst_u)
set c = KB_GetUnitCollisionSize(obst_u, obstX, obstY)
if (obst_u != kb_u) and IsUnitInRange(obst_u, kb_u, RMaxBJ(c, kb_collision[kb_id])) then //pick bigger collision
set foundUnits = 1
set obst_id = GetUnitUserData(obst_u)
call KB_TwoUnitsHit(kb_u, kb_id, obst_u, obst_id)
// if bounce primary unit:
if bouncePrimaryUnit then
set angle = Atan2(unitY - obstY, unitX - obstX) // reverse angle
set Tx = unitX+kb_d1[kb_id] * Cos(angle)
set Ty = unitY+kb_d1[kb_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, kb_collision[kb_id]) then
call SetUnitX(kb_u, Tx)
call SetUnitY(kb_u, Ty)
set kb_angle[kb_id] = angle //change angle
set kb_sin[kb_id] = Sin(kb_angle[kb_id])
set kb_cos[kb_id] = Cos(kb_angle[kb_id])
set kb_d1[kb_id] = kb_d1[kb_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
else // not walkable:
set kb_d1[kb_id] = 0.00 // end knockback
endif
elseif kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // if not bounce, then move [for slide type only]:
call SetUnitX(kb_u, targetX)
call SetUnitY(kb_u, targetY)
endif
// if bounce obstacle unit:
if bounceNewUnit then
set angle = Atan2(obstY - unitY, obstX - unitX) // reverse angle
if IsUnitInGroup(obst_u, kb_group) then
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // hit by sliding unit, initiate new slide, slide overwrites existing one
if kb_timeLeft[kb_id] * kb_distanceLeft[kb_id] > 1.00 then //don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if kb_bounceFiltrType[kb_id] == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, kb_distanceLeft[kb_id], kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectAttachPoint[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
else // hit by non-sliding unit, update existing kb:
set Tx = obstX+kb_d1[obst_id] * Cos(angle)
set Ty = obstY+kb_d1[obst_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, c) then
call SetUnitX(obst_u, Tx)
call SetUnitY(obst_u, Ty)
set kb_angle[obst_id] = angle //change angle
set kb_sin[obst_id] = Sin(kb_angle[obst_id])
set kb_cos[obst_id] = Cos(kb_angle[obst_id])
set kb_d1[obst_id] = kb_d1[obst_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
endif
endif
elseif (kb_timeLeft[kb_id] * kb_distanceLeft[kb_id]) > 1.00 then //unit NOT in kb_group, initial new kb; don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if b == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // if obstacle hits by sliding unit, change angle to 90
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
endif
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, kb_distanceLeft[kb_id], kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectAttachPoint[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
endif
//bounce obstacle unit:end
endif
call GroupRemoveUnit(kb_ug, obst_u)
endloop
call GroupClear (kb_ug)
set obst_u=null
return foundUnits > 0
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_MoveUnit takes unit u, integer id, real unitX, real unitY, real targetX, real targetY returns nothing
local boolean isWalkable = KB_IsPointWalkableForSize(targetX, targetY, kb_collision[id])
if kb_knockbackType[id] == KB_TYPE_NO_BOUNCE then // no bounce at all
if not isWalkable then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
elseif kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then // walkable:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
elseif (not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, false)) then //3 other filters:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
//---------------------------------SLIDE:-------------------------------------------------
elseif kb_knockbackType[id] == KB_TYPE_SLIDE then
if not isWalkable then
call KB_BounceUnit(u, id, unitX, unitY) //fires hit event
elseif kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
elseif (not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, true)) then //3 other filters:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
//--------------------------------- KB_TYPE_NORMAL / KB_TYPE_STOP_ON_OBSTACLES -------------------
elseif kb_knockbackType[id] == KB_TYPE_NORMAL or kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
if not isWalkable then
if kb_knockbackType[id] == KB_TYPE_NORMAL then
call KB_BounceUnit(u, id, unitX, unitY)
elseif kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
endif
elseif (kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE) then //walkable:
if (not (KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, KB_FILTR_BOUNCE_ALL, true, false))) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
elseif (not (KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], true, true))) then //3 other filters
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
endif
endfunction
//-----------------------------------------------------------------
function KB_KillTree takes nothing returns nothing
call KillTree(GetEnumDestructable())
endfunction
//------------------------------------------------------------------
function KB_ShowEffect takes unit u, integer id returns nothing
if kb_effect[id] != null and (kb_distanceLeft[id] < (kb_lastDistanceEffect[id] - kb_effectOccursDistance[id])) then
call DestroyEffect(AddSpecialEffectTarget(kb_effect[id], u, kb_effectAttachPoint[id]))
set kb_lastDistanceEffect[id] = kb_distanceLeft[id]
endif
endfunction
//------------------------------------------------------------------
function KB_LoopEnum takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetUnitUserData(u)
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local real targetX = unitX + kb_d1[id] * kb_cos[id]
local real targetY = unitY + kb_d1[id] * kb_sin[id]
if (not IsUnitAlive(u)) or kb_d1[id] <= 0.00 then
call KB_DestroyKnockbackOnUnit(u)
set u=null
return
endif
if kb_killTrees[id] and ((kb_d1[id]*KB_STEPS_PER_SEC) > kb_minSpeedToKillTree) then
call MoveRectTo(kb_rect, targetX, targetY)
call EnumDestructablesInRect(kb_rect, null, function KB_KillTree)
endif
call KB_ShowEffect(u, id)
call KB_MoveUnit(u, id, unitX, unitY, targetX, targetY)
set kb_d1[id] = kb_d1[id] - kb_d2[id] // decrease speed
set kb_distanceLeft[id] = kb_distanceLeft[id] - kb_d1[id] // for additional kb-ed units
set kb_timeLeft[id] = kb_timeLeft[id] - KB_INTERVAL // for additional kb-ed units
set u=null
endfunction
//--------------------------------------------------------------------------
function KB_Loop takes nothing returns boolean
call ForGroup(kb_group, function KB_LoopEnum)
return false
endfunction
//=======================================================
//=======================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
set kb_ug = CreateGroup()
set kb_group = CreateGroup()
set kb_timer = CreateTimer()
set kb_rect = Rect(0.0, 0.0, 256.0, 256.0)
set kb_trg_loop = CreateTrigger()
call TriggerRegisterTimerExpireEvent(kb_trg_loop, kb_timer) //timer kb_timer expires
call TriggerAddCondition(kb_trg_loop, Condition(function KB_Loop))
//----------------------------------------------------
call KB_AdvancedUserConfigurables()
set kb_minSpeedToKillTree = RMaxBJ(kb_minSpeedToKillTree, 0.00)
if kb_reduceSpeedOnHitFactor < 0.00 then
set kb_reduceSpeedOnHitFactor = 0.00
elseif kb_reduceSpeedOnHitFactor > 100.00 then
set kb_reduceSpeedOnHitFactor = 100.00
endif
set kb_reduceSpeedOnHit = 1.00 - (kb_reduceSpeedOnHitFactor / 100.00) // for kb_d1, range: 0.00 ... 1.00
//----------------------------------------------------
set kb_filtr[KB_FILTR_BOUNCE_NONE] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALL] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALLIED_ONLY] = Condition(function KBFiltr_groundNotStrucAlliedOnly)
set kb_filtr[KB_FILTR_BOUNCE_ENEMY_ONLY] = Condition(function KBFiltr_groundNotStrucEnemyOnly)
call TriggerRegisterVariableEvent( t, "udg_UnitIndexEvent", EQUAL, 2.00 ) // remove unit detection
call TriggerAddAction(t, function KB_OnRemoveFromGame)
set t=null
endfunction
endlibrary
library KnockbackSimple initializer Init uses IsDestructableTree, TerrainPathability // by ZibiTheWand3r3r v.1.03
// Features:
// Simple to use knockback: one function call, configurable behavior (16 possible versions), supports diffrent collision sizes
// callback function, can destroy knockbacks any time, system is for ground units only
// *****************************
// Requires:
// library IsDestructableTree * by BPower:
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isdestructabletree-248054/
// library TerrainPathability * by Rising_Dusk
// http://www.wc3c.net/showthread.php?t=103862
// GUI Unit Event v2.2.1.0 * by Bribe
// http://www.hiveworkshop.com/threads/gui-unit-event-v2-2-1-0.201641/
// *****************************
// How to use:
// function KnockBack takes:
// (code) onHitCode = code that will be executed when unit bounce/hits other unit/obstacles (structure, cliff, doodad, tree)
// set to "null" if not needed, (onHitCode is executed as trigger action)
// (unit) u = knockbacked unit
// (real) distance = how far unit will be moved
// (real) duration = how long knockback exists [sec]
// (real) angle = knockback angle [in radians!]
// (boolean) interruptSpells = if "true" it's interrupts orders/channeling spells on unit u, when knockback starts
// (boolean) killTrees = if "true" unit(s) will destroy trees if unit travels with >minimum speed, kb_minSpeedToKillTree
// (boolean) disableMovement = if "true" unit won't be able to move during knockback, it's using SetUnitPropWindow
// technique, so if you already using this in your map it may interfere. In this case set this boolean to false.
// (integer) kbType see table below, avaiable 4 types: KB_TYPE_NO_BOUNCE ,
// KB_TYPE_SLIDE , KB_TYPE_STOP_ON_OBSTACLES , KB_TYPE_NORMAL
// (integer) bounceTypeFiltr see table below, avaiable 4 filters: KB_FILTR_BOUNCE_NONE ,
// KB_FILTR_BOUNCE_ENEMY_ONLY , KB_FILTR_BOUNCE_ALLIED_ONLY , KB_FILTR_BOUNCE_ALL
// (string) effects - special effect played on unit(s) every given distance (variable effectOccursDistance),
// if null, no effect will be played
// (string) effectAttachPoint - atachment point for special effect, example: "origin"
// (real) effectOccursDistance - special effect declared above will be played every [effectOccursDistance] distance
// *****************************
// (code) onHitCode example:
// function OnHitExample takes nothing returns nothing
// avaiable global variables inside this callback function, read-only:
// (unit) kb_unit = main unit connected with this function
// (unit) kb_obstacle_unit = unit who was hit by kb_unit (if a unit was hit)
// (integer) kb_hitCount = counts all hits done by kb_unit
// (integer) kb_callback use to determine 2 'events' type: KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
// KB_HIT_THE_WALL = structure or doodad or cliff or tree, if KB_HIT_OBSTACLE_UNIT it refers to kb_obstacle_unit
// don't use "waits" here
// if kb_callback == KB_HIT_OBSTACLE_UNIT and kb_hitCount == 1 then
// call DisplayTextToForce(GetPlayersAll(), GetUnitName(kb_unit) + " hits the " + GetUnitName(kb_obstacle_unit))
// endif
// endfunction
// *****************************
// knockback types / bounce types description
// kbType: KB_TYPE_NO_BOUNCE: stops on obstacles /structures/cliffs/doodads
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore alied units pathing on my way, stops on first enemy unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore enemy units pathing on my way, stops on first allied unit on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// stops on first unit found on my way
// kb_type: KB_TYPE_SLIDE: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed when unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// ignore all units pathing on my way, don't bounce me from other units, and *don't bounce any units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- enemies only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore all units pathing on my way, don't bounce me from other units, but bounce unit on my way- allied only
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// ignore all units pathing on my way, don't bounce me from other units, but bounce all units on my way
// --> if 2 knockbacked units meet, KB_TYPE_SLIDE has priority and it *may* overwrite existing knockback <---
// --> if KB_TYPE_SLIDE will bounce other units it will be done with angle 90 degrees <--
// kb_type: KB_TYPE_STOP_ON_OBSTACLES: don't bounce me on obstacles structures/cliffs/doodads/trees and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
// kb_type: KB_TYPE_NORMAL: bounce me on obstacles and:
// if code "onHitCode" is specifed then it will be executed if unit hits obstacle/unit
// kb_bounceFiltrType: KB_FILTR_BOUNCE_NONE:
// bounce me on any other unit, and don't bounce units on my way (allied and enemy)
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ENEMY_ONLY:
// ignore only allies pathing on my way, and bounce me and enemy units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALLIED_ONLY:
// ignore only enemies pathing on my way, and bounce me and allied units on my way
// kb_bounceFiltrType: KB_FILTR_BOUNCE_ALL:
// bounce me on other unit, and bounce all units on my way (allied and enemy)
//
private function KB_FilterOutAbility takes integer abiId returns nothing
set kb_filterOutAbiInteger = kb_filterOutAbiInteger + 1
set kb_filterOutAbility[kb_filterOutAbiInteger] = abiId
endfunction
//*****************************************************************
// **************** BEGIN OF USER CONFIGURABLES **********************
private function KB_AdvancedUserConfigurables takes nothing returns nothing
// these will apply >globaly< to knockback system:
set kb_minSpeedToKillTree = 500.00 // minimum unit speed that allow to destroy tree, only positive values allowed
set kb_reduceSpeedOnHitFactor = 20.00 // allowed range: 0 ... 100
// (very high values of kb_reduceSpeedOnHitFactor may look ugly and cause unit to stop instantly)
// following settings will filter out units with abilities "ghost" "ghosts visible" and with buff "windwalk"
// so units with these abilities will not be bounced, as they already can be walked over
call KB_FilterOutAbility('Agho')
call KB_FilterOutAbility('Aeth')
call KB_FilterOutAbility('BOwk')
// you can add more abilities/buffs here like 3 above
// call KB_FilterOutAbility('YourAbiId')
endfunction
// **************** END OF USER CONFIGURABLES ************************
//*****************************************************************
globals
group kb_ug
real array kb_distance
real array kb_time
real array kb_angle
real array kb_d1
real array kb_d2
real array kb_sin
real array kb_cos
boolean array kb_killTrees
boolean array kb_interruptSpells
boolean array kb_disableMovement
integer array kb_knockbackType
integer array kb_bounceFiltrType
string array kb_effect
string array kb_effectAttachPoint
real array kb_effectOccursDistance
real array kb_lastDistanceEffect
real array kb_collision
real array kb_distanceLeft
real array kb_timeLeft
trigger array kb_trg_onHit
trigger kb_trg_loop
integer array kb_hitCounter // for callback function
integer kb_hitCount = 0 // for callback function
unit kb_unit = null // for callback function
unit kb_obstacle_unit = null // for callback function
player kb_player
rect kb_rect
group kb_group
timer kb_timer
real kb_minSpeedToKillTree = 500.00
real kb_reduceSpeedOnHitFactor = 10.00
real kb_reduceSpeedOnHit = 1.00
constant real KB_DETECT_OBSTACLE_UNIT_RADIUS = 150.00
constant real KB_INTERVAL = 0.03125
constant real KB_STEPS_PER_SEC = 1.00 / KB_INTERVAL
constant real KB_PI_DIVIDED_BY_2 = bj_PI / 2.00
constant real KB_PI_MULTIPLIED_BY_2 = bj_PI * 2.00
boolexpr array kb_filtr
constant integer KB_FILTR_BOUNCE_NONE = 0
constant integer KB_FILTR_BOUNCE_ENEMY_ONLY = 2
constant integer KB_FILTR_BOUNCE_ALLIED_ONLY = 3
constant integer KB_FILTR_BOUNCE_ALL = 4
constant integer KB_TYPE_NO_BOUNCE = 0
constant integer KB_TYPE_SLIDE = 10
constant integer KB_TYPE_STOP_ON_OBSTACLES = 20
constant integer KB_TYPE_NORMAL = 30
constant integer KB_HIT_OBSTACLE_UNIT = 101
constant integer KB_HIT_THE_WALL = 102
integer kb_callback
integer kb_filterOutAbiInteger = 0
integer array kb_filterOutAbility
endglobals
//-----------------------------------------------------------
//x, y, = centerX, centerY
//-----------------------------------------------------------
function KB_IsPointWalkableForSize takes real x, real y, real whatSize returns boolean //x, y, = centerX, centerY
if whatSize <= 16.00 then //one item check:
return IsTerrainWalkable(x, y)
endif // for larger units: 5 item checks
return IsTerrainWalkable(x, y) and IsTerrainWalkable(x+32.00, y) and IsTerrainWalkable(x-32.00, y) and IsTerrainWalkable(x, y+32.00) and IsTerrainWalkable(x, y-32.00)
endfunction
//------------------------------------------------------------
//------------------------------------------------------------
function KB_GetBounceAngle takes real angle, real x, real y, integer id returns real
local real newAngleVer1 = ( - angle )
local real newAngleVer2 = ( - bj_PI - angle ) // for negative angles
if angle>0.00 then
set newAngleVer2 = (bj_PI - angle)
endif
//++++
if KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer1), y+kb_d1[id] * Sin(newAngleVer1), kb_collision[id]) then
return newAngleVer1
elseif KB_IsPointWalkableForSize(x+kb_d1[id] * Cos(newAngleVer2), y+kb_d1[id] * Sin(newAngleVer2), kb_collision[id]) then
return newAngleVer2
else //3rd version - reverse direction:
set newAngleVer1 = (angle + bj_PI) // for negative angle
if angle>0.00 then
set newAngleVer1 = (angle - bj_PI)
endif
endif
return newAngleVer1
endfunction
//------------------------------------------------------------
//-----------------------------x/y = obstacleUnit X/Y
function KB_GetBounceAngle90 takes real angle, real x, real y, real unitX, real unitY returns real
local real newAnglePlus = ModuloReal((angle + KB_PI_DIVIDED_BY_2), KB_PI_MULTIPLIED_BY_2) // +90
local real newAngleMinus = ModuloReal((angle - KB_PI_DIVIDED_BY_2), KB_PI_MULTIPLIED_BY_2) // -90
local real x1 = x+32.00 * Cos(newAnglePlus)
local real y1 = y+32.00 * Sin(newAnglePlus)
local real x2 = x+32.00 * Cos(newAngleMinus)
local real y2 = y+32.00 * Sin(newAngleMinus)
//compare distances
if ((x1-unitX) * (x1-unitX) + (y1-unitY) * (y1-unitY)) > ((x2-unitX) * (x2-unitX) + (y2-unitY) * (y2-unitY)) then
return newAnglePlus
endif
return newAngleMinus
endfunction
//-------------------------------------------------------------------------------
//-------------Zwiebelchen:-------------------------------------------
// The difference between GroupEnumUnitsInRange and IsUnitInRangeXY is, that the former will only enumerate units
// whose coordinate center is inside the radius.
// The IsUnitInRange natives will already return true if only a part of the unit is inside the radius.
//--------------------------------------------------------
// KB_GetUnitCollisionSize checks up to 64 unit collision size // returns values from 8 ..to.. 64
//--------------------------------------------------------
function KB_GetUnitCollisionSize takes unit u, real unitX, real unitY returns real
local real i=0.00
local real size=64.00
loop
exitwhen i==64.00
if not IsUnitInRangeXY(u, (unitX+i), unitY, 0.00) then
set size = (i-1.00)
exitwhen true
endif
set i=i+1.00
endloop
if size<8.00 then
return 8.00
endif
return size
endfunction
//------------------------------------------------------------
function IsUnitAlive takes unit u returns boolean
return not (GetUnitTypeId(u) == 0 or IsUnitType(u, UNIT_TYPE_DEAD))
endfunction
//------------------------------------------------------------
// DisableUnitMovement /* v1.0.0.1
// * Disables unit movement. They can still turn, but will stay in
// * place. It simulates an ensnare-like effect, except that it will
// * not ground units, it does not have buffs, does not interrupt
// * channeled casts and appears to have no downsides.
// * Full credits to WaterKnight for discovering this technique.
//--------------------------------------------------------------------
function DisableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, 0)
endfunction
//--------------------------------------------------------------------
function EnableUnitMovement takes unit u returns nothing
call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u) * bj_DEGTORAD)
endfunction
//------------------------------------------------------------
function KBFiltr_1 takes unit u returns boolean
local integer a=1
local integer id = GetUnitUserData(u)
local boolean isKb = IsUnitInGroup(u, kb_group) // is knockbacked?
if IsUnitAlive(u) and (not IsUnitType(u, UNIT_TYPE_FLYING)) and (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsUnitVisible(u, kb_player)) then
if not (isKb and ((kb_knockbackType[id]==KB_TYPE_SLIDE) or (kb_knockbackType[id]==KB_TYPE_NO_BOUNCE and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_NONE))) then
if not (isKb and ((IsUnitAlly(u, kb_player) and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_ENEMY_ONLY) or (IsUnitEnemy(u, kb_player) and kb_bounceFiltrType[id]==KB_FILTR_BOUNCE_ALLIED_ONLY))) then
loop
exitwhen a>kb_filterOutAbiInteger
if GetUnitAbilityLevel(u, kb_filterOutAbility[a]) > 0 then
return false
endif
set a=a+1
endloop
return true
endif
endif
endif
return false
endfunction
//--------
function KBFiltr_groundNotStruc takes nothing returns boolean
return KBFiltr_1(GetFilterUnit())
endfunction
// set kb_player before confirm this 2 checks!
function KBFiltr_groundNotStrucAlliedOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitAlly(GetFilterUnit(), kb_player)
endfunction
function KBFiltr_groundNotStrucEnemyOnly takes nothing returns boolean
return KBFiltr_1(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), kb_player)
endfunction
//------------------------------------------------------------------------
function KB_OnRemoveFromGame takes nothing returns nothing
call GroupRemoveUnit(kb_group, udg_UDexUnits[udg_UDex])
//clean:
if kb_trg_onHit[udg_UDex] != null then
call DestroyTrigger(kb_trg_onHit[udg_UDex])
set kb_trg_onHit[udg_UDex] = null
endif
set kb_effect[udg_UDex] = null
set kb_effectAttachPoint[udg_UDex] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//----------------------------------------------------------------------------
function KB_DestroyKnockbackOnUnit takes unit u returns nothing
local integer id = GetUnitUserData(u)
call GroupRemoveUnit(kb_group, u)
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
call EnableUnitMovement(u)
set kb_effect[id] = null
set kb_effectAttachPoint[id] = null
if FirstOfGroup(kb_group)==null then
call PauseTimer(kb_timer)
endif
endfunction
//----------------------------------------------------------------------------
function KB_DestroyAllEnum takes nothing returns nothing
call KB_DestroyKnockbackOnUnit(GetEnumUnit())
endfunction
//----------------------------
function KB_DestroyAllKnockbacks takes nothing returns nothing
call ForGroup(kb_group, function KB_DestroyAllEnum)
endfunction
//----------------------------
//=======================================================
function KB_CheckAllowedTypes takes integer kbType, integer bounceTypeFiltr returns boolean
if kbType == KB_TYPE_NO_BOUNCE or kbType == KB_TYPE_SLIDE or kbType == KB_TYPE_STOP_ON_OBSTACLES or kbType == KB_TYPE_NORMAL then
if bounceTypeFiltr == KB_FILTR_BOUNCE_NONE or bounceTypeFiltr == KB_FILTR_BOUNCE_ENEMY_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALLIED_ONLY or bounceTypeFiltr == KB_FILTR_BOUNCE_ALL then
return true
endif
endif
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Knockback error: wrong integer pass to function")
return false
endfunction
//-----------------------------------------------------------------------------------------
//-----main function --------------------------------------------------------------------
function KnockBack takes code onHitCode, unit u, real distance, real duration, real angle, boolean interruptSpells, boolean killTrees, boolean disableMovement, integer kbType, integer bounceTypeFiltr, string effects, string effectAttachPoint, real effectOccursDistance returns nothing
local integer id = GetUnitUserData(u)
local integer q = R2I(duration / KB_INTERVAL) // (Number of steps)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
if GetUnitDefaultMoveSpeed(u) == 0.00 or (not KB_CheckAllowedTypes(kbType, bounceTypeFiltr)) then //protection
return
endif
set kb_effect[id] = null
set kb_effectAttachPoint[id] = null
if kb_trg_onHit[id] != null then
call DestroyTrigger(kb_trg_onHit[id])
set kb_trg_onHit[id] = null
endif
if onHitCode != null then
set kb_trg_onHit[id] = CreateTrigger()
call TriggerAddAction(kb_trg_onHit[id], onHitCode)
endif
set kb_distance[id] = distance
set kb_distanceLeft[id] = distance
set kb_time[id] = duration
set kb_timeLeft[id] = duration
set kb_hitCounter[id] = 0
set kb_angle[id] = angle
set kb_sin[id] = Sin(angle)
set kb_cos[id] = Cos(angle)
set kb_d1[id] = 2 * kb_distance[id] / (q + 1) // the "bit" in "moving a unit a bit a time"
set kb_d2[id] = kb_d1[id] / q // will decrease d1 with each execution
set kb_killTrees[id] = killTrees
set kb_interruptSpells[id] = interruptSpells
set kb_disableMovement[id] = disableMovement
set kb_knockbackType[id] = kbType
set kb_bounceFiltrType[id] = bounceTypeFiltr
if kb_interruptSpells[id] then
call SetUnitPosition(u, x, y)
endif
if kb_disableMovement[id] then
call DisableUnitMovement(u)
endif
if effects != null then
set kb_effect[id] = effects
endif
set kb_effectAttachPoint[id] = effectAttachPoint
set kb_lastDistanceEffect[id] = kb_distance[id]
set kb_effectOccursDistance[id] = effectOccursDistance
set kb_collision[id] = KB_GetUnitCollisionSize(u, x, y)
if FirstOfGroup(kb_group)==null then
call TimerStart(kb_timer, KB_INTERVAL, true, null)
endif
call GroupAddUnit(kb_group, u)
endfunction
// KBLOOP part =============================================
function KB_BounceEvent takes unit u, integer id, integer eventType returns nothing
if kb_trg_onHit[id] != null then
set kb_callback = eventType // KB_HIT_OBSTACLE_UNIT or KB_HIT_THE_WALL
set kb_unit = u
set kb_hitCounter[id] = kb_hitCounter[id] + 1
set kb_hitCount = kb_hitCounter[id]
call TriggerExecute(kb_trg_onHit[id])
endif
endfunction
//------------------------------------------------------------------------------------------------
// use this for bounce from structures/cliffs/doodads ONLY:
//------------------------------------------------------------------------------------------------
function KB_BounceUnit takes unit u, integer id, real unitX, real unitY returns nothing
set kb_angle[id] = KB_GetBounceAngle(kb_angle[id], unitX, unitY, id) //change angle
set kb_sin[id] = Sin(kb_angle[id])
set kb_cos[id] = Cos(kb_angle[id])
call SetUnitX(u, unitX + kb_d1[id] * kb_cos[id])
call SetUnitY(u, unitY + kb_d1[id] * kb_sin[id])
set kb_d1[id] = kb_d1[id] * kb_reduceSpeedOnHit // reducing distance kb_d1
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event
endfunction
//------------------------------------------------------------------------------------------------
//-this for better visual effect --
//-KB_BounceEvent is not included here inside this func -----------
function KB_BounceUnitReverseWithMinSpeed takes unit u, integer id returns nothing
local real angle = (kb_angle[id] + bj_PI)
if kb_angle[id]>0.00 then // reverse direction:
set angle = (kb_angle[id] - bj_PI)
endif // initiate new kb, so old one will be ended:
call KnockBack(null, u, 25.00, 0.30, angle, false, kb_killTrees[id], kb_disableMovement[id], KB_TYPE_SLIDE, KB_FILTR_BOUNCE_NONE, null, null, 1.00)
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_TwoUnitsHit takes unit mainUnit, integer id, unit obstacleUnit, integer obstacleId returns nothing
local integer kb1 = kb_knockbackType[id]
local integer kb2
if IsUnitInGroup(obstacleUnit, kb_group) then
set kb2 = kb_knockbackType[obstacleId]
if kb2 == KB_TYPE_NO_BOUNCE then
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
if kb2 == kb1 then // both "no-bounce"
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
call KB_BounceUnitReverseWithMinSpeed(obstacleUnit, obstacleId)
endif
elseif (kb2 == KB_TYPE_NORMAL or kb2 == KB_TYPE_STOP_ON_OBSTACLES) and (kb1 == KB_TYPE_NORMAL or kb1 == KB_TYPE_STOP_ON_OBSTACLES or kb1 == KB_TYPE_SLIDE) then
set kb_obstacle_unit = mainUnit
call KB_BounceEvent(obstacleUnit, obstacleId, KB_HIT_OBSTACLE_UNIT)
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
endif
else // obstacle unit *not* in kb_group:
set kb_obstacle_unit = obstacleUnit
call KB_BounceEvent(mainUnit, id, KB_HIT_OBSTACLE_UNIT)
if kb1 == KB_TYPE_NO_BOUNCE then
call KB_BounceUnitReverseWithMinSpeed(mainUnit, id)
endif
endif
endfunction
//-------------------------------------------------------------------------------
// kb_u primary unit, unitX = GetUnitX(kb_u)
// bouncePrimaryUnit - if "true" will bounce kb_u in opposite direction to new unit on way
// bounceNewUnit -if "true" will bounce new unit in opposite direction to kb_u unit
function KB_CheckUnitsAroundAndBounce takes unit kb_u, integer kb_id, real unitX, real unitY, real targetX, real targetY, integer filtrNr, boolean bouncePrimaryUnit, boolean bounceNewUnit returns boolean
local unit obst_u
local integer obst_id
local real obstX
local real obstY
local integer foundUnits=0
local real angle
local integer t
local integer b
local real Tx
local real Ty
local real c
local real newDistance
set kb_player = GetOwningPlayer(kb_u)
call GroupEnumUnitsInRange(kb_ug, targetX, targetY, KB_DETECT_OBSTACLE_UNIT_RADIUS, kb_filtr[filtrNr])
loop
set obst_u = FirstOfGroup(kb_ug)
exitwhen obst_u == null
set obstX = GetUnitX(obst_u)
set obstY = GetUnitY(obst_u)
set c = KB_GetUnitCollisionSize(obst_u, obstX, obstY)
if (obst_u != kb_u) and IsUnitInRange(obst_u, kb_u, RMaxBJ(c, kb_collision[kb_id])) then //pick bigger collision
set foundUnits = 1
set obst_id = GetUnitUserData(obst_u)
call KB_TwoUnitsHit(kb_u, kb_id, obst_u, obst_id)
// if bounce primary unit:
if bouncePrimaryUnit then
set angle = Atan2(unitY - obstY, unitX - obstX) // reverse angle
set Tx = unitX+kb_d1[kb_id] * Cos(angle)
set Ty = unitY+kb_d1[kb_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, kb_collision[kb_id]) then
call SetUnitX(kb_u, Tx)
call SetUnitY(kb_u, Ty)
set kb_angle[kb_id] = angle //change angle
set kb_sin[kb_id] = Sin(kb_angle[kb_id])
set kb_cos[kb_id] = Cos(kb_angle[kb_id])
set kb_d1[kb_id] = kb_d1[kb_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
else // not walkable:
set kb_d1[kb_id] = 0.00 // end knockback
endif
elseif kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // if not bounce, then move [for slide type only]:
call SetUnitX(kb_u, targetX)
call SetUnitY(kb_u, targetY)
endif
// if bounce obstacle unit:
if bounceNewUnit then
set angle = Atan2(obstY - unitY, obstX - unitX) // reverse angle
if IsUnitInGroup(obst_u, kb_group) then
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // hit by sliding unit, initiate new slide, slide overwrites existing one
if kb_timeLeft[kb_id] * kb_distanceLeft[kb_id] > 1.00 then //don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if kb_bounceFiltrType[kb_id] == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, kb_distanceLeft[kb_id], kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectAttachPoint[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
else // hit by non-sliding unit, update existing kb:
set Tx = obstX+kb_d1[obst_id] * Cos(angle)
set Ty = obstY+kb_d1[obst_id] * Sin(angle)
if KB_IsPointWalkableForSize(Tx, Ty, c) then
call SetUnitX(obst_u, Tx)
call SetUnitY(obst_u, Ty)
set kb_angle[obst_id] = angle //change angle
set kb_sin[obst_id] = Sin(kb_angle[obst_id])
set kb_cos[obst_id] = Cos(kb_angle[obst_id])
set kb_d1[obst_id] = kb_d1[obst_id] * kb_reduceSpeedOnHit // reducing distance kb_d1
endif
endif
elseif (kb_timeLeft[kb_id] * kb_distanceLeft[kb_id]) > 1.00 then //unit NOT in kb_group, initial new kb; don't run very low knockbacks! <---
set b = kb_bounceFiltrType[kb_id] // new bounce filter:
if b == KB_FILTR_BOUNCE_ENEMY_ONLY then
set b = KB_FILTR_BOUNCE_ALLIED_ONLY
endif
if kb_knockbackType[kb_id] == KB_TYPE_SLIDE then // if obstacle hits by sliding unit, change angle to 90
set angle = KB_GetBounceAngle90(kb_angle[kb_id], obstX, obstY, unitX, unitY) //angle 90 degrees
endif // now calculate new distance to get the same speed as kb_u
set newDistance = (kb_d1[kb_id] * ((I2R(R2I(kb_timeLeft[kb_id] / KB_INTERVAL)) + 1))) / 2.00
if KB_IsPointWalkableForSize((obstX+kb_d1[obst_id] * Cos(angle)), (obstY+kb_d1[obst_id] * Sin(angle)), c) then
call KnockBack(null, obst_u, newDistance, kb_timeLeft[kb_id], angle, kb_interruptSpells[kb_id], kb_killTrees[kb_id], kb_disableMovement[kb_id], kb_knockbackType[kb_id], b, kb_effect[kb_id], kb_effectAttachPoint[kb_id], kb_effectOccursDistance[kb_id])
endif
endif
endif
//bounce obstacle unit:end
endif
call GroupRemoveUnit(kb_ug, obst_u)
endloop
call GroupClear (kb_ug)
set obst_u=null
return foundUnits > 0
endfunction
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
function KB_MoveUnit takes unit u, integer id, real unitX, real unitY, real targetX, real targetY returns nothing
local boolean isWalkable = KB_IsPointWalkableForSize(targetX, targetY, kb_collision[id])
if kb_knockbackType[id] == KB_TYPE_NO_BOUNCE then // no bounce at all
if not isWalkable then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
elseif kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then // walkable:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
elseif (not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, false)) then //3 other filters:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
//---------------------------------SLIDE:-------------------------------------------------
elseif kb_knockbackType[id] == KB_TYPE_SLIDE then
if not isWalkable then
call KB_BounceUnit(u, id, unitX, unitY) //fires hit event
elseif kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
elseif (not KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], false, true)) then //3 other filters:
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
//--------------------------------- KB_TYPE_NORMAL / KB_TYPE_STOP_ON_OBSTACLES -------------------
elseif kb_knockbackType[id] == KB_TYPE_NORMAL or kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
if not isWalkable then
if kb_knockbackType[id] == KB_TYPE_NORMAL then
call KB_BounceUnit(u, id, unitX, unitY)
elseif kb_knockbackType[id] == KB_TYPE_STOP_ON_OBSTACLES then
call KB_BounceEvent(u, id, KB_HIT_THE_WALL) //fire hit event, obstacle
call KB_BounceUnitReverseWithMinSpeed(u, id)
endif
elseif (kb_bounceFiltrType[id] == KB_FILTR_BOUNCE_NONE) then //walkable:
if (not (KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, KB_FILTR_BOUNCE_ALL, true, false))) then
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
elseif (not (KB_CheckUnitsAroundAndBounce(u, id, unitX, unitY, targetX, targetY, kb_bounceFiltrType[id], true, true))) then //3 other filters
call SetUnitX(u, targetX)
call SetUnitY(u, targetY)
endif
endif
endfunction
//-----------------------------------------------------------------
function KB_KillTree takes nothing returns nothing
call KillTree(GetEnumDestructable())
endfunction
//------------------------------------------------------------------
function KB_ShowEffect takes unit u, integer id returns nothing
if kb_effect[id] != null and (kb_distanceLeft[id] < (kb_lastDistanceEffect[id] - kb_effectOccursDistance[id])) then
call DestroyEffect(AddSpecialEffectTarget(kb_effect[id], u, kb_effectAttachPoint[id]))
set kb_lastDistanceEffect[id] = kb_distanceLeft[id]
endif
endfunction
//------------------------------------------------------------------
function KB_LoopEnum takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetUnitUserData(u)
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local real targetX = unitX + kb_d1[id] * kb_cos[id]
local real targetY = unitY + kb_d1[id] * kb_sin[id]
if (not IsUnitAlive(u)) or kb_d1[id] <= 0.00 then
call KB_DestroyKnockbackOnUnit(u)
set u=null
return
endif
if kb_killTrees[id] and ((kb_d1[id]*KB_STEPS_PER_SEC) > kb_minSpeedToKillTree) then
call MoveRectTo(kb_rect, targetX, targetY)
call EnumDestructablesInRect(kb_rect, null, function KB_KillTree)
endif
call KB_ShowEffect(u, id)
call KB_MoveUnit(u, id, unitX, unitY, targetX, targetY)
set kb_d1[id] = kb_d1[id] - kb_d2[id] // decrease speed
set kb_distanceLeft[id] = kb_distanceLeft[id] - kb_d1[id] // for additional kb-ed units
set kb_timeLeft[id] = kb_timeLeft[id] - KB_INTERVAL // for additional kb-ed units
set u=null
endfunction
//--------------------------------------------------------------------------
function KB_Loop takes nothing returns boolean
call ForGroup(kb_group, function KB_LoopEnum)
return false
endfunction
//=======================================================
//=======================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
set kb_ug = CreateGroup()
set kb_group = CreateGroup()
set kb_timer = CreateTimer()
set kb_rect = Rect(0.0, 0.0, 256.0, 256.0)
set kb_trg_loop = CreateTrigger()
call TriggerRegisterTimerExpireEvent(kb_trg_loop, kb_timer) //timer kb_timer expires
call TriggerAddCondition(kb_trg_loop, Condition(function KB_Loop))
//----------------------------------------------------
call KB_AdvancedUserConfigurables()
set kb_minSpeedToKillTree = RMaxBJ(kb_minSpeedToKillTree, 0.00)
if kb_reduceSpeedOnHitFactor < 0.00 then
set kb_reduceSpeedOnHitFactor = 0.00
elseif kb_reduceSpeedOnHitFactor > 100.00 then
set kb_reduceSpeedOnHitFactor = 100.00
endif
set kb_reduceSpeedOnHit = 1.00 - (kb_reduceSpeedOnHitFactor / 100.00) // for kb_d1, range: 0.00 ... 1.00
//----------------------------------------------------
set kb_filtr[KB_FILTR_BOUNCE_NONE] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALL] = Condition(function KBFiltr_groundNotStruc)
set kb_filtr[KB_FILTR_BOUNCE_ALLIED_ONLY] = Condition(function KBFiltr_groundNotStrucAlliedOnly)
set kb_filtr[KB_FILTR_BOUNCE_ENEMY_ONLY] = Condition(function KBFiltr_groundNotStrucEnemyOnly)
call TriggerRegisterVariableEvent( t, "udg_UnitIndexEvent", EQUAL, 2.00 ) // remove unit detection
call TriggerAddAction(t, function KB_OnRemoveFromGame)
set t=null
endfunction
endlibrary