//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
TempLoc | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CTL /* v1.2.0.3
*************************************************************************************
*
* CTL or Constant Timer Loop provides a loop for constant merged timers of timeout .03125
*
* Similar to T32 but pauses timer when no structs have instances and removes structs
* from timer trigger when those structs have no instances.
*
* This can also create new timers after destroying a previous timer and generates less
* code in the module. It also generates no triggers so long as the module is implemented
* at the top of the struct.
*
************************************************************************************
*
* module CTL
*
* Allows creation/destruction of timers in a struct. Provides instancing of those timers.
*
* - static method create takes nothing returns thistype
* - method destroy takes nothing returns nothing
*
* CTL (optional)
* local variables, code before running any timers
* CTLExpire (not optional)
* timer code
* CTLNull (optional)
* null any locals, runs after all timers
* CTLEnd (not optional)
*
* module CT32
*
* Converts struct into a timer group. Allows the timer group to be started and stopped.
* Instancing and looping through active timers is up to the user.
*
* - static method start takes nothing returns nothing
* - static method stop takes nothing returns nothing
*
* CT32 (not optional)
* timer code
* CT32End (not optional)
*
* struct TimerGroup32 extends array
*
* Allows for the creation of timer groups. Timer instancing and looping is entirely up
* to the user.
*
* - static method create takes code func returns thistype
* - method destroy takes nothing returns nothing
* - method start takes nothing returns nothing
* - method stop takes nothing returns nothing
*
************************************************************************************/
globals
private integer tgc = 0 //timer group count
private integer array tgr //timer group recycler
private integer ic=0 //instance count
private integer tc=0 //timer count
private integer array rf //root first
private integer array n //next
private integer array p //previous
private integer array th //timer head
private integer array ns //next stack
private trigger t=CreateTrigger()
private timer m=CreateTimer()
private triggercondition array ct
private conditionfunc array rc
private boolean array e32 //enabled
private integer array i32r //ct32 recycler
private integer i32cr = 0 //ct32 count recycler
private boolean array ir32 //is recycling
private boolean array id32 //is destroying
endglobals
private function E takes nothing returns nothing
local integer i=ns[0]
set ns[0]=0
loop
exitwhen 0==i
if (0==p[i]) then
if (0==n[i]) then
call TriggerRemoveCondition(t,ct[th[i]])
set ct[th[i]]=null
set tc=tc-1
set rf[th[i]]=0
else
set rf[th[i]]=n[i]
set p[n[i]]=0
endif
else
set p[n[i]]=p[i]
set n[p[i]]=n[i]
endif
set n[i]=n[0]
set n[0]=i
set i=ns[i]
endloop
loop
exitwhen 0 == i32cr
set i32cr = i32cr - 1
set i = i32r[i32cr]
if (not e32[i]) then
call TriggerRemoveCondition(t,ct[i])
set ct[i] = null
if (id32[i]) then
set tgr[i] = tgr[0]
set tgr[0] = i
set id32[i] = false
endif
set ir32[i] = false
endif
endloop
if (0==tc) then
call PauseTimer(m)
else
call TriggerEvaluate(t)
endif
endfunction
private function CT takes integer r returns integer
local integer i
local integer f
if (0==n[0]) then
set i=ic+1
set ic=i
else
set i=n[0]
set n[0]=n[i]
endif
set th[i]=r
set ns[i]=-1
set f=rf[r]
if (0==f) then
set n[i]=0
set p[i]=0
set rf[r]=i
set ct[r]=TriggerAddCondition(t,rc[r])
//set ct[r] = null
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
else
set n[i]=f
set p[i]=0
set p[f]=i
set rf[r]=i
endif
return i
endfunction
private function DT takes integer t returns nothing
debug if (0>ns[t]) then
set ns[t]=ns[0]
set ns[0]=t
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"TIMER LOOP ERROR: ATTEMPT TO DESTROY NULL TIMER")
debug endif
endfunction
private function A takes code c returns integer
local integer i = tgr[0]
if (0 == i) then
set i = tgc + 1
set tgc = i
else
set tgr[0] = tgr[i]
endif
set rc[i]=Condition(c)
return i
endfunction
private function A32 takes integer i returns nothing
if (not (e32[i] or id32[i])) then
if (ir32[i]) then
set ir32[i] = false
else
set ct[i] = TriggerAddCondition(t, rc[i])
endif
if (0 == tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc = tc + 1
set e32[i] = true
endif
endfunction
private function SR32 takes integer i returns nothing
if (e32[i]) then
if (not (ir32[i] or id32[i])) then
set i32r[i32cr] = i
set i32cr = i32cr + 1
set ir32[i] = true
endif
set e32[i] = false
set tc = tc - 1
endif
endfunction
private function DT32 takes integer i returns nothing
if (not id32[i]) then
if (not ir32[i]) then
set ir32[i] = true
set tc = tc - 1
set i32r[i32cr] = i
set i32cr = i32cr + 1
set e32[i] = false
endif
set id32[i] = true
endif
endfunction
private keyword r
private keyword e
module CTL
static integer rctl32
static method create takes nothing returns thistype
return CT(rctl32)
endmethod
method destroy takes nothing returns nothing
call DT(this)
endmethod
static method ectl32 takes nothing returns boolean
local thistype this=rf[rctl32]
endmodule
module CTLExpire
implement CTL
loop
exitwhen 0==this
endmodule
module CTLNull
set this=n[this]
endloop
endmodule
module CTLEnd
implement CTLNull
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
module CT32
static integer rctl32
static method start takes nothing returns nothing
call A32(rctl32)
endmethod
static method stop takes nothing returns nothing
call SR32(rctl32)
endmethod
static method ectl32 takes nothing returns boolean
endmodule
module CT32End
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
struct TimerGroup32 extends array
static method create takes code c returns thistype
return A(c)
endmethod
method destroy takes nothing returns nothing
call DT32(this)
endmethod
method start takes nothing returns nothing
call A32(this)
endmethod
method stop takes nothing returns nothing
call SR32(this)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GetTerrainZ /* v1.0.0.0
********************************************************************
*
* function GetTerrainZ takes real x, real y returns real
*
********************************************************************/
globals
private constant location L = Location(0, 0)
endglobals
function GetTerrainZ takes real x, real y returns real
call MoveLocation(L, x, y)
return GetLocationZ(L)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitZ /* v1.0.0.0
********************************************************************
*
* */uses/*
* */ GetTerrainZ /*
* */optional/*
* */ AutoFly /* hiveworkshop.com/forums/jass-resources-412/snippet-autofly-unitindexer-version-195563/
*
* function GetUnitZ takes unit whichUnit returns real
* function SetUnitZ takes unit whichUnit, real z returns real
*
********************************************************************/
function GetUnitZ takes unit u returns real
return GetTerrainZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - GetTerrainZ(GetUnitX(u), GetUnitY(u)), 0)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**
* IsTerrainWalkable snippet for estimating the walkability status of a co-ordinate pair, credits
* to Anitarf and Vexorian.
*
* API:
* boolean IsTerrainWalkable(real x, real y) - returns the walkability of (x,y)
*/
library IsTerrainWalkable initializer init
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE=10.
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X=0.
public real Y=0.
private rect r
private item check
private item array hidden
private integer hiddenMax=0
endglobals
private function init takes nothing returns nothing
set check=CreateItem('ciri',0.,0.)
call SetItemVisible(check,false)
set r=Rect(0.0,0.0,128.0,128.0)
endfunction
private function hideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r,x,y)
call EnumItemsInRect(r,null,function hideBothersomeItem)
// try to move the check item and get its coordinates
// this unhides the item...
call SetItemPosition(check,x,y)
set X=GetItemX(check)
set Y=GetItemY(check)
//...so we must hide it again
call SetItemVisible(check,false)
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax==0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y)<MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once Event /* v2.0.0.1
************************************************************************************
*
* Functions
*
* function CreateEvent takes nothing returns integer
* function TriggerRegisterEvent takes trigger t, integer ev returns nothing
*
************************************************************************************
*
* struct Event extends array
*
* static method create takes nothing returns thistype
* method registerTrigger takes trigger t returns nothing
* method register takes boolexpr c returns nothing
* method fire takes nothing returns nothing
*
************************************************************************************/
globals
private real q=0
endglobals
struct Event extends array
private static integer w=0
private static trigger array e
static method create takes nothing returns thistype
set w=w+1
set e[w]=CreateTrigger()
return w
endmethod
method registerTrigger takes trigger t returns nothing
call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"q",EQUAL,this)
endmethod
method register takes boolexpr c returns nothing
call TriggerAddCondition(e[this],c)
endmethod
method fire takes nothing returns nothing
set q=0
set q=this
call TriggerEvaluate(e[this])
endmethod
endstruct
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function TriggerRegisterEvent takes trigger t,Event ev returns nothing
call ev.registerTrigger(t)
endfunction
function RegisterEvent takes boolexpr c,Event ev returns nothing
call ev.register(c)
endfunction
function FireEvent takes Event ev returns nothing
call ev.fire()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
* readonly static integer centerX
* readonly static integer centerY
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=R2I(GetRectMaxX(world))
set maxY=R2I(GetRectMaxY(world))
set minX=R2I(GetRectMinX(world))
set minY=R2I(GetRectMinY(world))
set centerX=R2I((maxX+minX)/2)
set centerY=R2I((minY+maxY)/2)
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.7
*************************************************************************************
*
* Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
* */uses/*
*
* */ WorldBounds /* hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
* */ Event /* hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
* SETTINGS
*/
globals
constant integer ABILITIES_UNIT_INDEXER = 'A003'
endglobals
/*
************************************************************************************
*
* Functions
*
* function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
* function GetUnitById takes integer index returns unit
* - Returns unit given a unit index
* function GetUnitId takes unit u returns integer
* - Returns unit index given a unit
*
* function IsUnitIndexed takes unit u returns boolean
* function IsUnitDeindexing takes unit u returns boolean
*
* function GetIndexedUnitId takes nothing returns integer
* function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
* module UnitIndexStructMethods
* static method operator [] takes unit u returns thistype
* - Return GetUnitUserData(u)
*
* readonly unit unit
* - The indexed unit of the struct
*
************************************************************************************
*
* module UnitIndexStruct extends UnitIndexStructMethods
*
* - A pseudo module interface that runs a set of methods if they exist and provides
* - a few fields and operators. Runs on static ifs to minimize code.
*
* readonly boolean allocated
* - Is unit allocated for the struct
*
* Interface:
*
* - These methods don't have to exist. If they don't exist, the code
* - that calls them won't even be in the module.
*
* private method index takes nothing returns nothing
* - called when a unit is indexed and passes the filter.
* -
* - thistype this: Unit's index
* private method deindex takes nothing returns nothing
* - called when a unit is deindexed and is allocated for struct
* -
* - thistype this: Unit's index
* private static method filter takes unit unitToIndex returns boolean
* - Determines whether or not to allocate struct for unit
* -
* - unit unitToIndex: Unit being filtered
*
************************************************************************************
*
* struct UnitIndexer extends array
*
* - Controls the unit indexer system.
*
* static constant Event UnitIndexer.INDEX
* static constant Event UnitIndexer.DEINDEX
* - Don't register functions and triggers directly to the events. Register them via
* - RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
* static boolean enabled
* - Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
* struct UnitIndex extends UnitIndexStructMethods
*
* - Constrols specific unit indexes.
*
* method lock takes nothing returns nothing
* - Locks an index. When an index is locked, it will not be recycled
* - when the unit is deindexed until all locks are removed. Deindex
* - events still fire at the appropriate times, the index just doesn't
* - get thrown into the recycler.
* method unlock takes nothing returns nothing
* - Unlocks an index.
*
************************************************************************************/
globals
private trigger q=CreateTrigger()
private trigger l=CreateTrigger()
private unit array e
private integer r=0
private integer y=0
private integer o=0
private boolean a=false
private integer array n
private integer array p
private integer array lc
endglobals
function GetIndexedUnitId takes nothing returns integer
return o
endfunction
function GetIndexedUnit takes nothing returns unit
return e[o]
endfunction
//! runtextmacro optional UNIT_LIST_LIB()
private struct PreLoader extends array
public static method run takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
set a=true
endmethod
public static method eval takes trigger t returns nothing
local integer f=n[0]
local integer d=o
loop
exitwhen 0==f
if (IsTriggerEnabled(t)) then
set o=f
if (TriggerEvaluate(t)) then
call TriggerExecute(t)
endif
else
exitwhen true
endif
set f=n[f]
endloop
set o=d
endmethod
public static method evalb takes boolexpr c returns nothing
local trigger t=CreateTrigger()
local thistype f=n[0]
local integer d=o
call TriggerAddCondition(t,c)
loop
exitwhen 0==f
set o=f
call TriggerEvaluate(t)
set f=n[f]
endloop
call DestroyTrigger(t)
set t=null
set o=d
endmethod
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO()
private module UnitIndexerInit
private static method onInit takes nothing returns nothing
local integer i=15
local boolexpr bc=Condition(function thistype.onLeave)
local boolexpr bc2=Condition(function thistype.onEnter)
local group g=CreateGroup()
local player p
set INDEX=CreateEvent()
set DEINDEX=CreateEvent()
call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
loop
set p=Player(i)
call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
call GroupEnumUnitsOfPlayer(g,p,bc2)
exitwhen 0==i
set i=i-1
endloop
call DestroyGroup(g)
set bc=null
set g=null
set bc2=null
set p=null
call TimerStart(CreateTimer(),0,false,function PreLoader.run)
endmethod
endmodule
struct UnitIndex extends array
method lock takes nothing returns nothing
debug if (null!=e[this]) then
set lc[this]=lc[this]+1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
debug endif
endmethod
method unlock takes nothing returns nothing
debug if (0<lc[this]) then
set lc[this]=lc[this]-1
if (0==lc[this] and null==e[this]) then
set n[this]=y
set y=this
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
debug endif
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitUserData(whichUnit)
endmethod
endstruct
struct UnitIndexer extends array
readonly static Event INDEX
readonly static Event DEINDEX
static boolean enabled=true
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (enabled and Q!=e[GetUnitUserData(Q)] and 0==GetUnitUserData(Q)) then
if (0==y) then
set r=r+1
set i=r
else
set i=y
set y=n[y]
endif
call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
call SetUnitUserData(Q,i)
set e[i]=Q
static if not LIBRARY_UnitList then
if (not a)then
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
endif
else
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
call GroupAddUnit(g,e[i])
endif
set o=i
call FireEvent(INDEX)
set o=d
endif
set Q=null
return false
endmethod
private static method onLeave takes nothing returns boolean
static if LIBRARY_UnitEvent then
implement optional UnitEventModule
else
local unit u=GetFilterUnit()
local integer i=GetUnitUserData(u)
local integer d=o
if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
static if not LIBRARY_UnitList then
if (not a)then
set n[p[i]]=n[i]
set p[n[i]]=p[i]
endif
else
set n[p[i]]=n[i]
set p[n[i]]=p[i]
call GroupRemoveUnit(g,e[i])
endif
set o=i
call FireEvent(DEINDEX)
set o=d
if (0==lc[i]) then
set n[i]=y
set y=i
endif
set e[i]=null
endif
set u=null
endif
return false
endmethod
implement UnitIndexerInit
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO_2()
function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
call RegisterEvent(c, ev)
if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.evalb(c)
endif
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
call TriggerRegisterEvent(t,ev)
if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.eval(t)
endif
endfunction
function GetUnitById takes integer W returns unit
return e[W]
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function IsUnitIndexed takes unit u returns boolean
return u==e[GetUnitUserData(u)]
endfunction
function IsUnitDeindexing takes unit u returns boolean
return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)
endfunction
module UnitIndexStructMethods
static method operator [] takes unit u returns thistype
return GetUnitUserData(u)
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
endmodule
module UnitIndexStruct
implement UnitIndexStructMethods
static if thistype.filter.exists then
static if thistype.index.exists then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
elseif (thistype.index.exists) then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
static if thistype.index.exists then
private static method onIndexEvent takes nothing returns boolean
static if thistype.filter.exists then
if (filter(e[o])) then
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
else
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
return false
endmethod
endif
static if thistype.deindex.exists then
private static method onDeindexEvent takes nothing returns boolean
static if thistype.filter.exists then
static if thistype.index.exists then
if (thistype(o).allocated) then
set thistype(o).allocated=false
call thistype(o).deindex()
endif
else
if (filter(e[o])) then
call thistype(o).deindex()
endif
endif
else
static if thistype.index.exists then
set thistype(o).allocated=false
endif
call thistype(o).deindex()
endif
return false
endmethod
endif
static if thistype.index.exists then
static if thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
else
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
endmethod
endif
elseif thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* GetClosestWidget v3.0.1.3
* by Bannar aka Spinnaker
*
* Allows finding closest widget with ease.
*
******************************************************************************
*
* Configurables:
*
* Choose which modules should or should not be implemented.
*
* constant boolean UNITS_MODULE
* constant boolean GROUP_MODULE
* constant boolean ITEMS_MODULE
* constant boolean DESTS_MODULE
*
* Define start and final distances for search iterations within generic GetClosest functions.
* If final value is reached, enumeration is performed on whole map.
*
* constant real START_DISTANCE
* constant real FINAL_DISTANCE
*
******************************************************************************
*
* Functions:
*
* Units:
* | function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* | returns unit closest to coords(x, y)
* |
* | function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* | returns unit closest to coords(x, y) within range radius
* |
* | function GetClosestUnitInGroup takes real x, real y, group g returns unit
* | returns unit closest to coords(x, y) within group g
*
*
* Group:
* | function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within range radius
* |
* | function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within group source
*
*
* Items:
* | function GetClosestItem takes real x, real y, boolexpr filter returns item
* | returns item closest to coords(x, y)
* |
* | function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* | returns item closest to coords(x, y) within range radius
*
*
* Destructables:
* | function GetClosestDestructable takes real x, real y, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y)
* |
* | function GetClosestDestructableInRange takes real x, real y, real radius, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y) within range radius
*
*
*****************************************************************************/
library GetClosestWidget
globals
private constant boolean UNITS_MODULE = true
private constant boolean GROUP_MODULE = true
private constant boolean ITEMS_MODULE = true
private constant boolean DESTS_MODULE = true
private constant real START_DISTANCE = 800
private constant real FINAL_DISTANCE = 3200
endglobals
globals
private real distance
private real coordX
private real coordY
endglobals
private keyword GroupModule
private function calcDistance takes real x, real y returns real
local real dx = x - coordX
local real dy = y - coordY
return ( (dx*dx + dy*dy) / 10000 )
endfunction
private struct ClosestWidget extends array
static if UNITS_MODULE then
static unit unit
static group group = CreateGroup()
endif
static if GROUP_MODULE then
static if not UNITS_MODULE then
static group group = CreateGroup()
endif
static integer count = 0
static unit array sorted
static real array vector
implement GroupModule
endif
static if ITEMS_MODULE then
static item item
static rect area = Rect(0, 0, 0, 0)
endif
static if DESTS_MODULE then
static destructable destructable
static if not ITEMS_MODULE then
static rect area = Rect(0, 0, 0, 0)
endif
endif
endstruct
private function Defaults takes real x, real y returns nothing
static if UNITS_MODULE then
set ClosestWidget.unit = null
endif
static if ITEMS_MODULE then
set ClosestWidget.item = null
endif
static if DESTS_MODULE then
set ClosestWidget.destructable = null
endif
set distance = 100000
set coordX = x
set coordY = y
endfunction
static if UNITS_MODULE then
//! runtextmacro DEFINE_GCW_UNIT_MODULE()
endif
static if GROUP_MODULE then
//! runtextmacro DEFINE_GCW_GROUP_MODULE()
endif
static if ITEMS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Item", "item")
endif
static if DESTS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Destructable", "destructable")
endif
//! textmacro DEFINE_GCW_UNIT_MODULE
private function doEnumUnits takes unit u returns nothing
local real dist = calcDistance(GetUnitX(u), GetUnitY(u))
if ( dist < distance ) then
set ClosestWidget.unit = u
set distance = dist
endif
endfunction
private function enumUnits takes nothing returns nothing
call doEnumUnits(GetEnumUnit())
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r = START_DISTANCE
local unit u
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call GroupEnumUnitsInRect(ClosestWidget.group, GetWorldBounds(), filter)
exitwhen true
else
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, r, filter)
exitwhen FirstOfGroup(ClosestWidget.group) != null
endif
set r = 2*r
endloop
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
return ClosestWidget.unit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
local unit u
call Defaults(x, y)
if ( radius >= 0 ) then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
endif
return ClosestWidget.unit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call Defaults(x, y)
call ForGroup(g, function enumUnits)
return ClosestWidget.unit
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_GROUP_MODULE
private module GroupModule
static method doSaveUnits takes unit u returns nothing
set count = count + 1
set sorted[count] = u
set vector[count] = calcDistance(GetUnitX(u), GetUnitY(u))
endmethod
static method saveUnits takes nothing returns nothing
call doSaveUnits(GetEnumUnit())
endmethod
static method sortUnits takes integer lo, integer hi returns nothing
local integer i = lo
local integer j = hi
local real pivot = vector[(lo+hi)/2]
loop
loop
exitwhen vector[i] >= pivot
set i = i + 1
endloop
loop
exitwhen vector[j] <= pivot
set j = j - 1
endloop
exitwhen i > j
set vector[0] = vector[i]
set vector[i] = vector[j]
set vector[j] = vector[0]
set sorted[0] = sorted[i]
set sorted[i] = sorted[j]
set sorted[j] = sorted[0]
set i = i + 1
set j = j - 1
endloop
if ( lo < j ) then
call sortUnits(lo, j)
endif
if ( hi > i ) then
call sortUnits(i, hi)
endif
endmethod
static method fillGroup takes integer n, group dest returns nothing
loop
exitwhen count <= 0 or sorted[count] == null
if ( count <= n ) then
call GroupAddUnit(dest, sorted[count])
endif
set sorted[count] = null
set count = count - 1
endloop
endmethod
endmodule
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
local unit u
call Defaults(x, y)
if ( radius >= 0 )then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call ClosestWidget.doSaveUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endif
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
local integer i = 0
call Defaults(x, y)
call ForGroup(source, function ClosestWidget.saveUnits)
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_MODULE takes NAME, TYPE
private function enum$NAME$s takes nothing returns nothing
local $TYPE$ temp = GetEnum$NAME$()
local real dist = calcDistance(Get$NAME$X(temp), Get$NAME$Y(temp))
if ( dist < distance ) then
set ClosestWidget.$TYPE$ = temp
set distance = dist
endif
set temp = null
endfunction
function GetClosest$NAME$ takes real x, real y, boolexpr filter returns $TYPE$
local real r = START_DISTANCE
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call Enum$NAME$sInRect(GetWorldBounds(), filter, function enum$NAME$s)
exitwhen true
else
call SetRect(ClosestWidget.area, x-r, y-r, x+r, y+r)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
exitwhen ClosestWidget.$TYPE$ != null
endif
set r = 2*r
endloop
return ClosestWidget.$TYPE$
endfunction
function GetClosest$NAME$InRange takes real x, real y, real radius, boolexpr filter returns $TYPE$
call Defaults(x, y)
if ( radius > 0 ) then
call SetRect(ClosestWidget.area, x-radius, y-radius, x+radius, y+radius)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
endif
return ClosestWidget.$TYPE$
endfunction
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* 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 either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
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 = 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
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=0
//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
library AutoFly /* v1.0.0.1
-Credits to Magtheridon96 and Bribe for code update
-Credits to Azlier for original
-thehelper.net/forums/showthread.php/139729-AutoFly
*************************************************************************************
*
* Makes SetUnitFlyHeight possible
*
*************************************************************************************
*
* */uses/*
*
* */ UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*
************************************************************************************/
private function i takes nothing returns boolean
return UnitAddAbility(GetIndexedUnit(), 'Amrf') and UnitRemoveAbility(GetIndexedUnit(), 'Amrf')
endfunction
private module Init
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function i), UnitIndexer.INDEX)
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GummyLink uses CTL, WorldBounds, UnitZ, IsTerrainWalkable optional AutoFly
/*
Gummy Link v2.5
Descriptions:
A library used to create magical link between
two units which will drag the target toward the
source elastically.
!! Source of any link should not be able to move !!
External Dependencies:
(Required)
- CTL
- UnitZ
- WorldBounds
- UnitIndexer
- IsTerrainWalkable
(Optional)
- AutoFly
How to use:
struct GummyLink
1. Create connection between two units
static method connect takes unit s, unit t, real z, real power, real elastic, string mdl, boolean is3D returns thistype
s => source
t => target
z => lightning z offset
power => drag power
elastic => link elasticity
mdl => lightning model string
is3D => simulate 3D effect
2. Remove a connection
method remove takes nothing returns nothing
Credits:
- IsTerrainWalkable by Anitarf and Vexorian
- CTL by Nestharus
- AutoFly by Nestharus
- WorldBounds by Nestharus
- UnitIndexer by Nestharus
- GetClosestWidget by Bannar
- SpellEffectEvent by Bribe
- UnitZ by Garfield1337
- OrbDragonX.mdx by Frankster
- BTNManaBreathe by D.ee
- LaserBlue.blp by Erkki2
Link:
hiveworkshop.com/forums/spells-569/gummy-leash-v1-0-a-257717/
*/
// CONFIGURATIONS
globals
// Unit flying height reset rate
private constant real RESET_RATE = 15.0
// Add KB effect after unit's link destroy
private constant boolean ENABLE_KB = true
endglobals
// Just skip these KB datas if ENABLE_KB is false
private module KBData
// Friction rate when units flung on the ground
static constant real FRICTION_FACTOR_GROUND = 0.04
// Friction rate when units flung in the air
static constant real FRICTION_FACTOR_AIR = 0.5
// General destructable heights
// For better idea, this is minimum height
// before pathability checking is performed
static constant real DEST_HEIGHT = 100.0
endmodule
// =============================================================================
// Check whether given coords is in map bound or not
private constant function inBound takes real x, real y returns boolean
return x < WorldBounds.maxX and y < WorldBounds.maxY and x > WorldBounds.minX and y > WorldBounds.minY
endfunction
private module KBModule
unit target
real speed
real rate
real cos
real sin
implement KBData
implement CTL
local real f
local real x
local real y
implement CTLExpire
if .speed > .rate then
set x = GetUnitX(.target) + .speed * .cos
set y = GetUnitY(.target) + .speed * .sin
set f = GetUnitFlyHeight(.target)
if f > DEST_HEIGHT then
// Ignore any pathability issue except world bounds
if inBound(x, y) then
call SetUnitX(.target, x)
call SetUnitY(.target, y)
endif
else
if IsTerrainWalkable(x, y) then
call SetUnitX(.target, x)
call SetUnitY(.target, y)
else
call destroy()
set .target = null
endif
endif
if f > .01 then
set .speed = .speed - .rate * FRICTION_FACTOR_AIR
else
set .speed = .speed - .rate
endif
else
call destroy()
set .target = null
endif
implement CTLEnd
static method knock takes unit u, real a, real s returns nothing
local thistype this = create()
set .target = u
set .speed = s
set .cos = Cos(a)
set .sin = Sin(a)
set .rate = s * FRICTION_FACTOR_GROUND
endmethod
endmodule
// Add simple KB effect to a unit
private struct SimpleKB extends array
static if ENABLE_KB then
implement KBModule
endif
endstruct
// Struct used to modify and save unit's fly height
// since the default one doesn't fit my needs
private struct UnitZ extends array
unit unit
real rate
real height
static real array Height
static thistype array Index
implement CTL
local integer dex
implement CTLExpire
set dex = GetUnitUserData(.unit)
if Height[dex] > .height then
set Height[dex] = Height[dex] - .rate
if Height[dex] < .height then
set Height[dex] = .height
endif
call SetUnitFlyHeight(.unit, Height[dex], 0)
elseif Height[dex] < .height then
set Height[dex] = Height[dex] + .rate
if Height[dex] > .height then
set Height[dex] = .height
endif
call SetUnitFlyHeight(.unit, Height[dex], 0)
else
set Index[dex] = 0
call destroy()
set .unit = null
endif
implement CTLEnd
static method get takes unit u returns real
return Height[GetUnitUserData(u)]
endmethod
static method apply takes unit u, real h, real r returns nothing
local thistype this
local integer dex = GetUnitUserData(u)
// If instant rate
if r < 1 then
set Height[dex] = h
call SetUnitFlyHeight(u, Height[dex], 0)
if Index[dex] != 0 then
set Index[dex].height = h
endif
else
if Index[dex] == 0 then
set this = create()
set .unit = u
set .height = h
set .rate = r
set Index[dex] = this
set Height[dex] = GetUnitFlyHeight(u)
else
set Index[dex].height = h
set Index[dex].rate = r
endif
endif
endmethod
static method onIndex takes nothing returns boolean
set Height[GetIndexedUnitId()] = GetUnitFlyHeight(GetIndexedUnit())
return false
endmethod
static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndex), UnitIndexer.INDEX)
endmethod
endstruct
struct GummyLink extends array
readonly unit source
readonly unit target
private real z
private real pow
private real elstc
private boolean b
private boolean is3D
private lightning link
private static real array sX
private static real array sY
private static real array tX
private static real array tY
private static real array HVel
private static real array VVel
private static real array Height
private static boolean array Bool
private static constant real TAU = bj_PI*2
private static constant real HP = bj_PI/2
private static constant real M = 0.2
// Gets circular differences between two radians
private static method circDifference takes real a, real b returns real
local real r = RAbsBJ(a-b)
if r * 2 <= TAU then
return r
else
return TAU - r
endif
endmethod
implement CTL
local real a
local real v
local real z
local integer sDex
local integer tDex
implement CTLExpire
set sDex = GetUnitUserData(.source)
set tDex = GetUnitUserData(.target)
// Automatically destroy link if unit has been removed
if .b and GetUnitTypeId(.source) != 0 and GetUnitTypeId(.target) != 0 then
set a = Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex])
// Calculate horizontal velocity
if HVel[tDex] >= 0 then
set HVel[tDex] = HVel[tDex] - .elstc
else
set HVel[tDex] = HVel[tDex] - .pow
endif
set tX[tDex] = tX[tDex] + HVel[tDex] * Cos(a)
set tY[tDex] = tY[tDex] + HVel[tDex] * Sin(a)
if circDifference(a, Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex])) > HP then
set HVel[tDex] = HVel[tDex] * -1
endif
set z = GetUnitZ(.source) + .z
// If 3D simulation is enabled
if .is3D then
// Calculate vertical velocity
if Height[tDex] < z then
set VVel[tDex] = VVel[tDex] + .pow*M
else
set VVel[tDex] = VVel[tDex] - (.elstc+elstc*(1-M))
endif
set Height[tDex] = Height[tDex] + VVel[tDex]
if Height[tDex] < 0 then
set Height[tDex] = 0
if VVel[tDex] < HVel[tDex] then
set VVel[tDex] = 0
endif
endif
call UnitZ.apply(.target, Height[tDex], 0)
endif
if inBound(tX[tDex], tY[tDex]) then
call SetUnitX(.target, tX[tDex])
call SetUnitY(.target, tY[tDex])
// Update the lightning
call MoveLightningEx(.link, false, sX[sDex], sY[sDex], z, tX[tDex], tY[tDex], GetUnitZ(.target) + .z)
endif
else
call SetUnitPathing(.source, true)
call SetUnitPathing(.target, true)
static if ENABLE_KB then
// Determine KB direction
if HVel[tDex] < 0 then
call SimpleKB.knock(.target, Atan2(sY[sDex] - tY[tDex], sX[sDex] - tX[tDex]), RAbsBJ(HVel[tDex]))
else
call SimpleKB.knock(.target, Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex]), HVel[tDex])
endif
endif
// Reset fly height
if .is3D then
call UnitZ.apply(.target, GetUnitDefaultFlyHeight(.target), RESET_RATE)
endif
call DestroyLightning(.link)
set Bool[tDex] = false
set HVel[tDex] = 0
set VVel[tDex] = -RESET_RATE
call destroy()
set .source = null
set .target = null
set .link = null
endif
implement CTLEnd
method remove takes nothing returns nothing
set .b = false
endmethod
static method connect takes unit s, unit t, real z, real power, real elastic, string mdl, boolean is3D returns thistype
local thistype this = 0
local integer dex
if not Bool[GetUnitUserData(t)] then
set this = create()
set .z = z
set .source = s
set .target = t
set .is3D = is3D
set .pow = power
set .elstc = elastic
set .b = true
set .link = AddLightningEx(mdl, false, GetUnitX(s), GetUnitY(s), GetUnitZ(s) + z, GetUnitX(t), GetUnitY(t), GetUnitZ(t) + z)
set dex = GetUnitUserData(s)
set sX[dex] = GetUnitX(s)
set sY[dex] = GetUnitY(s)
set Bool[dex] = true
set dex = GetUnitUserData(t)
set tX[dex] = GetUnitX(t)
set tY[dex] = GetUnitY(t)
set Bool[dex] = true
call SetUnitPathing(s, false)
call SetUnitPathing(t, false)
if is3D then
set Height[dex] = UnitZ.get(t)
static if not LIBRARY_AutoFly then
if UnitAddAbility(t, 'Amrf') and UnitRemoveAbility(t, 'Amrf') then
endif
endif
endif
debug else
debug call BJDebugMsg("Connecting failed: target is already connected to other instance.")
endif
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope GummyLeash
/*
Gummy Leash v2.5
Descriptions:
Summons a mythical orb upon the target point.
Nearby enemy units will be captured by an
elastical energy stream which will keep them
from moving further away from the orb.
Requires:
- GummyLink
External Dependencies:
(Required)
- CTL
- UnitIndexer
- WorldBounds
(Optional)
- GetClosestWidget
- SpellEffectEvent
How to import:
- Install and configure all dependencies properly
- Copy GummyLeash trigger group into your map
- Import all necessary stuffs (OE & import data) to your map
- Configure the spell
Credits:
- IsTerrainWalkable by Anitarf and Vexorian
- CTL by Nestharus
- AutoFly by Nestharus
- WorldBounds by Nestharus
- UnitIndexer by Nestharus
- GetClosestWidget by Bannar
- SpellEffectEvent by Bribe
- UnitZ by Garfield1337
- OrbDragonX.mdx by Frankster
- BTNManaBreathe by D.ee
- LaserBlue.blp by Erkki2
Link:
hiveworkshop.com/forums/spells-569/gummy-leash-v1-0-a-257717/
*/
// CONFIGURATIONS
// Native declaration
native UnitAlive takes unit id returns boolean
globals
// Summoned unit's rawcode
private constant integer WARD_ID = 'h000'
// Main ability's rawcode
private constant integer MAIN_ID = 'A000'
// Adjust z offset of the lightning
// As example, if you use a floating model for the ward
// then you can adjust the lightning's z offset here
private constant real Z_OFFSET = 75.0
// Lightning effect string
private constant string LIGHTNING_EFFECT = "BLUE"
// Allow ward to detect for new targets during lifespan
private constant boolean ALLOW_NEW_TARGET = true
// Simulate 3D effect for GummyLink
private constant boolean ENABLE_3D = true
// Add timed life bar to the ward
private constant boolean ADD_TIMED_LIFE = true
private constant integer TIMED_LIFE = 'BTLF'
// Maximum possible target
// Set this to target count at max ability level
private constant integer MAXIMUM_TARGET = 5
// Attached sfx on victims
private constant string TARGET_SFX = "war3mapImported\\GummyLeash.mdx"
private constant string TARGET_SFX_PT = "chest"
// Add buff effect (stun) to targets
private constant boolean ADD_BUFF = true
// Damage configuration
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// Just skip these buff datas if ADD_BUFF is false
private module BuffData
// Dummy unit's rawcode
static constant integer DUMMY_ID = 'h001'
// Buff ability rawcode
static constant integer SPELL_ID = 'A001'
// Buff rawcode
static constant integer BUFF_ID = 'B000'
// Stun order id
static constant integer BUFF_ORDER = 852127 // stomp
// The higher the safer but just leave it
static constant real BUFF_SAFETY = 64.0
endmodule
// Maximum targets per ward
private constant function count takes integer level returns integer
return 2 + level
endfunction
// Ward's lifespan
private constant function duration takes integer level returns real
return 3.0 + 2.0 * level
endfunction
// Apply damage interval
private constant function delay takes integer level returns real
return 0.0
endfunction
// Dealt damage amount
private constant function damage takes integer level returns real
return 0.0
endfunction
// Detect AoE
private constant function aoe takes integer level returns real
return 600.0
endfunction
// Drag power of the link (when shrinking)
private constant function power takes integer level returns real
return 3.2
endfunction
// Drag power of the link (when stretching)
private constant function elasticity takes integer level returns real
return 3.2
endfunction
// Configure desired targets
// By default, it's unable to targets structures and magic immune
private function targets takes unit u, player p returns boolean
return UnitAlive(u) and IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_MECHANICAL)
endfunction
// This function is called whenever a unit takes damage from this spell
private function onDamage takes unit caster, unit target returns nothing
endfunction
// This function is called whenever a unit is released by this spell
private function onRelease takes unit caster, unit target returns nothing
endfunction
// =============================================================================
private struct LeashBuff extends array
unit target
boolean dispose
effect sfx
static unit Caster
static group Group = CreateGroup()
static boolean array Bool
static thistype array Index
static if ADD_BUFF then
implement BuffData
endif
method remove takes nothing returns nothing
set .dispose = true
endmethod
implement CTLExpire
if .dispose or not UnitAlive(.target) then
set Bool[GetUnitUserData(.target)] = false
static if ADD_BUFF then
call UnitRemoveAbility(.target, BUFF_ID)
endif
call DestroyEffect(.sfx)
call destroy()
set .sfx = null
set .target = null
endif
implement CTLEnd
static method apply takes unit target returns thistype
local thistype this = 0
local integer dex = GetUnitUserData(target)
local real x
local real y
if Bool[dex] then
set this = Index[dex]
else
if not(IsUnitType(target, UNIT_TYPE_STRUCTURE)) then
set this = create()
set .target = target
set .dispose = false
set .sfx = AddSpecialEffectTarget(TARGET_SFX, .target, TARGET_SFX_PT)
set Bool[dex] = true
set Index[dex] = this
static if ADD_BUFF then
set x = GetUnitX(target)
set y = GetUnitY(target)
// If unit don't have the buff yet
if GetUnitAbilityLevel(target, BUFF_ID) == 0 then
call SetUnitX(Caster, x)
call SetUnitY(Caster, y)
call IssueImmediateOrderById(Caster, BUFF_ORDER)
call SetUnitPosition(Caster, WorldBounds.maxX, WorldBounds.maxY)
endif
// Remove unwanted buff
call GroupEnumUnitsInRange(Group, x, y, BUFF_SAFETY, null)
loop
set target = FirstOfGroup(Group)
exitwhen target == null
call GroupRemoveUnit(Group, target)
if GetUnitAbilityLevel(target, BUFF_ID) > 0 and not Bool[GetUnitUserData(target)] then
call UnitRemoveAbility(target, BUFF_ID)
endif
endloop
endif
debug else
debug call BJDebugMsg("Error occured: failed to attach buff")
endif
endif
return this
endmethod
private static method onInit takes nothing returns nothing
static if ADD_BUFF then
set Caster = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, 0)
call UnitAddAbility(Caster, SPELL_ID)
endif
endmethod
endstruct
// Container for targets per instance
private struct LeashTargets
unit array target[MAXIMUM_TARGET]
GummyLink array link[MAXIMUM_TARGET]
LeashBuff array buff[MAXIMUM_TARGET]
endstruct
private struct GummyLeash extends array
real x
real y
real dur
real aoe
real pow
real elstc
real dly
real dlyx
real dmg
integer ct
integer ctx
player p
unit caster
unit ward
LeashTargets t
static group Group = CreateGroup()
static player TempPlayer
static thistype TempDex
static boolean array Bool
// Target filtration, made to be compatible for GetClosestUnit
static method filter takes nothing returns boolean
return targets(GetFilterUnit(), TempPlayer)
endmethod
implement CTL
local unit u
local integer i
local integer dex
implement CTLExpire
if .dur > 0.03125 and UnitAlive(.ward) then
set .dur = .dur - 0.03125
set .dly = .dly - 0.03215
set i = 0
loop
exitwhen i >= .ct
if UnitAlive(.t.target[i]) then
if .dly <= 0 and .dmg > 0 then
call onDamage(.caster, .t.target[i])
call UnitDamageTarget(.caster, .t.target[i], .dmg, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
else
call onRelease(.caster, .t.target[i])
set Bool[GetUnitUserData(.t.target[i])] = false
call .t.link[i].remove()
// Deindex
set .ct = .ct - 1
set .t.target[i] = .t.target[.ct]
set .t.link[i] = .t.link[.ct]
set .t.buff[i] = .t.buff[.ct]
set .t.target[.ct] = null
set i = i - 1
endif
set i = i + 1
endloop
if .dly <= 0 then
set .dly = .dlyx
endif
static if ALLOW_NEW_TARGET then
if .ct < .ctx then
set TempPlayer = .p
call GroupEnumUnitsInRange(Group, .x, .y, .aoe, function thistype.filter)
loop
exitwhen .ct >= .ctx
static if LIBRARY_GetClosestWidget then
set u = GetClosestUnitInGroup(.x, .y, Group)
else
set u = FirstOfGroup(Group)
endif
exitwhen u == null
call GroupRemoveUnit(Group, u)
set dex = GetUnitUserData(u)
if not Bool[dex] then
set .t.buff[.ct] = LeashBuff.apply(u)
// If successfully buffed
if .t.buff[.ct] != 0 then
set .t.link[.ct] = GummyLink.connect(.ward, u, Z_OFFSET, .pow, .elstc, LIGHTNING_EFFECT, ENABLE_3D)
// If successfully connected
if .t.link[.ct] != 0 then
set Bool[dex] = true
set .t.target[.ct] = u
set .ct = .ct + 1
endif
endif
endif
endloop
set u = null
endif
endif
else
set i = 0
loop
exitwhen i >= .ct
call onRelease(.caster, .t.target[i])
set Bool[GetUnitUserData(.t.target[i])] = false
call .t.link[i].remove()
call .t.buff[i].remove()
set .t.target[i] = null
set i = i + 1
endloop
call KillUnit(.ward)
call .t.destroy()
call destroy()
set .ward = null
set .caster = null
endif
implement CTLEnd
static method onCast takes nothing returns boolean
local thistype this = create()
local integer level
local integer dex
local unit fog
set .caster = GetTriggerUnit()
set .p = GetTriggerPlayer()
set .ward = CreateUnit(.p, WARD_ID, GetSpellTargetX(), GetSpellTargetY(), bj_UNIT_FACING)
set .x = GetUnitX(.ward)
set .y = GetUnitY(.ward)
set .ct = 0
set level = GetUnitAbilityLevel(.caster, MAIN_ID)
set .aoe = aoe(level)
set .dur = duration(level)
set .elstc = elasticity(level)
set .pow = power(level)
set .ctx = count(level)
set .dlyx = delay(level)
set .dmg = damage(level)
set .dly = .dlyx
set .t = LeashTargets.create()
static if ADD_TIMED_LIFE then
call UnitApplyTimedLife(.ward, TIMED_LIFE, .dur)
endif
set TempPlayer = .p
call GroupEnumUnitsInRange(Group, .x, .y, .aoe, function thistype.filter)
loop
exitwhen .ct >= .ctx
static if LIBRARY_GetClosestWidget then
set fog = GetClosestUnitInGroup(.x, .y, Group)
else
set fog = FirstOfGroup(Group)
endif
exitwhen fog == null
call GroupRemoveUnit(Group, fog)
set dex = GetUnitUserData(fog)
if not Bool[dex] then
set .t.buff[.ct] = LeashBuff.apply(fog)
// If successfully buffed
if .t.buff[.ct] != 0 then
set .t.link[.ct] = GummyLink.connect(.ward, fog, Z_OFFSET, .pow, .elstc, LIGHTNING_EFFECT, ENABLE_3D)
// If successfully connected
if .t.link[.ct] != 0 then
set Bool[dex] = true
set .t.target[.ct] = fog
set .ct = .ct + 1
endif
endif
endif
endloop
set fog = null
return false
endmethod
static if not LIBRARY_SpellEffectEvent then
private static method checkId takes nothing returns boolean
if GetSpellAbilityId() == MAIN_ID then
call thistype.onCast()
endif
return false
endmethod
endif
static method onInit takes nothing returns nothing
static if LIBRARY_SpellEffectEvent then
call RegisterSpellEffectEvent(MAIN_ID, function thistype.onCast)
else
set gg_trg_GummyLeash = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_GummyLeash, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(gg_trg_GummyLeash, Condition(function thistype.checkId))
endif
endmethod
endstruct
endscope