Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
* Charge of Darkness by jim7777
* v1.5
*
* Spell based from the game, Defense of the Ancients
*
* The hero charges towards the target, knockbacking every enemy on its path, then stunning the target if the caster reaches it.
*
* This spell is mostly configurable to suit your needs
* Hope you enjoy it!
*
* Requires:
* GetClosestWidget - Spinnaker
* DummyCaster - Nestharus
* RegisterPlayerUnitEvent - Magtheridon96
* SpellEffectEvent - Bribe
* TimerUtils - Vexorian
* Timer32 - Jesus4Lyf
*
********************************************************************/
scope ChargeOfDarkness
globals
private constant integer ABILITY_ID = 'A000' //ability id of Charge of Darkness ability.
private constant integer FAERIE_ID = 'A001' //ability id of CoD_BuffPlacer ability
private constant integer STUN_ID = 'A002' //ability id of CoD_StunPlacer ability, modify the ability to suit your needs
private constant integer BUFF_ID = 'B000' //buff id of Charge of Darkness buff
private constant integer ANIM_INDEX = 2 //The animation index from the model you'll be using on this spell, applicable if ANIM_TYPE is set to true
//2 is the animation index for Walk of the model Spirit Walker
//Read the Documentation on checking the animation index of a unit.
private constant real DETECTOR_RANGE = 1500 //This value will tell if we are in range of the target and if we are, add the buff to the target
private constant real STUN_RANGE = 100 //how far will we cast the stun? This is already a recommended value >:D
private constant real RADIUS = 400 //range to detect enemies when last target is dead, only applicable if CHANGE_ONDEAD is set to true
private constant real KNOCK_RADIUS = 200 //the radius within the caster that will be affected by knock backs, applicable if KNOCKBACK_PATH is set to true
private constant real KNOCK_DISTANCE = 80 //the distance of the knockback
private constant real KNOCK_DUR = 1.5 //duration of knockback
private constant boolean CHANGE_ONDEAD = true //charge to the nearest target if target is dead? This will detect enemies within RADIUS range, setting this to false will just stop the caster from charging
private constant boolean DISABLE_STOP = true //Should orders stop the spell?
private constant boolean LOOPING_SFX = false //The LOOP_SFX will be created for every instance,if false, LOOP SFX will be created on spell start
private constant boolean ANIM_TYPE = true //true: uses index, false: uses string animations
private constant boolean KNOCKBACK_PATH = true //knockback enemies in its path
private constant string ANIM_STR = "walk" //what animation to be applied on the caster, applicable if ANIM_TYPE is set to false.
//NOTE: I recommend using the INDEX because the native we are using here is quite buggy to some models like the Spirit Walker model
//I advise using the ANIM_INDEX variable instead of this variable if you'd like to see your spell animations smoother.
private constant string TARGET = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl" //the effect that will appear on the target
private constant string LOOP_SFX = "Abilities\\Spells\\Orc\\Shockwave\\ShockwaveMissile.mdl" //the effect that will appear on the caster while charging
private constant string KNOCK_SFX = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl" //the effect that will appear if a unit is knock backed
private constant string ATTACH_POINT = "origin" //attchment point of LOOP_SFX
private hashtable ht = InitHashtable()
private group tmpGroup = null
endglobals
//! textmacro ChargeOfDarkness_OnChargeEnemyPath
//the variable f is our target
//this.caster is the unit who casted ChargeOfDarkness
//this.owner is the player who owns our caster
//modify this thing to suit your needs
//This will only work if KNOCKBACK_PATH is set to true
call DummyCaster[STUN_ID].castTarget(this.owner,1,OrderId("firebolt"),f)
//! endtextmacro
//! textmacro ChargeOfDarkness_IsOnKnockback
//variable u is our target
//it should match with the ChargeOfDarkness_OnChargeEnemyPath macro
//in this example, we check if the unit is stunned.
//modify this thing to suit your needs
//This will only work if KNOCKBACK_PATH is set to true
local boolean b = /*
only modify the following parts to your condition
*/ GetUnitAbilityLevel(u,'BSTN') > 0
//! endtextmacro
private function CoD_Speed takes unit u, real lvl returns real
//total speed is to be multiplied by the number of instances per second.
//there are 33 instances per second
return 15+(2*lvl)
endfunction
private struct SChaser
unit caster
unit target
integer lvl
effect eff
player owner
real speed
real px
real py
group stacks
static if not LOOPING_SFX then
effect lpsfx
endif
private static thistype tmp
private method destroy takes nothing returns nothing
static if not LOOPING_SFX then
call DestroyEffect(this.lpsfx)
endif
static if KNOCKBACK_PATH then
call DestroyGroup(this.stacks)
endif
call RemoveSavedInteger(ht,GetHandleId(this.caster),0)
call SetUnitPathing(this.caster,true)
call SetUnitTimeScale(this.caster,1)
call SetUnitAnimation(this.caster,"stand")
call UnitShareVision(this.target,this.owner,false)
call DestroyEffect(this.eff)
call UnitRemoveAbility(this.target,BUFF_ID)
call this.stopPeriodic()
call this.deallocate()
endmethod
static method Filt takes nothing returns boolean
return GetFilterUnit() != tmp.target and IsUnitEnemy(GetFilterUnit(),tmp.owner) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE) and GetUnitAbilityLevel(GetFilterUnit(),'Aloc') <= 0
endmethod
static if KNOCKBACK_PATH then
static method CheckStack takes nothing returns nothing
local unit u = GetEnumUnit()
//! runtextmacro ChargeOfDarkness_IsOnKnockback()
if not b then
call GroupRemoveUnit(tmp.stacks,u)
endif
set u = null
endmethod
endif
private method periodic takes nothing returns nothing
local real ux = GetUnitX(this.caster)
local real uy = GetUnitY(this.caster)
local real tx = GetUnitX(this.target)
local real ty = GetUnitY(this.target)
local real dx = tx - ux
local real dy = ty - uy
local real dist = SquareRoot(dx * dx + dy * dy)
local real angle = Atan2(dy, dx)
local real x = ux + this.speed * Cos(angle)
local real y = uy + this.speed * Sin(angle)
local string str
local boolean dest = false
local unit f
static if LOOPING_SFX then
call DestroyEffect(AddSpecialEffectTarget(LOOP_SFX,this.caster,ATTACH_POINT))
endif
static if ANIM_TYPE then
call SetUnitAnimationByIndex(this.caster,ANIM_INDEX)
else
call SetUnitAnimation(this.caster,ANIM_STR)
endif
if not IsUnitVisible(this.target,this.owner) and not IsUnitType(this.target,UNIT_TYPE_DEAD) then
call UnitShareVision(this.target,this.owner,true)
endif
static if DISABLE_STOP then
call SaveBoolean(ht,GetHandleId(this.caster),1,true) //we need to do this D:
if not IssueImmediateOrder(this.caster,"holdposition") then //detect if the unit can do any orders, if not, 99% it is stunned or disabled.
call this.destroy()
set dest = true
endif
call RemoveSavedBoolean(ht,GetHandleId(this.caster),1)
else
call IssueImmediateOrder(this.caster,"holdposition")
endif
if IsUnitType(this.target,UNIT_TYPE_DEAD) then
static if CHANGE_ONDEAD then
call UnitRemoveAbility(this.target,BUFF_ID)
call DestroyEffect(this.eff)
set tmp = this
set this.target = GetClosestUnitInRange(tx,ty,RADIUS,Filter(function thistype.Filt))
if this.target == null and not dest then
call IssueImmediateOrder(this.caster,"stop")
call this.destroy()
set dest = true
else
set str = TARGET
if not IsPlayerAlly(GetLocalPlayer(),this.owner) then
set str = ""
endif
set this.eff = AddSpecialEffectTarget(str,this.target,"head")
endif
elseif not dest then
call this.destroy()
set dest = true
endif
elseif (IsUnitType(this.caster,UNIT_TYPE_DEAD) or IsUnitPaused(this.caster)) and not dest then
call this.destroy()
set dest = true
endif
if not IsUnitType(this.target,UNIT_TYPE_DEAD) then
call SetUnitX(this.caster,x)
call SetUnitY(this.caster,y)
call SetUnitFacing(this.caster,angle*bj_RADTODEG)
static if KNOCKBACK_PATH then
set tmp = this
call ForGroup(this.stacks,function thistype.CheckStack)
call GroupEnumUnitsInRange(tmpGroup,x,y,KNOCK_RADIUS,null)
loop
set f = FirstOfGroup(tmpGroup)
exitwhen f == null
call GroupRemoveUnit(tmpGroup,f)
if not HaveSavedInteger(ht,GetHandleId(this.caster),0) and not IsUnitInGroup(f,this.stacks) and f != this.target and IsUnitEnemy(f,this.owner) and not IsUnitType(f,UNIT_TYPE_DEAD) and not IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) and GetUnitAbilityLevel(f,'Aloc') <= 0 then
set angle = Atan2(GetUnitY(f) - GetUnitY(this.caster), GetUnitX(f) - GetUnitX(this.caster))
//call KBS_BeginEx(f,KNOCK_DISTANCE,KNOCK_DUR,angle*bj_RADTODEG,KNOCK_SFX,100,false,true)
//! runtextmacro ChargeOfDarkness_OnChargeEnemyPath()
call GroupAddUnit(this.stacks,f)
endif
endloop
endif
if dist <= DETECTOR_RANGE and GetUnitAbilityLevel(this.target,BUFF_ID) == 0 then
call DummyCaster[FAERIE_ID].castTarget(this.owner,1,OrderId("faeriefire"),this.target)
endif
if dist <= STUN_RANGE then
call DummyCaster[STUN_ID].castTarget(this.owner,this.lvl,OrderId("firebolt"),this.target)
call IssueTargetOrder(this.caster,"attack",this.target)
if not dest then
call this.destroy()
endif
endif
endif
set f = null
endmethod
implement T32x
private static method start takes nothing returns boolean
local thistype this = thistype.allocate()
local string str = TARGET
set this.caster = GetTriggerUnit()
set this.target = GetSpellTargetUnit()
set this.speed = CoD_Speed(this.caster,GetUnitAbilityLevel(this.caster,ABILITY_ID))
set this.owner = GetOwningPlayer(this.caster)
set this.px = GetUnitX(this.caster)
set this.py = GetUnitY(this.caster)
set this.lvl = GetUnitAbilityLevel(this.caster,ABILITY_ID)
static if KNOCKBACK_PATH then
set this.stacks = CreateGroup() //seriously
endif
if not IsPlayerAlly(GetLocalPlayer(),this.owner) then
set str = ""
endif
set this.eff = AddSpecialEffectTarget(str,this.target,"head")
static if not LOOPING_SFX then
set this.lpsfx = AddSpecialEffectTarget(LOOP_SFX,this.caster,ATTACH_POINT)
endif
call SetUnitTimeScale(this.caster,1.2)
call UnitShareVision(this.target,this.owner,true)
call SetUnitPathing(this.caster,false)
call SaveInteger(ht,GetHandleId(this.caster),0,this)
call this.startPeriodic()
return false
endmethod
static if DISABLE_STOP then
static method OnOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local thistype dat = LoadInteger(ht,GetHandleId(u),0)
if not HaveSavedBoolean(ht,GetHandleId(u),1) and dat != 0 then
call dat.destroy()
endif
set u = null
return false
endmethod
endif
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABILITY_ID,function thistype.start)
static if DISABLE_STOP then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER,function thistype.OnOrder)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER,function thistype.OnOrder)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER,function thistype.OnOrder)
endif
call Preload(TARGET)
call Preload(LOOP_SFX)
call Preload(KNOCK_SFX)
static if KNOCKBACK_PATH then //let us save memory!
set tmpGroup = CreateGroup()
endif
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* Get Closest Widget
* by Spinnaker v2.0.0.0
*
* Special thanks to Troll-Brain
*
******************************************************************************
*
* This snippet contains several functions which returns closest
* widget to given coordinates and passed filter.
*
******************************************************************************
*
* Requirements:
* Snippet optionaly requires IsDestructableTree,
* enabling user to choose if enumerated should be all destructables
* or only tree-type ones, therefore gives option to get closest tree.
*
******************************************************************************
*
* Important:
* Performance drop depends on amount of objects currently on the map
* Snippet functions are designed to reduce the search time as much as
* posible, although it's recommended to use non specific functions
* wisely, especialy if your map contains large numbers of widgets.
*
******************************************************************************
*
* Functions:
* function GetClosestItem takes real x, real y, boolexpr filter returns item
* - Gets single item, nearest passed coordinates
* function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* - Returns single item, closest to coordinates in given range
*
* function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
* - Gets single destructable, nearest passed coordinates
* function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
* - Retrieves single destructable, closest to coordinates in given range
*
* function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* - Returns closest unit matching specific filter
* function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* - Gets nearest unit in range matching specific filter
* function GetClosestUnitInGroup takes real x, real y, group g returns unit
* - Retrieves closest unit in given group
*
* function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
* - Returns up to N nearest units in range of passed coordinates
* function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
* - Retrieves up to N closest units in passed group
*
*****************************************************************************/
library GetClosestWidget uses optional IsDestructableTree
private keyword Init
globals
private unit array Q
private real array V
private integer C=0
endglobals
struct ClosestWidget extends array
readonly static destructable cDest=null
readonly static item cItem=null
readonly static real distance=0
readonly static real cX=0
readonly static real cY=0
readonly static unit cUnit=null
static boolean cTree=false
static rect initRect=null
static method resetData takes real x, real y returns nothing
set cDest=null
set distance=100000
set cItem=null
set cUnit=null
set cX=x
set cY=y
endmethod
static method enumItems takes nothing returns nothing
local item i=GetEnumItem()
local real dx=GetWidgetX(i)-cX
local real dy=GetWidgetY(i)-cY
set dx = (dx*dx+dy*dy)/10000.
if dx<distance then
set cItem=i
set distance=dx
endif
set i =null
endmethod
static method enumDestructables takes nothing returns nothing
local destructable d=GetEnumDestructable()
local real dx
local real dy
static if LIBRARY_IsDestructableTree then
if cTree and not IsDestructableTree(d) then
return
endif
endif
set dx=GetWidgetX(d)-cX
set dy=GetWidgetY(d)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cDest=d
set distance=dx
endif
set d=null
endmethod
static method enumUnits takes nothing returns nothing
local unit u=GetEnumUnit()
local real dx=GetUnitX(u)-cX
local real dy=GetUnitY(u)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cUnit=u
set distance=dx
endif
set u=null
endmethod
static method sortUnits takes integer l, integer r returns nothing
local integer i=l
local integer j=r
local real v=V[(l+r)/2]
loop
loop
exitwhen V[i]>=v
set i=i+1
endloop
loop
exitwhen V[j]<=v
set j=j-1
endloop
if i<=j then
set V[0]=V[i]
set V[i]=V[j]
set V[j]=V[0]
set Q[0]=Q[i]
set Q[i]=Q[j]
set Q[j]=Q[0]
set i=i+1
set j=j-1
endif
exitwhen i>j
endloop
if l<j then
call sortUnits(l,j)
endif
if r>i then
call sortUnits(i,r)
endif
endmethod
static method saveGroup takes nothing returns nothing
local real dx
local real dy
set C=C+1
set Q[C]=GetEnumUnit()
set dx=GetUnitX(Q[C])-cX
set dy=GetUnitY(Q[C])-cY
set V[C]=(dx*dx+dy*dy)/10000.
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
set thistype.initRect=Rect(0,0,0,0)
endmethod
endmodule
function GetClosestItem takes real x, real y, boolexpr filter returns item
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call EnumItemsInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumItems)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
exitwhen ClosestWidget.cItem!=null
endif
set r=2*r
endloop
return ClosestWidget.cItem
endfunction
function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
call ClosestWidget.resetData(x,y)
if radius>=0 then
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
endif
return ClosestWidget.cItem
endfunction
function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
local real r=800.
call ClosestWidget.resetData(x,y)
set ClosestWidget.cTree=treeOnly
loop
if r>3200. then
call EnumDestructablesInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumDestructables)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
exitwhen ClosestWidget.cDest!=null
endif
set r=2*r
endloop
return ClosestWidget.cDest
endfunction
function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
call ClosestWidget.resetData(x,y)
if radius>=0 then
set ClosestWidget.cTree=treeOnly
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
endif
return ClosestWidget.cDest
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, filter)
exitwhen true
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, r, filter)
exitwhen FirstOfGroup(bj_lastCreatedGroup)!=null
endif
set r=2*r
endloop
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
endif
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call ClosestWidget.resetData(x,y)
call ForGroup(g, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(g, Q[-n+q])
set n =n-1
endloop
endif
set C=0
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
call ForGroup(sourceGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(destGroup, Q[-n+q])
set n=n-1
endloop
set C=0
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyCaster /* v2.0.0.1
*************************************************************************************
*
* Dummy caster for casting spells
*
* Spells must have 0 cooldown, 92083 range, and cost 0 mana. Spells must be instant and
* can't share the same order as other spells on the dummy caster.
*
*************************************************************************************
*
* */uses/*
* */ optional UnitIndexer, /* hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*
************************************************************************************
* SETTINGS
*/
globals
constant integer UNITS_DUMMY_CASTER = 'h@DC'
/*************************************************************************************
*
* PLAYER_OWNER
*
* Owner of dummy caster
*
*************************************************************************************/
private constant player PLAYER_OWNER = Player(15)
endglobals
/*
************************************************************************************
*
* Dummy at position 32256,32256
*
* struct DummyCaster extends array
*
* method cast takes player castingPlayer, integer abilityLevel, integer order, real x, real y returns boolean
* - call DummyCaster[abilityId].cast(...)
* method castTarget takes player castingPlayer, integer abilityLevel, integer order, widget t returns boolean
* - call DummyCaster[abilityId].castTarget(...)
* method castPoint takes player castingPlayer, integer abilityLevel, integer order, real x, real y returns boolean
* - call DummyCaster[abilityId].castPoint(...)
*
************************************************************************************/
globals
private unit u
endglobals
private module N
private static method onInit takes nothing returns nothing
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled=false
set u=CreateUnit(PLAYER_OWNER,UNITS_DUMMY_CASTER,0,0,0)
set UnitIndexer.enabled=true
else
set u=CreateUnit(PLAYER_OWNER,UNITS_DUMMY_CASTER,0,0,0)
endif
call SetUnitPosition(u,32256,32256)
endmethod
endmodule
struct DummyCaster extends array
implement N
private static method prep takes integer a, player p, integer l returns nothing
call UnitAddAbility(u, a)
if (1 < l) then
call SetUnitAbilityLevel(u, a, l)
endif
if (null != p) then
call SetUnitOwner(u, p, false)
endif
endmethod
private static method finish takes integer a returns nothing
call SetUnitOwner(u, PLAYER_OWNER, false)
call UnitRemoveAbility(u, a)
endmethod
method unit takes nothing returns unit
return u
endmethod
method cast takes player p, integer level, integer order, real x, real y returns boolean
local boolean b
call SetUnitX(u, x)
call SetUnitY(u, y)
call prep(this, p, level)
set b = IssueImmediateOrderById(u,order)
call finish(this)
call SetUnitPosition(u, 32256, 32256)
return b
endmethod
method castTarget takes player p, integer level, integer order, widget t returns boolean
local boolean b
call SetUnitX(u, GetWidgetX(t))
call SetUnitY(u, GetWidgetY(t))
call prep(this, p, level)
set b = IssueTargetOrderById(u,order,t)
call finish(this)
call SetUnitPosition(u, 32256, 32256)
return b
endmethod
method castPoint takes player p, integer level, integer order, real px, real py, real x, real y returns boolean
local boolean b
call SetUnitX(u, px)
call SetUnitY(u, py)
call prep(this, p, level)
set b = IssuePointOrderById(u,order,x,y)
call finish(this)
call SetUnitPosition(u, 32256, 32256)
return b
endmethod
endstruct
endlibrary
//TESH.scrollpos=29
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.0.0.0
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must
* return false. They can return nothing as well. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent
* takes
* playerunitevent whichEvent : The event you would like to register
* code whichFunction : The code you would like to register
* returns
* nothing
*
* - Registers code that will execute when an event fires.
*
* - function RegisterPlayerUnitEventForPlayer
* takes
* playerunitevent whichEvent : The event you would like to register
* code whichFunction : The code you would like to register
* player whichPlayer : The player you would like to register the event for
* returns
* nothing
*
* - Registers code that will execute when an event fires for a certain player.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 260 + 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
endlibrary
//TESH.scrollpos=13
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=454
//TESH.alwaysfold=0
library KBS initializer Init
//***************************************************************************************
//** **
//** Knockback System (KBS) **By kenny!** **
//** v 2.04 **
//** **
//** A system I made that can be used for knockback spells in a variety of **
//** applications in a map. User defined variables make it easy to give the desired **
//** effect for any spell. **
//** **
//** Requires: **
//** - A vJASS preprocessor (NewGen Pack). **
//** - No other systems. **
//** - A dummy unit. One can be found in this map ('u000') **
//** - The special effects found in the import/export section of this map. **
//** They are the "Dust.mdx" and the "SlideWater.mdx". Or you can use your **
//** own if you have some. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Other Information: **
//** - Angle is taken in degrees. But can easily be changed if you know how. **
//** - Units will be stopped if they hit the map borders. **
//** - Remember to import the special effect into your map if you need them. **
//** - There are two functions that can be used in this system. **
//** - NEW FUNCTION: KBS_BeginCommon() is for all the common knockback uses in **
//** your map. It only takes 4 parameters, and use global parameters for Area **
//** AllowMove and CheckPathing. Making it an icredibly easy function to use. **
//** **
//** Sliding Distances: **
//** - This is some general knowledge on the different distances and how to **
//** get them. **
//** - Sliding distances have changed in this version of the system. **
//** - The function call (KBS_Begin/Ex) now takes distance and duration **
//** parameters instead of startspeed and deceleration. **
//** - Therefore the parameter 'Distance' will set the length of the knockback **
//** while the parameter 'Duration" will set the time it will take. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Implementation: **
//** - First off you need the NewGen world editor. This is a must. **
//** - Copy this trigger into your map. Or make a trigger in your map and **
//** convert it to custom text. Then copy this text into that trigger. **
//** - Now you will either need to export the special effects from this map to **
//** your map, or you will need your own. I do not take credit for the **
//** special effects found in this map. **
//** - Once you have the effects in your map. Find the configuration block **
//** that is underneath all this green text. Change the WATER_SFX and the **
//** DIRT_SFX strings to the ones in your map. These should be the same if **
//** you exported the ones from this map. **
//** - Make sure you have the dummy unit needed. It can be any unit, but i **
//** suggest you use the one found in this map, it is labelled 'Dummy Unit' **
//** or 'u000'. **
//** - Your done! Save your map, make a spell using this system and try it out. **
//** **
//** NOTE: Please report any bugs to me at thehelper.net via PM. (User name: kenny!)**
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Usage: **
//** **
//** call KBS_Begin(Target,Distance,Duration,Angle,Area) **
//** or **
//**call KBS_BeginEx(Target,Distance,Duration,Angle,Effect,Area,AllowMove,CheckPathing)**
//** or **
//** call KBS_BeginCommon(Target,Distance,Duration,Angle) **
//** **
//** - Target - The unit that will be used in the knockback. **
//** - Distance - The distance of the knockback (how far the unit will slide). **
//** - Duration - The duration of the knockback refers to how long the unit **
//** will slide for. **
//** - Angle - The angle that the unit will be knocked back at. **
//** THIS MUST BE IN DEGREES. <- Easier for GUI users. **
//** - Effect - This is only used in the extended function. If you do not **
//** want one of the predefined effects, you can choose your own. **
//** However, this effect is attached to the unit and removed at **
//** the end, so non-looping or repeating effect wont work. **
//** - Area - This determines whether or not trees will be knocked down. **
//** For trees to be knocked down, a positive number (real) must **
//** be used, such as 150.00, which would give a radius of 150.00 **
//** in which trees will be knocked down. **
//** For trees to not be knocked down, a negative number (real) **
//** must be used, such as -150.00, which would create a radius **
//** that if a tree comes within it, the unit will stop moving. **
//** For none of those effects, the number 0 (0.00) can be used. **
//** This will just cause the units to "bounce" off trees. **
//** -Allowmove - This boolean will decided whether or not you want the unit **
//** to have the ability to move while being knocked back. **
//** "true" will allow them to move, while "false" will not. **
//** -CheckPathing - A boolean that, if true, will check for unpathable terrain **
//** such as a wall or cliff, or where doodads may be. If false **
//** it will ignore these changes and the unit will be pushed **
//** along the wall, cliff or doodad. **
//** **
//** REMEMBER: Positive = trees destroyed, Negative = trees not destroyed. **
//** **
//** Example of Usage: **
//** **
//** -call KBS_Begin(target,500.00,2.00,270.00,150.00) **
//** or **
//** -call KBS_BeginEx(target,500.00,2.00,270.00,"Effect.mdl",-150.00,true,true) **
//** **
//** The first one will cause the target unit of a spell to be knocked back 500.00 **
//** range over a 2.00 second duration. It will be knocked back at an angle of **
//** 270.00, which I'm pretty sure is south, and it will knock down trees within **
//** a 150.00 radius around the unit. **
//** **
//** The bottom one of the two will do the exact same as the top, however you are **
//** able to choose your own special effect that will be attached to the unit. As **
//** you can see, the number for Area (destroying trees) is negative, therefore **
//** no trees will be knocked down, and the unit will stop moving if it comes in **
//** contact with a tree. There is also an extra boolean nearing end, this is for **
//** allowing units to move while sliding. It is true, meaning units are allowed. **
//** There is also an added feature that will check for pathable terrain and stop a **
//** unit if it comes in contact with things such as walls, cliffs or doodads. **
//** If true, the unit will stop moving if it comes in direct contact will a wall **
//** or cliff. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Other functions: **
//** **
//** - call KBS_IsUnitSliding(Target) **
//** **
//** - This function checks if a picked unit is currently sliding using this **
//** system. It will return true if it is. **
//** **
//** - call KBS_StopUnitSliding(Target) **
//** **
//** - This function will stop a picked unit from sliding (using this system). **
//** It also returns true if the unit is stopped. **
//** **
//** - These functions can be used in conjunction with each other, by checking if a **
//** unit is sliding then stopping it if it is. The regular Storm Bolt spell in **
//** the Test Spells section uses these two function in conjunction with each **
//** other as an example. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Some points on checking for pathable terrain: **
//** **
//**` - The area specified for destroying trees must be at least 75.00-100.00 range **
//** larger than the distance for checking for pathing, which is below in the **
//** globals block. **
//** - When using KBS_BeginEx() and using either 0.00 or a negative value for the **
//** parameter 'Area' (destroying trees) it is better to have CheckPathing as **
//** FALSE. This is due to the fact that setting CheckPathing to TRUE will **
//** override the 'Area' Parameter and stop a unit if it gets near trees. **
//** - Basically what this means is that you cannot get a unit to just 'bounce' off **
//** trees anymore, it is either destroy trees or get stopped by them. **
//** **
//***************************************************************************************
globals
// CONFIGURABLES:
private constant integer DUMMY_ID = 'u000' // This is the dummy unit.
private constant real INTERVAL = 0.04 // Recommended Interval.
private constant string ATTACH_POINT = "origin" // Attachment point for effects.
private constant string WATER_SFX = "SlideWater.mdx" // Water slide effect.
private constant string DIRT_SFX = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" // Ground slide effect.
private constant string COLLISION_SFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl" // Collision effect when unit hits a wall/cliff etc.
private constant boolean SHOW_COLLISION = true // Whether or not effect will show if a unit collides with a wall/cliff etc.
// The follow are used when using KBS_BeginCommon() for the common parameters that will be used.
private constant real COMMON_AREA = 150.00 // The area in which trees will be destroyed.
private constant boolean COMMON_ALLOW_MOVE = true // Whether or not to allow units to move.
private constant boolean COMMON_CHECK_PATHING = true // Whether or not to check pathing.
// The following raw codes will probably not need to be changed for most maps.
private constant integer HARVEST = 'Ahrl' // Raw code for: Harvest(Ghouls Lumber).
private constant integer LOCUST = 'Aloc' // Raw code for Locust ability.
// The following value is used for terrain pathability checking.
private constant real DISTANCE = 50.00
// The above is the distance in front of the unit that terrain pathability
// will be checked. A value of 40.00-50.00 is recommended. Also make sure
// that this distance is AT LEAST 75.00-100.00 units less than the ranges set
// for destroying trees, otherwise it wont work properly.
// Example: Keep above distance at 50.00, and use 150.00 for knocking down trees.
// This seems to work very well.
private constant real MAX_RANGE = 10.00
// Max range for checking for pathability. I suggest you leave this alone.
endglobals
//***************************************************************************************
//** **
//** DO NOT TOUCH BELOW HERE UNLESS YOU KNOW WHAT YOUR ARE DOING! **
//** **
//***************************************************************************************
private keyword Data
globals
private Data array D
private boolean array BA
private item array Hidden
private integer Hidden_max = 0
private integer Total = 0
private real Game_maxX = 0.00
private real Game_minX = 0.00
private real Game_maxY = 0.00
private real Game_minY = 0.00
private rect Rect1 = null
private unit Tree_dummy = null
private item Item = null
private timer Timer = null
private boolexpr Tree_filt = null
endglobals
//=======================================================================
private function Filter_items takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hidden[Hidden_max] = GetEnumItem()
call SetItemVisible(Hidden[Hidden_max],false)
set Hidden_max = Hidden_max + 1
endif
endfunction
// Thanks to Vexorian for original concept, and Anitarf for the up-to-date version. (Slightly midified).
private function Check_pathability takes real x1, real y1 returns boolean
local real x2 = 0.00
local real y2 = 0.00
call SetRect(Rect1,0.00,0.00,128.00,128.00)
call MoveRectTo(Rect1,x1,y1)
call EnumItemsInRect(Rect1,null,function Filter_items)
call SetItemPosition(Item,x1,y1)
set x2 = GetItemX(Item)
set y2 = GetItemY(Item)
call SetItemVisible(Item,false)
loop
exitwhen Hidden_max <= 0
set Hidden_max = Hidden_max - 1
call SetItemVisible(Hidden[Hidden_max],true)
set Hidden[Hidden_max] = null
endloop
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < MAX_RANGE * MAX_RANGE
endfunction
//=======================================================================
private function Destroy_trees takes nothing returns nothing
if BA[0] then
call KillDestructable(GetEnumDestructable()) // Used to destroy destructables.
else
set BA[1] = true
endif
endfunction
// Thanks to PitzerMike for this function. (Modified Slightly).
private function Check_trees takes nothing returns boolean
local destructable dest = GetFilterDestructable()
local boolean result = false
if GetDestructableLife(dest) > 0.405 then
call ShowUnit(Tree_dummy,true)
call SetUnitX(Tree_dummy,GetWidgetX(dest))
call SetUnitY(Tree_dummy,GetWidgetY(dest))
set result = IssueTargetOrder(Tree_dummy,"harvest",dest)
call IssueImmediateOrder(Tree_dummy,"stop")
call ShowUnit(Tree_dummy,false)
set dest = null
return result
endif
set dest = null
return result
endfunction
// Checks if a point is on the map boundaries.
private function Outside_bounds takes real x, real y returns boolean
return (x > Game_maxX or y > Game_maxY or x < Game_minX or y < Game_minY)
endfunction
//=======================================================================
private struct Data
unit targ = null
real speed = 0.00
real decrease = 0.00
real sin = 0.00
real cos = 0.00
real radius = 0.00
integer sfxmode = 0
effect sfx = null
string effect_loop
boolean custom = false
boolean allowmove = false
boolean pathing = false
boolean forcestop = false
// Checking for terrain type.
method terrain takes nothing returns integer
local real x = GetUnitX(.targ)
local real y = GetUnitY(.targ)
if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
return 1
elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
return 2
endif
return 0
endmethod
static method create takes unit Target, real Distance, real Duration, real Angle, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns Data
local Data d = Data.allocate()
// Allocates struct members to user defined variables.
set d.targ = Target
set d.speed = (2.00 * Distance) / (Duration + 1.00)
set d.decrease = d.speed / Duration
set d.sin = Sin(Angle)
set d.cos = Cos(Angle)
set d.radius = Area
set d.allowmove = AllowMove
set d.pathing = CheckPathing
set d.sfxmode = d.terrain()
if Effect != "" and Effect != null then
set d.custom = true
endif
// Adding effects to the unit.
if d.custom then
set d.sfx = AddSpecialEffectTarget(Effect,d.targ,ATTACH_POINT)
set d.effect_loop = Effect
else
if d.sfxmode == 1 then
set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
set d.effect_loop = DIRT_SFX
elseif d.sfxmode == 2 then
set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
set d.effect_loop = WATER_SFX
endif
endif
return d
endmethod
private method onDestroy takes nothing returns nothing
set .targ = null
if .sfx != null then
call DestroyEffect(.sfx) // Destroys effects if needed.
set .sfx = null
endif
set BA[0] = false
set BA[1] = false
endmethod
endstruct
//=======================================================================
private function Update takes nothing returns nothing
local integer i = 1
local integer newmode = 0
local integer height = 0
local Data d = 0
local real x = 0.00
local real y = 0.00
local real newx = 0.00
local real newy = 0.00
loop
exitwhen i > Total
set d = D[i]
set newmode = d.sfxmode
set x = GetUnitX(d.targ)
set y = GetUnitY(d.targ)
// Destroys trees if wanted, or stops the unit
if d.radius != 0.00 then
set BA[0] = d.radius > 0.00
call SetRect(Rect1,x - d.radius,y - d.radius,x + d.radius,y + d.radius)
call EnumDestructablesInRect(Rect1,Tree_filt,function Destroy_trees)
endif
call DestroyEffect(d.sfx)
call DestroyEffect(AddSpecialEffectTarget(d.effect_loop,d.targ,ATTACH_POINT))
// Checks for terrain pathability, such as walls and cliffs.
if d.pathing then
if Check_pathability(x + DISTANCE * d.cos,y + DISTANCE * d.sin) == false then
set height = 1
if SHOW_COLLISION then
call DestroyEffect(AddSpecialEffect(COLLISION_SFX,x,y))
endif
endif
endif
if not d.custom then
set d.sfxmode = d.terrain() // Checks for pathing again.
// Adds special effect if terrain changes.
if d.sfxmode == 1 and newmode == 2 then
call DestroyEffect(d.sfx)
set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
elseif d.sfxmode == 2 and newmode == 1 then
call DestroyEffect(d.sfx)
set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
endif
endif
if d.speed <= 0 or Outside_bounds(x,y) or BA[1] or height == 1 or d.forcestop == true then
call d.destroy() // Finish knockback
set D[i] = D[Total]
set Total = Total - 1
set i = i - 1
set height = 0
else
set newx = x + d.speed * d.cos
set newy = y + d.speed * d.sin
// Allows unit to move while sliding, if specified.
if d.allowmove then
call SetUnitX(d.targ,newx)
call SetUnitY(d.targ,newy)
else
call SetUnitPosition(d.targ,newx,newy)
endif
set d.speed = d.speed - d.decrease // Sets new speed.
endif
set i = i + 1
endloop
if Total <= 0 then
call PauseTimer(Timer)
set Total = 0
endif
endfunction
//=======================================================================
// Checks if a unit is sliding - returns true if it is.
public function IsUnitSliding takes unit Target returns boolean
local integer i = 1
loop
exitwhen i > Total
if D[i].targ == Target then
return true
endif
set i = i + 1
endloop
return false
endfunction
//=======================================================================
// Stops a unit from sliding - returns true if a unit is stopped.
public function StopUnitSliding takes unit Target returns boolean
local integer i = 1
loop
exitwhen i > Total
if D[i].targ == Target then
set D[i].forcestop = true
return true
endif
set i = i + 1
endloop
return false
endfunction
//=======================================================================
// Extended function - gives the most customisation for a single unit knockback.
public function BeginEx takes unit Target, real Distance, real Duration, real Angle, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns boolean
local Data d = 0
if IsUnitType(Target,UNIT_TYPE_STRUCTURE) == false and IsUnitType(Target,UNIT_TYPE_MAGIC_IMMUNE) == false and GetUnitTypeId(Target) != 'o003' then
if Target == null or Distance <= 0.00 or Duration <= 0.00 then
debug call BJDebugMsg("Error: Invalid input in KBS_Begin(Ex)") // Error message.
return false
endif
set d = Data.create(Target,Distance,(Duration/INTERVAL),(Angle * 0.01745328),Effect,Area,AllowMove,CheckPathing)
set Total = Total + 1
if Total == 1 then
call TimerStart(Timer,INTERVAL,true,function Update)
endif
set D[Total] = d
return true
else
return false
endif
endfunction
//=======================================================================
// Basic function - Can be used in a wide variety of ways and gives a basic knockback.
public function Begin takes unit Target, real Distance, real Duration, real Angle, real Area returns nothing
call BeginEx(Target,Distance,Duration,Angle,"",Area,false,false)
endfunction
// Common function - For all basic knockback needs, easy to use and simple to remember.
public function BeginCommon takes unit Target, real Distance, real Duration, real Angle returns nothing
call BeginEx(Target,Distance,Duration,Angle,null,COMMON_AREA,COMMON_ALLOW_MOVE,COMMON_CHECK_PATHING)
endfunction
//=======================================================================
// Sets map boundries and sets timer, rect and filter.
private function Init takes nothing returns nothing
set Timer = CreateTimer()
set Rect1 = Rect(0.00,0.00,1.00,1.00)
set Tree_filt = Filter(function Check_trees)
// Map bounds
set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
set Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
set Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
// Boolean values for destroying trees
set BA[0] = false
set BA[1] = false
// Creating unit for destroying trees
set Tree_dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),DUMMY_ID,0.00,0.00,0.00)
call SetUnitPathing(Tree_dummy,false)
call ShowUnit(Tree_dummy,false)
call UnitAddAbility(Tree_dummy,HARVEST)
call UnitAddAbility(Tree_dummy,LOCUST)
// Creating item for pathability checking
set Item = CreateItem('ciri',0.00,0.00)
call SetItemVisible(Item,false)
endfunction
endlibrary