Name | Type | is_array | initial_value |
//TESH.scrollpos=23
//TESH.alwaysfold=0
library Event
//2.0.0.1
/////////////////////////////////////////////////////////////////////////
//function CreateEvent takes nothing returns integer
//function TriggerRegisterEvent takes trigger t, integer ev returns nothing
//function RegisterEvent takes boolexpr c, integer ev returns nothing
//function FireEvent takes 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=-1
//TESH.alwaysfold=0
library DestructableLib initializer Initialization
//* ============================================================================ *
//* Made by PitzerMike *
//* *
//* I made this to detect if a destructable is a tree or not. It works not only *
//* for the standard trees but also for custom destructables created with the *
//* object editor. It uses a footie as a dummy with the goul's harvest ability. *
//* The dummy ids can be changed though. I also added the IsDestructableDead *
//* function for completeness. *
//* ============================================================================ *
globals
private constant integer DUMMY_UNIT_ID = 'hfoo' // footman
private constant integer HARVEST_ID = 'Ahrl' // ghouls harvest
private constant player OWNING_PLAYER = Player(15)
private unit dummy = null
endglobals
function IsDestructableIsDead takes destructable dest returns boolean
return GetDestructableLife(dest) <= 0.405
endfunction
function IsDestructableIsTree takes destructable dest returns boolean
local boolean result = false
if (dest != null) then
call PauseUnit(dummy, false)
set result = IssueTargetOrder(dummy, "harvest", dest)
call PauseUnit(dummy, true) // stops order
endif
return result
endfunction
private function Initialization takes nothing returns nothing
set dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0.0, 0.0, 0.0)
call ShowUnit(dummy, false) // cannot enumerate
call UnitAddAbility(dummy, HARVEST_ID)
call UnitAddAbility(dummy, 'Aloc') // unselectable, invulnerable
call PauseUnit(dummy, true)
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
Lightning System v1.03
by Adiktuz
Basically it allows you to easily create timed or un-timed lightning effects
Features:
->You can create lightnings between two points, two unit, or a point and a unit.
->For lightnings attached to a unit, the system automatically updates the lightning
for changes on the unit (like unit position, height etc)
->Specify the height of the lightning
->Create timed-lightnings that get automatically destroyed when their duration is over
->Supports moving points too (but the lightnings are forced to be timed)
->Allows you to add actions that will run when a lightning has ended and during
update (time of which is equal to T32's period)
->Allows you to attach any type of custom data to each instance
Methods available
How to use: call Lightning.methodName(parameters)
unitToPoint takes unit unit1, real x, real y, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
unitToUnit takes unit unit1, unit unit2, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
pointToPoint takes real sourceX, real sourceY,real targetX, real targetY, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
pointToPointEx takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
unitToPointEx takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
pointToPointExZ takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real sourceCurZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
unitToPointExZ takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
Parameters:
unit unit1 -> unit end of the lightning
unit unit2 -> other unit end of the lightning for the UTU methods
real x,y -> X,Y coordinates of the point end of the lightning for the UTP methods
real sourceX,sourceY -> X,Y coordinates of the first point of the lightning for the PTP methods
real sourceZ,targetZ -> height of the lightning at each ends (actual height is calculated as z + height of unit (if there's a unit end) + locationZ)
real deltaSourceX,deltaSourceY,deltaTargetX,deltaTargetY,sourceCurZ,targetCurZ -> for the Ex and ExZ methods, the final value of each coordinate/height
(the lightning moves from sourceX to deltaSourceX, etc through the given time)
boolean timed -> whether the lightning has a timed life or not, defaulted to true for the Ex and ExZ methods
real duration -> timed life of the lightning
string leffect -> string name of the lightning effect
integer eventkey -> a key used to determine the correct update and end functions to run if available
(if you're not using the update and end events, you're probably saf to just set it to 0)
Note: all of the above methods return the Lightning instance so you can save it in a variable
Extra/Interface/Events:
registerUpdateEvent(integer eventkey, code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning with the
same eventkey as the one registered is updated (every T32_PERIOD)
registerEndEvent(integer eventkey, code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning with the
same eventkey as the one registered ends
NOTE: If you're going to use the next three functions/methods, make sure you set the
corresponding boolean at the globals block to true
registerGlobalUpdateEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning
is updated (every T32_PERIOD)
registerGlobalEndEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning ends
registerGlobalCreateEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning is created
To allow creation and usage of custom data that is attached to every instance
turn the boolean USE_CUSTOM_DATA to true
then to add a custom data to an instance:
for integers:
set YourLightningInstance.customData[integer key] = value
for others:
set YourLightningInstance.customData.type[integer key] = value
->type can be real,unit,trigger,effect, etc...
->integer key is the key that will point to the data, you can utilize StringHash if you want to use strings
something like: customData.real[StringHash("damage")]
then to load data:
YourLightningInstance.customData.type[integer key]
To forcefully remove a lightning:
remove() returns nothing
To obtain which lightning instance triggered the events:
instance()
*/
//DO NOT EDIT ANYTHING BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING
//Note: I've set the checkvisibility field to false because the lightnings look
// weird when I set it to true (sometimes they "jump")
library LightningSystem requires T32, Table
globals
//Set this to true if you're gonna use the global update event handler
private constant boolean USE_GLOBAL_UPDATE = false
//Set this to true if you're gonna use the global end event handler
private constant boolean USE_GLOBAL_END = false
//Set this to true if you're gonna use the global create event handler
private constant boolean USE_GLOBAL_CREATE = false
//Set this to true if you're gonna use the custom data feature
private constant boolean USE_CUSTOM_DATA = true
private location loc = Location(0,0)
public Table UpdateTable
public Table EndTable
private trigger globalUpdate
private trigger globalEnd
private trigger globalCreate
endglobals
private module init
static method onInit takes nothing returns nothing
set EndTable = Table.create()
set UpdateTable = Table.create()
static if USE_GLOBAL_UPDATE then
set globalUpdate = CreateTrigger()
endif
static if USE_GLOBAL_END then
set globalEnd = CreateTrigger()
endif
static if USE_GLOBAL_CREATE then
set globalCreate = CreateTrigger()
endif
endmethod
endmodule
struct Lightning extends array
lightning light
real sourceX
real targetX
real sourceY
real targetY
real sourceZ
real targetZ
real deltaSourceX
real deltaTargetX
real deltaSourceY
real deltaTargetY
real sourceCurZ
real targetCurZ
real deltaSourceZ
real deltaTargetZ
unit u1
unit u2
integer xtype
boolean timed
boolean moving
real duration
integer eventkey
Table customData
static thistype instance
private static integer instanceCount = 0
private static thistype recycle = 0
private thistype recycleNext
static method registerEndEvent takes integer eventkey, code toDo returns nothing
if not EndTable.handle.has(eventkey) then
set EndTable.trigger[eventkey] = CreateTrigger()
endif
call TriggerAddCondition(EndTable.trigger[eventkey],Filter(toDo))
endmethod
static method registerUpdateEvent takes integer eventkey, code toDo returns nothing
if not UpdateTable.handle.has(eventkey) then
set UpdateTable.trigger[eventkey] = CreateTrigger()
endif
call TriggerAddCondition(UpdateTable.trigger[eventkey],Filter(toDo))
endmethod
static method registerGlobalEndEvent takes code toDo returns nothing
call TriggerAddCondition(globalEnd,Filter(toDo))
endmethod
static method registerGlobalUpdateEvent takes code toDo returns nothing
call TriggerAddCondition(globalUpdate,Filter(toDo))
endmethod
static method registerGlobalCreateEvent takes code toDo returns nothing
call TriggerAddCondition(globalCreate,Filter(toDo))
endmethod
method remove takes nothing returns nothing
call DestroyLightning(this.light)
set instance = this
if EndTable.handle.has(this.eventkey) then
call TriggerEvaluate(EndTable.trigger[this.eventkey])
endif
static if USE_GLOBAL_END then
call TriggerEvaluate(globalEnd)
endif
call this.stopPeriodic()
set this.deltaSourceZ = 0.0
set this.deltaTargetZ = 0.0
set .recycleNext=recycle
set recycle=this
endmethod
static method new takes nothing returns thistype
local thistype this
if (recycle == 0) then
set instanceCount = instanceCount + 1
return instanceCount
else
set this = recycle
set recycle = recycle.recycleNext
endif
return this
endmethod
private method periodic takes nothing returns nothing
if this.xtype == 1 then
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceCurZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
if this.moving then
set this.targetX = this.targetX + this.deltaTargetX
set this.targetY = this.targetY + this.deltaTargetY
endif
call MoveLocation(loc, this.targetX,this.targetY)
if this.deltaTargetZ != 0.0 then
set this.targetZ = this.targetZ + this.deltaTargetZ
endif
set this.targetCurZ = targetZ + GetLocationZ(loc) + this.deltaTargetZ
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
elseif this.xtype == 2 then
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = GetUnitX(this.u2)
set this.targetY = GetUnitY(this.u2)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceCurZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetCurZ = targetZ + GetUnitFlyHeight(this.u2) + GetLocationZ(loc)
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
else
if this.moving then
set this.sourceX = this.sourceX + this.deltaSourceX
set this.targetX = this.targetX + this.deltaTargetX
set this.sourceY = this.sourceY + this.deltaSourceY
set this.targetY = this.targetY + this.deltaTargetY
call MoveLocation(loc, this.sourceX,this.sourceY)
if this.deltaSourceZ != 0.0 then
set this.sourceZ = this.sourceZ + this.deltaSourceZ
endif
if this.deltaTargetZ != 0.0 then
set this.targetZ = this.targetZ + this.deltaTargetZ
endif
set this.sourceCurZ = sourceZ + GetLocationZ(loc) + this.deltaSourceZ
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetCurZ = targetZ + GetLocationZ(loc) + this.deltaTargetZ
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
endif
endif
set instance = this
if UpdateTable.handle.has(this.eventkey) then
call TriggerEvaluate(UpdateTable.trigger[this.eventkey])
endif
static if USE_GLOBAL_UPDATE then
call TriggerEvaluate(globalUpdate)
endif
if this.timed then
set this.duration = this.duration - T32_PERIOD
if this.duration <= 0.0 then
call this.remove()
endif
endif
endmethod
implement T32x
static method unitToPoint takes unit unit1, real x, real y, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToUnit takes unit unit1, unit unit2, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = unit2
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = GetUnitX(this.u2)
set this.targetY = GetUnitY(this.u2)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetUnitFlyHeight(this.u2) + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 2
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPoint takes real sourceX, real sourceY,real targetX, real targetY, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPointEx takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
set this.deltaSourceX = (deltaSourceX - sourceX)*(T32_PERIOD/duration)
set this.deltaSourceY = (deltaSourceY - sourceY)*(T32_PERIOD/duration)
set this.deltaTargetX = (deltaTargetX - targetX)*(T32_PERIOD/duration)
set this.deltaTargetY = (deltaTargetY - targetY)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToPointEx takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
set this.deltaTargetX = (xx - x)*(T32_PERIOD/duration)
set this.deltaTargetY = (yx - y)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPointExZ takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real sourceCurZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
set this.deltaSourceZ = (sourceCurZ - sourceZ)*(T32_PERIOD/duration)
set this.deltaTargetZ = (targetCurZ-targetZ)*(T32_PERIOD/duration)
set this.deltaSourceX = (deltaSourceX - sourceX)*(T32_PERIOD/duration)
set this.deltaSourceY = (deltaSourceY - sourceY)*(T32_PERIOD/duration)
set this.deltaTargetX = (deltaTargetX - targetX)*(T32_PERIOD/duration)
set this.deltaTargetY = (deltaTargetY - targetY)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToPointExZ takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
set this.deltaTargetZ = (targetCurZ - targetZ)*(T32_PERIOD/duration)
set this.deltaTargetX = (xx - x)*(T32_PERIOD/duration)
set this.deltaTargetY = (yx - y)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
implement init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MoveSpeedXGUI /* v1.1.0.0
*************************************************************************************
*
* This library allows you to set unit movement speeds beyond 522 without bugs.
* This is an extension of the library MoveSpeedX, but is formatted for GUI use.
* Credits to Jesus4Lyf for the original system.
*
************************************************************************************
*
* SETTINGS
*/
globals
private constant real PERIOD = 0.03125
// This is the period on which all units will be run.
// If you lower this value, movement bonuses will be smoother,
// but will require more processing power (lag more).
// Also, the lower this is, the higher the move speed can be
// before it starts bugging on waypoints. The lowest valid
// period is 0.00125. A period of 0.00625 is very robust.
private constant real MARGIN = 0.01
// This is the margin of approximation when comparing reals.
// You will most likely not need to change this.
private real array udg_UnitSpeedX
endglobals
/*
************************************************************************************
*
* Functions
*
* function GetUnitMoveSpeedX takes unit whichUnit returns real
* - Returns a unit movement speed. The GUI function will
* - not return the correct value. This function will always
* - return the correct value regardless of whether the unit
* - has a movement speed beyond 522.
*
************************************************************************************
*
* REQUIREMENTS
*
* 1. JassNewGen Pack v5d
* 2. JassHelper 0.A.2.B
* 3. Any unit indexer
*
* HOW TO IMPLEMENT
*
* 1. Copy the 'folder' MoveSpeedX.
* 2. Paste it into your map.
* 3. Open "Advanced -> Gameplay Constants".
* 4. Checkmark "Use Custom Gameplay Constants".
* 5. Find the field, "Movement - Unit Speed - Maximum", change
* that to 522.
* 6. Find the field, "Movement - Unit Speed - Minimum", hold
* shift and click, and change it to 0.
* 7. Read HOW TO USE.
*
************************************************************************************
*
* HOW TO USE
*
* This system will automatically work by itself. You can use the
* normal GUI function for modifying unit movement speeds. Simply
* use "Unit - Set Movement Speed", input whatever value you want,
* and you are good to go! It will handle values beyond 522 by itself.
*
* HOWEVER, the GUI function will not return correct values if a unit
* has a movement speed greater than 522. To fix this, use the function
* GetUnitMoveSpeedX to return the correct value. A sample is given in
* the trigger "Speed Change" in the test map.
*
************************************************************************************
*
* NOTES
*
* Units that were issued orders as groups might not *always* end up in the proper
* "order". (they might not end up in an organized formation) They do sometimes though.
* This is only for units with speeds above 522.
*
* This also will not factor in bonuses and probably not slows either.
*
* Units may waddle around the point for a little bit. Reduce PERIOD to fix
* it a little bit. I recommend about 0.02 if you have really high speeds.
*
************************************************************************************/
private function ApproxEqual takes real A, real B returns boolean
return (A >= (B - MARGIN)) and (A <= (B + MARGIN))
endfunction
private module M
private static trigger issued = CreateTrigger()
thistype next
thistype prev
boolean enabled
unit curr
real speed
real x
real y
real ox
real oy
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
set this.enabled = false
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next // first instance in list
local real nx // the x-coordinate after tick
local real ny // the y-coordinate after tick
local real dx // distance between new-x and old-x
local real dy // distance between new-y and old-y
local real d // distance between new point and old point
local integer order // the unit's current order
local unit u // unit being affected
loop
exitwhen this == 0
set u = .curr
set nx = GetUnitX(u)
set ny = GetUnitY(u)
if IsUnitType(u, UNIT_TYPE_DEAD) then
call this.destroy()
elseif not ApproxEqual(nx, .x) or not ApproxEqual(ny, .y) then
if (not IsUnitPaused(u)) and GetUnitAbilityLevel(u, 'BSTN') == 0 and GetUnitAbilityLevel(u, 'BPSE') == 0 then
set order = GetUnitCurrentOrder(u)
set dx = nx - .x
set dy = ny - .y
set d = SquareRoot(dx * dx + dy * dy)
set dx = dx / d * .speed // move the unit offset-x by this
set dy = dy / d * .speed // move the unit offset-y by this
if (order == 851986 or order == 851971) and /*
*/ (this.ox - nx)*(this.ox - nx) < (dx*dx) and /*
*/ (this.oy - ny)*(this.oy - ny) < (dy*dy) then
// if the unit is issued a move or smart order and they are near their destination
// then move them there instantly (removes a bit of glitchyness towards the end)
call SetUnitX(u, .ox)
call SetUnitY(u, .oy)
set .x = .ox
set .y = .oy
call IssueImmediateOrderById(u, 851972) // order them to stop
else
set .x = nx + dx
set .y = ny + dy
call SetUnitX(u, .x)
call SetUnitY(u, .y)
endif
endif
endif
set this = this.next
endloop
set u = null
endmethod
static method create takes unit whichUnit, real newSpeed returns thistype
local thistype this = GetUnitUserData(whichUnit)
set this.next = thistype(0).next
set thistype(0).next.prev = this
set thistype(0).next = this
set this.prev = 0
set this.curr = whichUnit
set this.speed = (newSpeed - 522) * PERIOD
set this.x = GetUnitX(whichUnit)
set this.y = GetUnitY(whichUnit)
set this.enabled = true
return this
endmethod
static method update takes unit whichUnit, real newSpeed returns nothing
local thistype this = GetUnitUserData(whichUnit)
if this.enabled then
if newSpeed > 522 then
set this.speed = (newSpeed - 522) * PERIOD
else
call this.destroy()
endif
elseif newSpeed > 522 then
call thistype.create(whichUnit, newSpeed)
endif
endmethod
private static method storeOrderPoint takes nothing returns boolean
local thistype this = GetUnitUserData(GetTriggerUnit())
set this.ox = GetOrderPointX()
set this.oy = GetOrderPointY()
return false
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), PERIOD, true, function thistype.periodic)
call TriggerRegisterAnyUnitEventBJ(issued, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(issued, Condition(function thistype.storeOrderPoint))
endmethod
endmodule
private struct MoveSpeedStruct extends array
implement M
endstruct
function GetUnitMoveSpeedX takes unit whichUnit returns real
if MoveSpeedStruct(GetUnitUserData(whichUnit)).enabled then
return udg_UnitSpeedX[GetUnitUserData(whichUnit)]
endif
return GetUnitMoveSpeed(whichUnit)
endfunction
function SetUnitMoveSpeedX takes unit whichUnit, real newSpeed returns nothing
call MoveSpeedStruct.update(whichUnit, newSpeed)
set udg_UnitSpeedX[GetUnitUserData(whichUnit)] = newSpeed
endfunction
hook SetUnitMoveSpeed SetUnitMoveSpeedX
endlibrary
//TESH.scrollpos=12
//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
//
library SpellEffectEvent initializer Init requires RegisterPlayerUnitEvent
globals
private hashtable ht = InitHashtable()
endglobals
private function Run takes nothing returns nothing
call TriggerEvaluate(LoadTriggerHandle(ht, 0, GetSpellAbilityId()))
endfunction
function RegisterSpellEffectEvent takes integer i, code c returns nothing
if not HaveSavedHandle(ht, 0, i) then
call SaveTriggerHandle(ht, 0, i, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(ht, 0, i), Filter(c))
endfunction
private function Init takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function Run)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimedEffect /* v1.3
*************************************************************************************
*
* Allows one to create timed effects on 3d points or units.
*
*************************************************************************************
*
* API
*
* static method createOnPoint takes string mdl, real x, real y, real z, real duration returns thistype
* - starts a timed effect on a 3d point
*
* static method createOnUnit takes string mdl, unit u, string attach, real duration returns thistype
* - starts a timed effect on a unit
*
* method operator newDuration= takes real value returns nothing
* - sets an instance' new duration
*
* method destroy takes nothing returns nothing
* - destroys an instance
*
*************************************************************************************
*
* Credits
*
* PurgeAndFire for the OTip trick.
*
**************************************************************************************/
struct TimedEffect extends array
private static timer t = CreateTimer()
private static thistype instance = 0
private static integer count = 0
private static effect tempEffect
private static destructable platform
private thistype recycle
private thistype next
private thistype prev
private effect effects
private real time
method destroy takes nothing returns nothing
call DestroyEffect(effects)
set effects = null
set time = 0
set prev.next = next
set next.prev = prev
set recycle = thistype(0).recycle
set thistype(0).recycle = this
set count = count - 1
if 0 == count then
call PauseTimer(t)
endif
endmethod
method operator newDuration= takes real value returns nothing
set time = time + value
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen 0 == this
set time = time - 0.031250000
if 0 >= time then
call destroy()
endif
set this = next
endloop
endmethod
private static method allocate takes nothing returns thistype
local thistype this = thistype(0).recycle
if 0 == this then
set this = instance + 1
set instance = this
else
set thistype(0).recycle = recycle
endif
set next = 0
set prev = 0
set thistype(0).prev.next = this
set thistype(0).prev = this
set count = count + 1
if 1 == count then
call TimerStart(t, 0.031250000, true, function thistype.periodic)
endif
return this
endmethod
static method createOnPoint takes string mdl, real x, real y, real z, real duration returns thistype
local thistype this
if 0 < z then
set platform = CreateDestructableZ('OTip', x, y, z, 0, 1, 0)
endif
set tempEffect = AddSpecialEffect(mdl, x, y)
if null != platform then
call RemoveDestructable(platform)
endif
if duration >= 0.031250000 then
set this = allocate()
set time = duration
set effects = tempEffect
return this
endif
call DestroyEffect(tempEffect)
set tempEffect = null
return 0
endmethod
static method createOnUnit takes string mdl, unit u, string attach, real duration returns thistype
local thistype this
set tempEffect = AddSpecialEffectTarget(mdl, u, attach)
if duration >= 0.031250000 then
set this = allocate()
set effects = tempEffect
set time = duration
return this
endif
call DestroyEffect(tempEffect)
set tempEffect = null
return 0
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtilsEx requires optional Table
/*************************************************
*
* TimerUtilsEx
* v2.1.0.2
* By Vexorian, Bribe & Magtheridon96
*
* Original version by Vexorian.
*
* Flavors:
* Hashtable:
* - RAM: Minimal
* - TimerData: Slow
*
* Array:
* - RAM: Maximal
* - TimerData: Fast
*
* All the functions have O(1) complexity.
* The Array version is the fastest, but the hashtable
* version is the safest. The Array version is still
* quite safe though, and I would recommend using it.
* The system is much slower in debug mode.
*
* Optional Requirement:
* - Table by Bribe
* - hiveworkshop.com/forums/showthread.php?t=188084
*
* API:
* ----
* - function NewTimer takes nothing returns timer
* - Returns a new timer from the stack.
* - function NewTimerEx takes integer i returns timer
* - Returns a new timer from the stack and attaches a value to it.
* - function ReleaseTimer takes timer t returns integer
* - Throws a timer back into the stack. Also returns timer data.
* - function SetTimerData takes timer t, integer value returns nothing
* - Attaches a value to a timer.
* - function GetTimerData takes timer t returns integer
* - Returns the attached value.
*
*************************************************/
// Configuration
globals
// Use hashtable, or fast array?
private constant boolean USE_HASH = false
// Max Number of Timers Held in Stack
private constant integer QUANTITY = 1024
endglobals
globals
private timer array tT
private integer tN = 0
endglobals
private module Init
private static method onInit takes nothing returns nothing
static if not USE_HASH then
local integer i = QUANTITY
loop
set i = i - 1
set tT[i] = CreateTimer()
exitwhen i == 0
endloop
set tN = QUANTITY
elseif LIBRARY_Table then
set tb = Table.create()
endif
endmethod
endmodule
// JassHelper doesn't support static ifs for globals.
private struct Data extends array
static if not USE_HASH then
static integer array data
endif
static if LIBRARY_Table then
static Table tb = 0
else
static hashtable ht = InitHashtable()
endif
implement Init
endstruct
// Double free protection
private function ValidTimer takes integer i returns boolean
static if LIBRARY_Table then
return Data.tb.boolean[-i]
else
return LoadBoolean(Data.ht, i, 1)
endif
endfunction
private function Get takes integer id returns integer
debug if not ValidTimer(id) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to get data from invalid timer.")
debug endif
static if USE_HASH then
static if LIBRARY_Table then
return Data.tb[id]
else
return LoadInteger(Data.ht, id, 0)
endif
else
return Data.data[id - 0x100000]
endif
endfunction
private function Set takes integer id, integer data returns nothing
debug if not ValidTimer(id) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to attach data to invalid timer.")
debug endif
static if USE_HASH then
static if LIBRARY_Table then
set Data.tb[id] = data
else
call SaveInteger(Data.ht, id, 0, data)
endif
else
set Data.data[id - 0x100000] = data
endif
endfunction
function SetTimerData takes timer t, integer data returns nothing
call Set(GetHandleId(t), data)
endfunction
function GetTimerData takes timer t returns integer
return Get(GetHandleId(t))
endfunction
function NewTimerEx takes integer data returns timer
local integer id
if tN == 0 then
static if USE_HASH then
set tT[0] = CreateTimer()
else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: No Timers In The Stack! You must increase 'QUANTITY'")
return null
endif
else
set tN = tN - 1
endif
set id = GetHandleId(tT[tN])
static if LIBRARY_Table then
set Data.tb.boolean[-id] = true
else
call SaveBoolean(Data.ht, id, 1, true)
endif
call Set(id, data)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
function ReleaseTimer takes timer t returns integer
local integer id = GetHandleId(t)
local integer data = 0
// Pause the timer just in case.
call PauseTimer(t)
// Make sure the timer is valid.
if ValidTimer(id) then
// Get the timer's data.
set data = Get(id)
// Unmark handle id as a valid timer.
static if LIBRARY_Table then
call Data.tb.boolean.remove(-id)
else
call RemoveSavedBoolean(Data.ht, id, 1)
endif
//If it's not run in USE_HASH mode, this next block is useless.
static if USE_HASH then
//At least clear hash memory while it's in the recycle stack.
static if LIBRARY_Table then
call Data.tb.remove(id)
else
call RemoveSavedInteger(Data.ht, id, 0)
endif
// If the recycle limit is reached
if tN == QUANTITY then
// then we destroy the timer.
call DestroyTimer(t)
return data
endif
endif
//Recycle the timer.
set tT[tN] = t
set tN = tN + 1
//Tried to pass a bad timer.
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to release non-active timer!")
endif
//Return Timer Data.
return data
endfunction
endlibrary
library TimerUtils requires TimerUtilsEx
endlibrary
//TESH.scrollpos=184
//TESH.alwaysfold=0
library UnitEvent /* v3.0.1.2
*************************************************************************************
*
* Makes new unit events for Warcraft 3.
*
*************************************************************************************
*
* */uses/*
*
* */ UnitIndexer /* (4.0.2.3) - hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
* */ RegisterPlayerUnitEvent /* hiveworkshop.com/forums/jass-functions-413/snippet-registerplayerunitevent-203338/
*
************************************************************************************
*
* Events: registered via Event API
* static constant Event UnitEvent.REMOVE
* static constant Event UnitEvent.DECAY
* static constant Event UnitEvent.EXPLODE
* static constant Event UnitEvent.RESURRECT
* static constant Event UnitEvent.REINCARNATE
* static constant Event UnitEvent.ANIMATE
* static constant Event UnitEvent.START_REINCARNATE
*
* Functions:
* function IsUnitDead takes integer index returns boolean
* function IsUnitReincarnating takes integer index returns boolean
* function IsUnitAnimated takes integer index returns boolean
* function GetEventUnitId takes nothing returns integer
* function GetEventUnit takes nothing returns unit
*
*******************************************************************
*
* module UnitEventStruct
*
* - A pseudo module interface that runs a set of methods if they exist.
*
* - implements UnitIndexStruct automatically
*
* 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 remove takes nothing returns nothing
* private method decay takes nothing returns nothing
* private method explode takes nothing returns nothing
* private method resurrect takes nothing returns nothing
* private method startReincarnate takes nothing returns nothing
* private method reincarnate takes nothing returns nothing
* private method animate takes nothing returns nothing
*
************************************************************************************/
//! textmacro UNIT_EVENT_MACRO
globals
private constant integer UNITS_UNIT_EVENT = 'e000'
private real h=1000
private timer time=null
private real array j
private boolean array k
private boolean array z
private boolean array x
private boolean array v
private integer array b
private timer m=CreateTimer()
endglobals
function GetEventUnitId takes nothing returns integer
return o
endfunction
function GetEventUnit takes nothing returns unit
return e[o]
endfunction
function IsUnitDead takes integer index returns boolean
return z[index]
endfunction
function IsUnitReincarnating takes integer index returns boolean
return x[index]
endfunction
function IsUnitAnimated takes integer index returns boolean
return k[index]
endfunction
private function OnReincarnateStart takes nothing returns nothing
local integer i=o
set o=b[0]
loop
if (x[o]) then
call FireEvent(UnitEvent.START_REINCARNATE)
endif
set o=b[o]
exitwhen 0==o
endloop
set b[0]=0
set o=i
endfunction
private function OnDeath takes nothing returns boolean
local unit u=GetTriggerUnit()
local integer i=GetUnitUserData(u)
local integer d=o
if (u==e[i]) then
set z[i]=true
set x[i]=false
if (not k[i]) then
set j[i]=TimerGetElapsed(time)
else
set v[i]=true
set k[i]=false
set o=i
call FireEvent(UnitEvent.EXPLODE)
set o=d
endif
endif
set u=null
return false
endfunction
private module UnitEventModule
local unit u=GetFilterUnit()
local integer s=GetUnitUserData(u)
local integer d=o
if (u==e[s]) then
set o=s
if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)) then
set x[s]=false
set k[s]=false
if (not v[s]) then
if (z[s] and h<=TimerGetElapsed(time)-j[s]) then
set z[s]=false
call FireEvent(UnitEvent.DECAY)
else
set z[s]=false
call FireEvent(UnitEvent.REMOVE)
endif
else
set z[s]=false
set v[s]=false
endif
static if not LIBRARY_UnitList then
if (not a)then
set n[p[s]]=n[s]
set p[n[s]]=p[s]
endif
else
set n[p[s]]=n[s]
set p[n[s]]=p[s]
call GroupRemoveUnit(g,e[s])
endif
call FireEvent(DEINDEX)
if (0==lc[s]) then
set n[s]=y
set y=s
endif
set e[s]=null
elseif (.405<GetWidgetLife(u) and 0!=GetUnitTypeId(u)) then
if (x[s]) then
call FireEvent(UnitEvent.REINCARNATE)
set x[s]=false
elseif (z[s]) then
set z[s]=false
if (IsUnitType(u,UNIT_TYPE_SUMMONED)) then
set k[s]=true
call FireEvent(UnitEvent.ANIMATE)
else
call FireEvent(UnitEvent.RESURRECT)
endif
endif
else
set x[s]=true
set b[s]=b[0]
set b[0]=s
call TimerStart(m,0,false,function OnReincarnateStart)
endif
set o=d
endif
set u=null
endmodule
//! endtextmacro
//! textmacro UNIT_EVENT_MACRO_2
private module UnitEventInits
private static method decayer takes nothing returns boolean
set h=TimerGetElapsed(time)-.9375
call DestroyTrigger(GetTriggeringTrigger())
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
local unit u
set REMOVE=CreateEvent()
set DECAY=CreateEvent()
set EXPLODE=CreateEvent()
set RESURRECT=CreateEvent()
set REINCARNATE=CreateEvent()
set ANIMATE=CreateEvent()
set START_REINCARNATE=CreateEvent()
set UnitIndexer.enabled=false
set u=CreateUnit(Player(14),UNITS_UNIT_EVENT,WorldBounds.maxX,WorldBounds.maxY,0)
set UnitIndexer.enabled=true
call KillUnit(u)
call ShowUnit(u,false)
call TriggerRegisterUnitEvent(t,u,EVENT_UNIT_ISSUED_ORDER)
call TriggerAddCondition(t,function thistype.decayer)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
call TimerStart(time,1000000,false,null)
set u=null
set t=null
endmethod
endmodule
struct UnitEvent extends array
readonly static Event REMOVE
readonly static Event DECAY
readonly static Event EXPLODE
readonly static Event RESURRECT
readonly static Event REINCARNATE
readonly static Event ANIMATE
readonly static Event START_REINCARNATE
implement UnitEventInits
endstruct
//! endtextmacro
//! textmacro UNIT_EVENT_STRUCT_MACRO
private static method onInit takes nothing returns nothing
static if thistype.remove.exists then
call RegisterEvent(Condition(function thistype.onRemoveEvent),UnitEvent.REMOVE)
endif
static if thistype.decay.exists then
call RegisterEvent(Condition(function thistype.onDecayEvent),UnitEvent.DECAY)
endif
static if thistype.explode.exists then
call RegisterEvent(Condition(function thistype.onExplodeEvent),UnitEvent.EXPLODE)
endif
static if thistype.resurrect.exists then
call RegisterEvent(Condition(function thistype.onResurrectEvent),UnitEvent.RESURRECT)
endif
static if thistype.startReincarnate.exists then
call RegisterEvent(Condition(function thistype.onStartReincarnateEvent),UnitEvent.START_REINCARNATE)
endif
static if thistype.reincarnate.exists then
call RegisterEvent(Condition(function thistype.onReincarnateEvent),UnitEvent.REINCARNATE)
endif
static if thistype.animate.exists then
call RegisterEvent(Condition(function thistype.onAnimateEvent),UnitEvent.ANIMATE)
endif
endmethod
//! endtextmacro
module UnitEventStruct
implement UnitIndexStruct
static if thistype.remove.exists then
private static method onRemoveEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).remove()
endif
return false
endmethod
endif
static if thistype.decay.exists then
private static method onDecayEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).decay()
endif
return false
endmethod
endif
static if thistype.explode.exists then
private static method onExplodeEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).explode()
endif
return false
endmethod
endif
static if thistype.resurrect.exists then
private static method onResurrectEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).resurrect()
endif
return false
endmethod
endif
static if thistype.startReincarnate.exists then
private static method onStartReincarnateEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).startReincarnate()
endif
return false
endmethod
endif
static if thistype.reincarnate.exists then
private static method onReincarnateEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).reincarnate()
endif
return false
endmethod
endif
static if thistype.animate.exists then
private static method onAnimateEvent takes nothing returns boolean
if (thistype(GetEventUnitId()).allocated) then
call thistype(GetEventUnitId()).animate()
endif
return false
endmethod
endif
static if thistype.remove.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.decay.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.explode.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.resurrect.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.startReincarnate.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.reincarnate.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
elseif thistype.animate.exists then
//! runtextmacro UNIT_EVENT_STRUCT_MACRO()
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.8
*************************************************************************************
*
* 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/
*
************************************************************************************
*
* 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 constant integer ABILITIES_UNIT_INDEXER = 'A002'
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 method operator enabled takes nothing returns boolean
return IsTriggerEnabled(q)
endmethod
static method operator enabled= takes boolean b returns nothing
if (b) then
call DisableTrigger(q)
call DisableTrigger(l)
else
call EnableTrigger(q)
call EnableTrigger(l)
endif
endmethod
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (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=23
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xecast initializer init requires xebasic
//******************************************************************************
// xecast 0.9
// ------
// Because dummy casters REALLY ARE this complicated!
//
//******************************************************************************
//==============================================================================
globals
private constant integer MAXINSTANCES = 8190
//this is a lot, unless you leak xecast objects (which is a bad thing)
private constant integer INITIAL_DUMMY_COUNT = 12
//don't allow to keep more than DUMMY_STACK_LIMIT innactive dummy units :
private constant integer DUMMY_STACK_LIMIT = 50
// If your map does not give visibility to all players, or
// for other reasons, you might want xecast to work on
// units that are not visible to the player, in that case
// change this to true, else it is just a performance loss.
private constant boolean FORCE_INVISIBLE_CAST = false
//When AUTO_RESET_MANA_COOLDOWN is set to true, xecast will reset
// the dummy unit's cooldown and mana before casting every spell.
// it is a performance penalty, so if you are sure that all dummy spells
// in your map got 0 mana and cooldown cost, you may set it to false.
private constant boolean AUTO_RESET_MANA_COOLDOWN = true
endglobals
//=========================================================================
// Please notice all textmacros in this library are considered private.
// in other words: DON'T RUN THOSE TEXTMACROS!
//
private keyword structinit
globals
private real EPSILON=0.001 //noticed in war3 this is the sort of precision we want...
endglobals
struct xecast[MAXINSTANCES]
public integer abilityid = 0 //ID (rawcode) of the ability to cast
public integer level = 1 //Level of the ability to cast
public real recycledelay = 0.0 //Please notice, some spells need a recycle delay
// This is, a time period before they get recycle.
// For example, some spells are not instant, there is
// also the problem with damaging spells, this recycle
// delay must be large enough to contain all the time
// in which the spell can do damage.
public player owningplayer=Player(15) //which player to credit for the ability cast?
//notice this can also affect what units are targeteable
//==================================================================================================
// You need an order id for the ability so the dummy unit is able to cast it, two ways to assign it
// set instance.orderid = 288883 //would assign an integer orderid
// set instance.orderstring = "chainlightning" //would assign an orderstring
// (as those in the object editor)
//
method operator orderid= takes integer v returns nothing
set .oid=v
endmethod
method operator orderstring= takes string s returns nothing
set .oid=OrderId(s)
endmethod
//=================================================================================================
// Finally, you can determine from which point to cast the ability: z is the height coordinate.
//
public boolean customsource=false //Use a custom casting source?
public real sourcex // Determine the casting source for the dummy spell, require customsource =true
public real sourcey // You might prefer to use the setSourcePoint method
public real sourcez=0.0 //
method setSourcePoint takes real x, real y, real z returns nothing
set .sourcex=x
set .sourcey=y
set .sourcez=z
set .customsource=true
endmethod
method setSourceLoc takes location loc, real z returns nothing
set .sourcex=GetLocationX(loc)
set .sourcey=GetLocationY(loc)
set .sourcez=z
set .customsource=true
endmethod
private boolean autodestroy = false
//========================================================================================================
// you are always allowed to use .create() but you can also use createBasic which sets some things that
// are usually necessary up.
//
public static method createBasic takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
return r
endmethod
//========================================================================================================
// Just like the above one, but the instance will self destruct after a call to any cast method
// (recommended)
//
public static method createBasicA takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// Just like create, but the struct instance self destructs after a call to any cast method
// (Recommended)
//
public static method createA takes nothing returns xecast
local xecast r=xecast.allocate()
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// So, create the dummy, assign options and cast the skill!
// .castOnTarget(u) : If you want to hit a unit u with the ability, supports FORCE_INVISIBLE_CAST.
// .castOnWidgetTarget(w) : If you want to hit a widget w with the ability.
// .castOnPoint(x,y) : If you want to hit a point (x,y) with the ability.
// .castInPoint(x,y) : For spells like warstomp which do not have a target.
// .castOnAOE(x,y,radius) : Classic area of effect cast. Considers collision size.
// .castOnGroup(g) : Cast unit the unit group g, notice it will empty the group yet not destroy it.
//
//**********************************************************************************************************
// The implementation of such methods follows:
private static unit array dummystack
private static integer top=0
private static unit instantdummy
private integer oid=0
private static timer gametime
private static timer T
private static unit array recycle
private static real array expiretime
private static integer rn=0
//==========================================================================================================
// private dorecycle method, sorry but I need this up here.
//
private static method dorecycle takes nothing returns nothing
local unit u =.recycle[0]
local integer l
local integer r
local integer p
local real lt
call IssueImmediateOrder(u,"stop")// bugfix, see: http://www.wc3c.net/showpost.php?p=1131163&postcount=5
call UnitRemoveAbility(u,GetUnitUserData(u))
call SetUnitUserData(u,0)
call SetUnitFlyHeight(u,0,0)
call PauseUnit(u,false)
if(.top==DUMMY_STACK_LIMIT) then
call RemoveUnit(u)
else
set .dummystack[.top]=u
set .top=.top+1
endif
set .rn=.rn-1
if(.rn==0) then
return
endif
set p=0
set lt=.expiretime[.rn]
loop
set l=p*2+1
exitwhen l>=.rn
set r=p*2+2
if(r>=.rn)then
if(.expiretime[l]<lt) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
exitwhen true
endif
elseif (lt<=.expiretime[l]) and (lt<=.expiretime[r]) then
exitwhen true
elseif (.expiretime[l]<.expiretime[r]) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
set .expiretime[p]=.expiretime[r]
set .recycle[p]=.recycle[r]
set p=r
endif
endloop
set .recycle[p]=.recycle[.rn]
set .expiretime[p]=lt
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
endmethod
private static trigger abilityRemove
// Repetitive process and no inline implemented for large functions, so for now it is a textmacro:
//! textmacro xecast_allocdummy
if(.recycledelay<EPSILON) then
set dummy=.instantdummy
call SetUnitOwner(dummy,.owningplayer,false)
elseif (.top>0) then
set .top=.top-1
set dummy=.dummystack[.top]
call SetUnitOwner(dummy,.owningplayer,false)
else
set dummy=CreateUnit(.owningplayer,XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,dummy,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(dummy,'Aloc')
call UnitAddAbility(dummy,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(dummy,XE_HEIGHT_ENABLER)
endif
call UnitAddAbility(dummy, abilityid)
static if AUTO_RESET_MANA_COOLDOWN then
call UnitResetCooldown(dummy)
call SetUnitState(dummy, UNIT_STATE_MANA, 10000.0)
endif
if(level>1) then
call SetUnitAbilityLevel(dummy, abilityid, level)
endif
//! endtextmacro
private static integer cparent
private static integer current
private static real cexpire
//! textmacro xecast_deallocdummy
if(.recycledelay>=EPSILON) then
set .cexpire=TimerGetElapsed(.gametime)+.recycledelay
set .current=.rn
set .rn=.rn+1
loop
exitwhen (.current==0)
set .cparent=(.current-1)/2
exitwhen (.expiretime[.cparent]<=.cexpire)
set .recycle[.current]=.recycle[.cparent]
set .expiretime[.current]=.expiretime[.cparent]
set .current=.cparent
endloop
set .expiretime[.current]=.cexpire
set .recycle[.current]=dummy
call SetUnitUserData(dummy,.abilityid)
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
else
call SetUnitUserData(dummy,0)
call SetUnitFlyHeight(dummy,0,0)
call UnitRemoveAbility(dummy,.abilityid)
endif
//! endtextmacro
method castOnTarget takes unit target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
static if (FORCE_INVISIBLE_CAST) then
call UnitShareVision(target, .owningplayer, true)
call IssueTargetOrderById(dummy,this.oid,target)
call UnitShareVision(target, .owningplayer, false)
else
call IssueTargetOrderById(dummy,this.oid,target)
endif
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
//accepts units, items and destructables, if you know it is
// a unit it is better to use castOnTarget since that would
// be able to use FORCE_INVISIBLE_CAST if necessary.
//
method castOnWidgetTarget takes widget target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
call IssueTargetOrderById(dummy,this.oid,target)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
endif
call IssuePointOrderById(dummy,this.oid,x,y)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castOnPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//ignores custom source x and y (for obvious reasons)
method castInPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitFlyHeight(dummy,.sourcez,0.0)
endif
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call IssueImmediateOrderById(dummy,this.oid)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castInLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castInPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//===================================================================================================
// For method castOnAOE:
//
private static group enumgroup
private static real aoex
private static real aoey
private static real aoeradius
private static xecast cinstance
private static boolexpr aoefunc
// Might look wrong, but this is the way to make it consider collision size, a spell that
// got a target circle and uses this method will let the user know which units it will
// hit with the mass cast.
static method filterAOE takes nothing returns boolean
local unit u=GetFilterUnit()
if IsUnitInRangeXY(u, .aoex, .aoey, .aoeradius) then
call .cinstance.castOnTarget(u)
endif
set u=null
return false
endmethod
//
method castOnAOE takes real x, real y, real radius returns nothing
local boolean ad=this.autodestroy
if(ad) then
set this.autodestroy=false
endif
set .aoex=x
set .aoey=y
set .aoeradius=radius
set .cinstance=this
call GroupEnumUnitsInRange(.enumgroup,x,y,radius + XE_MAX_COLLISION_SIZE , .aoefunc)
if(ad) then
call this.destroy()
endif
endmethod
method castOnAOELoc takes location loc,real radius returns nothing
call .castOnAOE(GetLocationX(loc),GetLocationY(loc),radius)
endmethod
//==================================================================================================
// A quick and dirt castOnGroup method, perhaps it'll later have castOntarget inlined, but not now
//
method castOnGroup takes group g returns nothing
local boolean ad=this.autodestroy
local unit t
if(ad) then
set this.autodestroy=false
endif
loop
set t=FirstOfGroup(g)
exitwhen(t==null)
call GroupRemoveUnit(g,t)
call .castOnTarget(t)
endloop
if(ad) then
call this.destroy()
endif
endmethod
private static method removeAbility takes nothing returns boolean
local unit u=GetTriggerUnit()
if(GetUnitUserData(u)!=0) then
call PauseUnit(u,true)
endif
//This is necessary, picture a value for recycle delay that's higher than the casting time,
//for example if the spell does dps, if you leave the dummy caster with the ability and it
//is owned by an AI player it will start casting the ability on player units, so it is
// a good idea to pause it...
set u=null
return true
endmethod
//===================================================================================================
// structinit is a scope private keyword.
//
static method structinit takes nothing returns nothing
local integer i=INITIAL_DUMMY_COUNT+1
local unit u
set .aoefunc=Condition(function xecast.filterAOE)
set .enumgroup=CreateGroup()
set .abilityRemove = CreateTrigger()
loop
exitwhen (i==0)
set u=CreateUnit(Player(15),XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,u,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(u,'Aloc')
call UnitAddAbility(u,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(u,XE_HEIGHT_ENABLER)
set .dummystack[.top]=u
set .top=.top+1
set i=i-1
endloop
call TriggerAddCondition(.abilityRemove, Condition(function xecast.removeAbility ) )
set .top=.top-1
set .instantdummy=.dummystack[.top]
set .T=CreateTimer()
set .gametime=CreateTimer()
call TimerStart(.gametime,12*60*60,false,null)
endmethod
endstruct
private function init takes nothing returns nothing
call xecast.structinit()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AVL /* v1.1.0.6
*************************************************************************************
*
* An AVL Tree where all nodes are connected by an AVL tree, a linked list, and
* are referenced by a hashtable.
*
*************************************************************************************
*
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
* module AVLTree
*
* Interface:
* method lessThan takes thistype value returns boolean
* method greaterThan takes thistype value returns boolean
*
* readonly thistype tree
* - Tree pointer. Accessible from any node on the tree.
* readonly thistype root
* - Root of two children (if root is 0, it's the tree's root)
* readonly thistype left
* readonly thistype right
* readonly thistype down (tree pointer only)
* - The tree has only 1 child, the root
*
* readonly thistype next
* readonly thistype prev
* readonly boolean head
*
* readonly thistype value
* - Value stored in node
*
* static method create takes nothing returns thistype
* method destroy takes nothing returns nothing
*
* method search takes thistype value returns thistype
* - Returns the node containing the value
* - If the value didn't exist, it will return 0
* method searchClose takes thistype value, boolean low returns thistype
* - Searches for the best match to the value.
* -
* - Low = True
* - Search for the closest value <= to the target value
* -
* - Low = False
* - Search for the closest value >= to the target value
*
* method has takes thistype value returns boolean
* - Returns true if a node contains the value
*
* method add takes thistype value returns thistype
* - Returns new node containing added value. If the value
* - was already in the tree, it returns the node that already
* - contained that value.
* method delete takes nothing returns nothing
* - Deletes node
*
* -> Delete the value 15 from the tree if it exists
* -> call search(15).delete()
*
* method clear takes nothing returns thistype
* - Clears the tree of all nodes
* - Returns tree pointer (just in case some node in the tree was passed in rather than the tree)
*
************************************************************************************/
module AVL
private static Table array table //for O(1) searches on specific values
private static thistype c=0 //instance count
private static thistype array b //root
private static thistype array l //left
private static thistype array r //right
private static integer array h //height
private static thistype array p //parent
private static thistype array v //value
private static integer array nn //next node
private static integer array pn //prev node
private static integer array ro //root
method operator tree takes nothing returns thistype
return ro[this]
endmethod
method operator root takes nothing returns thistype
return p[this]
endmethod
method operator down takes nothing returns thistype
return b[this]
endmethod
method operator left takes nothing returns thistype
return l[this]
endmethod
method operator right takes nothing returns thistype
return r[this]
endmethod
method operator value takes nothing returns thistype
return v[this]
endmethod
method operator next takes nothing returns thistype
return nn[this]
endmethod
method operator prev takes nothing returns thistype
return pn[this]
endmethod
method operator head takes nothing returns boolean
return 0==p[this]
endmethod
private method getHeight takes nothing returns integer
//return the bigger leaf height
if (h[l[this]]>h[r[this]]) then
return h[l[this]]+1
endif
return h[r[this]]+1
endmethod
private method updateParent takes integer n returns nothing
//only update the parent of a target leaf if that leaf isn't the original leaf
if (n!=this) then
//update the parent point to
//first leaf
if (0==p[p[this]]) then
set b[p[this]]=n
//left
elseif (l[p[this]]==this) then
set l[p[this]]=n
//right
else
set r[p[this]]=n
endif
//update the leaf point back
//if the leaf isn't null, update the leaf's parent
if (0!=n) then
set p[n]=p[this]
endif
endif
endmethod
private method finishRotate takes thistype n returns nothing
//this code is identical in rotateLeft and rotateRight, so it has
//been abstracted to a method
call updateParent(n)
set p[this]=n
set h[this]=getHeight()
set h[n]=n.getHeight()
endmethod
private method rotateLeft takes nothing returns thistype
local thistype n=r[this]
set r[this]=l[n]
set p[l[n]]=this
set l[n]=this
call finishRotate(n)
return n
endmethod
private method rotateRight takes nothing returns thistype
local thistype n=l[this]
set l[this]=r[n]
set p[r[n]]=this
set r[n]=this
call finishRotate(n)
return n
endmethod
//return the difference between the left and right leaf heights
private method getBalanceFactor takes nothing returns integer
return h[l[this]]-h[r[this]]
endmethod
private static method allocate takes nothing returns thistype
local integer n
if (0==nn[0]) then
set n=c+1
set c=n
else
set n=nn[0] //notice that the recycler uses the next pointer
//the reason it is used is for fast clear/destroy and to save
//a variable
set nn[0]=nn[n]
endif
set l[n]=0 //left leaf
set r[n]=0 //right leaf
set b[n]=0 //down leaf (first node of tree)
set h[n]=1 //height (a node will always have at least a height of 1 for itself)
return n
endmethod
static method create takes nothing returns thistype
local integer n=allocate()
set p[n]=0 //the parent of the tree node is 0
//initialize tree next and prev
set nn[n]=n
set pn[n]=n
//tree value table for O(1) searches on specific values
set table[n]=Table.create()
//tree root is itself (allows one to pass any node from the tree into the methods)
set ro[n]=n
return n
endmethod
//balance from the current node up to the root O(log n)
//balancing is rotations wherever rotations need to be done
private method balance takes nothing returns nothing
local integer f
loop
exitwhen 0==p[this]
set h[this]=getHeight()
set f=getBalanceFactor()
if (2==f) then
if (-1==l[this].getBalanceFactor()) then
call l[this].rotateLeft()
endif
set this=rotateRight()
return
elseif (-2==f) then
if (1==r[this].getBalanceFactor()) then
call r[this].rotateRight()
endif
set this=rotateLeft()
return
endif
set this=p[this]
endloop
endmethod
//goes to the very bottom of a node (for deletion)
private method getBottom takes nothing returns thistype
if (0!=r[this]) then
if (0!=l[this]) then
set this=r[this]
loop
exitwhen 0==l[this]
set this=l[this]
endloop
return this
else
return r[this]
endif
elseif (0!=l[this]) then
return l[this]
endif
return this
endmethod
method search takes thistype val returns thistype
return table[ro[this]][val]
endmethod
method has takes thistype val returns boolean
return table[ro[this]].has(val)
endmethod
method searchClose takes thistype val, boolean low returns thistype
local thistype n
//retrieve tree
set this=ro[this]
//if tree is empty, return 0
if (0==b[this]) then
return 0
endif
//check to see if the node exists in the tree and return it if it does
set n=table[this][val]
if (0!=n) then
return n
endif
//perform a standard tree search for the value to the bottom of the tree
//will always be at most 1 off from the best match
set this=b[this]
loop
if (val.lessThan(v[this])) then
exitwhen 0==l[this]
set this=l[this]
else
exitwhen 0==r[this]
set this=r[this]
endif
endloop
//look at the found value's neighbors on the linked list
if (low) then
//shift down if greater than
if (v[this].greaterThan(val)) then
set this=prev
endif
//return 0 if node wasn't found
if (0==p[this] or v[this].greaterThan(val)) then
return 0
endif
else
//shift up if less than
if (v[this].lessThan(val)) then
set this=next
endif
//return 0 if node wasn't found
if (0==p[this] or v[this].lessThan(val)) then
return 0
endif
endif
return this
endmethod
method add takes thistype val returns thistype
local thistype n
//check if the tree already has the value in it
set this=ro[this]
set n=table[this][val]
//if the tree doesn't have the value in it, add the value
if (0==n) then
set n=this
set this=allocate()
set ro[this]=n //store tree into leaf
set table[n][val]=this //store leaf into value table
set v[this]=val //store value into leaf
//if the tree is empty
if (0==b[n]) then
set b[n]=this //place as first node
set p[this]=n //parent of first node is tree
//add to list
set nn[this]=n
set pn[this]=n
set nn[n]=this
set pn[n]=this
else
//go to the first node in the tree
set n=b[n]
//go to the bottom of the tree with search algorithm
loop
if (val.lessThan(v[n])) then
exitwhen 0==l[n]
set n=l[n]
else
exitwhen 0==r[n]
set n=r[n]
endif
endloop
//add leaf to tree
set p[this]=n
if (val.lessThan(v[n])) then
set l[n]=this
else
set r[n]=this
endif
//update the height of the parent
set h[n]=n.getHeight()
//balance from the parent upwards
call p[n].balance()
//add leaf to list
if (v[n].greaterThan(v[this])) then
set n=pn[n]
endif
set nn[this]=nn[n]
set pn[this]=n
set pn[nn[n]]=this
set nn[n]=this
endif
return this
endif
return n
endmethod
method delete takes nothing returns nothing
local thistype n
local thistype y
//if the leaf to be deleted isn't 0 and the leaf isn't the tree
if (0 != this and 0 != p[this]) then
//remove the leaf from the value table
call table[ro[this]].remove(v[this])
set n=getBottom() //retrieve the bottom leaf
set y=p[n] //store the parent here for balancing later
//move the found leaf into the deleted leaf's position
call n.updateParent(0)
call updateParent(n)
if (this!=n) then
set l[n]=l[this]
set p[l[n]]=n
set r[n]=r[this]
set p[r[n]]=n
set p[n]=p[this]
set h[n]=h[this]
endif
//balance from the found leaf's old parent upwards
call y.balance()
//remove deleted leaf from list
set nn[pn[this]]=nn[this]
set pn[nn[this]]=pn[this]
set nn[this]=nn[0]
set nn[0]=this
endif
endmethod
method clear takes nothing returns thistype
//quick clear
set this=ro[this]
if (nn[this] != this) then
set nn[pn[this]]=nn[0]
set nn[0]=nn[this]
set nn[this]=this
set pn[this]=this
set b[this] = 0
call table[this].flush()
endif
return this
endmethod
method destroy takes nothing returns nothing
//quick destroy
set this=ro[this]
set nn[pn[this]]=nn[0]
set nn[0]=this
call table[this].destroy()
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BinaryHeap /* v4.0.0.0
*************************************************************************************
*
* Binary Heap
*
************************************************************************************
*
* Interface:
* private static method compare takes thistype value1, thistype value2 returns boolean
* - < for minimum heap
* - > for maximum heap
*
* static readonly thistype root
* - The node with the smallest/biggest value
* readonly thistype node
* - Node stored within heap position (array[heapPosition] = node)
* readonly thistype heap
* - Heap position of node (array[heapPosition] = node)
* static readonly integer size
* - Size of binary heap
* readonly integer value
* - Sorted value (the value of the node)
*
* method modify takes integer sortValue returns nothing
* - Modifies the value of the node
*
* static method insert takes integer sortValue returns thistype
* - Inserts a new node into the heap and returns it
* - Assigns that node the passed in value
* method delete takes nothing returns nothing
* - Deletes node from heap
*
* static method clear takes nothing returns nothing
* - Clears the heap
*
************************************************************************************/
module BinaryHeap
readonly static integer size = 0
readonly thistype node //node
private static thistype instanceCount = 0 //node instance count
private static thistype array recycler //node recycler
readonly thistype value
readonly thistype heap
static method operator root takes nothing returns thistype
return thistype(1).node
endmethod
static method allocate takes thistype value returns thistype
local thistype this = recycler[0]
if (0 == this) then
set this = instanceCount + 1
set instanceCount = this
else
set recycler[0] = recycler[this]
endif
set this.value = value
set node.heap = 0
return this
endmethod
method deallocate takes nothing returns nothing
set recycler[this]=recycler[0]
set recycler[0]=this
endmethod
private method link takes thistype heapPosition returns nothing
set heapPosition.node = this
set heap = heapPosition
endmethod
private method bubbleUp takes nothing returns nothing
local thistype value = this.value
local thistype heapPosition = heap
local thistype parent
/*
* Bubble node up
*/
loop
set parent = heapPosition/2
exitwhen (0 == parent or compare(parent.node.value, value))
set heapPosition.node = parent.node
set heapPosition.node.heap = heapPosition
set heapPosition = parent
endloop
/*
* Update pointers
*/
call link(heapPosition)
endmethod
private method bubbleDown takes nothing returns nothing
local thistype value = this.value
local thistype heapPosition = heap
local thistype left
local thistype right
/*
* Bubble node down
*/
loop
set left = heapPosition*2
set right = left + 1
exitwhen (0 == left.node or compare(value, left.node.value)) and (0 == right.node or compare(value, right.node.value))
if (0 == right.node.value or (0 != left.node and compare(left.node.value, right.node.value))) then
/*
* Go left
*/
set heapPosition.node = left.node
set heapPosition.node.heap = heapPosition
set heapPosition = left
else
/*
* Go right
*/
set heapPosition.node = right.node
set heapPosition.node.heap = heapPosition
set heapPosition = right
endif
endloop
/*
* Update pointers
*/
call link(heapPosition)
endmethod
method modify takes integer value returns nothing
set this.value = value
/*
* Bubble node into correct position
*/
call bubbleUp()
call bubbleDown()
endmethod
static method insert takes thistype value returns thistype
local thistype heapPosition
local thistype this
/*
* Allocate new node
*/
set this = allocate(value)
/*
* Increase heap size
*/
set heapPosition = size + 1
set size = heapPosition
/*
* Store node in last heap position
*/
call link(heapPosition)
/*
* Bubble node into correct position
*/
call bubbleUp()
return this
endmethod
method delete takes nothing returns nothing
local thistype lastNode
debug if (0 == size) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Attempted To Delete Node From Empty Heap")
debug set this = 1/0
debug endif
/*
* Deallocate node
*/
call deallocate()
/*
* Remove last node from last position
*/
set lastNode = thistype(size).node
set thistype(size).node = 0
set size = size - 1
if (lastNode != node) then
/*
* Put last node in deallocated node's position
*/
call lastNode.link(heap)
/*
* Bubble into correct spot
*/
call lastNode.bubbleUp()
call lastNode.bubbleDown()
endif
endmethod
static method clear takes nothing returns nothing
set size = 0
set recycler[0] = 0
set instanceCount = 0
set thistype(1).node = 0
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DamageEvent /* v3.1.1.3
*************************************************************************************
*
* Simple light damage detection. Will not run 0 damage events as only invalid attacks and spells can cause these.
* Can instance spells and attacks with fireSpell and fireAttack methods.
*
*************************************************************************************
*
* */uses/*
*
* */ UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
* */ PriorityEvent /* hiveworkshop.com/forums/submissions-414/snippet-priority-event-213573/
* */ BinaryHeap /* hiveworkshop.com/forums/jass-resources-412/snippet-binary-heap-199353/
*
************************************************************************************
*
* SETTINGS
*/
globals
/*************************************************************************************
*
* How many units can refresh at a given moment (when a trigger is rebuilt).
* larger size means less triggers but harder refreshes.
*
*************************************************************************************/
private constant integer TRIGGER_SIZE = 80
endglobals
/*
*************************************************************************************
*
* struct DamageEvent extends array
*
* Fields
* ------------------
* static constant PriorityEvent ANY
*
* readonly static UnitIndex targetId
* readonly static UnitIndex sourceId
* readonly static unit target
* readonly static unit source
* readonly static real amount
* readonly static integer depth
* - if depth == 1, then current damage is the original attack, not a modifier
*
* static boolean enabled
*
* readonly static integer damageType
* - useful for custom attack types (spell, hero, chaos, etc)
* readonly static integer damageId
* - useful for things like ability ids
* readonly static integer instance
* - useful for passing in a struct
*
* Methods
* ------------------
* static method unitDamageTarget takes unit attacker, widget target, real amount, integer damageType, integer damageId, integer instance returns boolean
*
*************************************************************************************
*
* module ExtendDamageEvent extends DamageEvent, AdvDamageEvent (if exists)
*
* module DamageEvent extends DamageEvent, AdvDamageEvent (if exists)
*
* Interface
* ------------------
* private static constant method PRIORITY takes nothing returns integer (optional)
* - without this declared, the priority will be set to 0 (no priority)
* private static method onDamage takes nothing returns nothing (optional)
* private static method filter takes nothing returns boolean (optional)
*
*************************************************************************************/
private struct DamageEventProperties extends array
static method operator target takes nothing returns unit
return GetUnitById(DamageEvent.targetId)
endmethod
static method operator source takes nothing returns unit
return GetUnitById(DamageEvent.sourceId)
endmethod
endstruct
//! runtextmacro optional ADV_DAMAGE_EVENT_EXT_CODE()
globals
private boolexpr damageCondition
endglobals
private struct DamageTrigger extends array
private static integer instanceCount = 0
private thistype first
private thistype next
private thistype prev
readonly thistype parent
private integer inactiveUnits
readonly integer activeUnits
private trigger damageTrigger
private method registerUnit takes UnitIndex whichUnit returns boolean
if (activeUnits < TRIGGER_SIZE) then
call TriggerRegisterUnitEvent(damageTrigger, GetUnitById(whichUnit), EVENT_UNIT_DAMAGED)
set activeUnits = activeUnits + 1
return true
endif
return false
endmethod
private method unregisterUnit takes UnitIndex whichUnit returns nothing
set inactiveUnits = inactiveUnits + 1
set activeUnits = activeUnits - 1
endmethod
private method createTrigger takes nothing returns nothing
set damageTrigger = CreateTrigger()
call TriggerAddCondition(damageTrigger, damageCondition)
endmethod
private method remakeTrigger takes nothing returns nothing
call DestroyTrigger(damageTrigger)
call createTrigger()
endmethod
private method rebuildTrigger takes nothing returns nothing
local thistype current = first
call remakeTrigger()
/*
* Iterate over all units registered to the trigger and reregister them
*/
set current.prev.next = 0
loop
exitwhen 0 == current
call TriggerRegisterUnitEvent(damageTrigger, GetUnitById(current), EVENT_UNIT_DAMAGED)
set current = current.next
endloop
set first.prev.next = current
endmethod
private method remake takes nothing returns nothing
if (inactiveUnits == TRIGGER_SIZE) then
set inactiveUnits = 0
call rebuildTrigger()
endif
endmethod
private method addToList takes thistype whichUnit returns nothing
set whichUnit.parent = this
if (0 == first) then
set first = whichUnit
set whichUnit.next = whichUnit
set whichUnit.prev = whichUnit
else
set this = first
set whichUnit.prev = prev
set whichUnit.next = this
set prev.next = whichUnit
set prev = whichUnit
endif
endmethod
method add takes thistype whichUnit returns boolean
if (0 == this) then
return false
endif
if (registerUnit(whichUnit)) then
call addToList(whichUnit)
return true
endif
return false
endmethod
private method removeFromList takes thistype whichUnit returns nothing
set whichUnit.parent = 0
set whichUnit.prev.next = whichUnit.next
set whichUnit.next.prev = whichUnit.prev
if (first == whichUnit) then
set first = whichUnit.next
if (first == whichUnit) then
set first = 0
endif
endif
endmethod
static method remove takes thistype whichUnit returns nothing
local thistype this = whichUnit.parent
call removeFromList(whichUnit)
call unregisterUnit(whichUnit)
call remake()
endmethod
private static method allocate takes nothing returns thistype
set instanceCount = instanceCount + 1
return instanceCount
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
call createTrigger()
return this
endmethod
endstruct
private struct DamageTriggerHeapInner extends array
private static method compare takes DamageTrigger trig1, DamageTrigger trig2 returns boolean
return trig1.activeUnits <= trig2.activeUnits
endmethod
implement BinaryHeap
endstruct
private struct DamageTriggerHeap extends array
private static DamageTriggerHeapInner array parent
static method add takes UnitIndex whichUnit returns nothing
local DamageTrigger damageTrigger = DamageTriggerHeapInner.root.value
if (not damageTrigger.add(whichUnit)) then
set damageTrigger = DamageTrigger.create()
call damageTrigger.add(whichUnit)
set parent[damageTrigger] = DamageTriggerHeapInner.insert(damageTrigger)
else
call parent[damageTrigger].modify(damageTrigger)
endif
endmethod
static method remove takes UnitIndex whichUnit returns nothing
local DamageTrigger damageTrigger = DamageTrigger(whichUnit).parent
call DamageTrigger.remove(whichUnit)
call parent[damageTrigger].modify(damageTrigger)
endmethod
endstruct
private module DamageEventMod
readonly static PriorityEvent ANY
readonly static UnitIndex targetId
readonly static UnitIndex sourceId
readonly static real amount
static boolean enabled
private static integer array damageType_p
private static integer array damageId_p
private static integer array instance_p
private static integer runInstanceCount_p
private static integer runAttackCount_p
private static method allocateAttack takes integer damageType, integer damageId, integer instance returns nothing
set runInstanceCount_p = runInstanceCount_p + 1
set damageType_p[runInstanceCount_p] = damageType
set damageId_p[runInstanceCount_p] = damageId
set instance_p[runInstanceCount_p] = instance
endmethod
static method unitDamageTarget takes unit attacker, widget target, real amount, integer damageType, integer damageId, integer instance returns boolean
if (enabled) then
call allocateAttack(damageType, damageId, instance)
return UnitDamageTarget(attacker, target, amount, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
endif
return false
endmethod
static method operator damageType takes nothing returns integer
return damageType_p[runInstanceCount_p]
endmethod
static method operator damageId takes nothing returns integer
return damageId_p[runInstanceCount_p]
endmethod
static method operator instance takes nothing returns integer
return instance_p[runInstanceCount_p]
endmethod
static method operator depth takes nothing returns integer
return runAttackCount_p
endmethod
private static delegate DamageEventProperties damageEventProperties = 0
private static method damage takes nothing returns boolean
local integer previousTargetId
local integer previousSourceId
local real previousAmount
//! runtextmacro optional ADV_DAMAGE_EVENT_LOC_BEFORE()
if (enabled and 0 != GetEventDamage()) then
/*
* Setup spell
*/
set runAttackCount_p = runAttackCount_p + 1
if (runAttackCount_p != runInstanceCount_p) then
set runInstanceCount_p = runInstanceCount_p + 1
endif
/*
* Store previous amounts
*/
set previousTargetId = targetId
set previousSourceId = sourceId
set previousAmount = amount
/*
* Update amounts to new amounts
*/
set targetId = GetUnitUserData(GetTriggerUnit())
set sourceId = GetUnitUserData(GetEventDamageSource())
set amount = GetEventDamage()
/*
* Fire event
*/
//! runtextmacro optional ADV_DAMAGE_EVENT_EXT()
call ANY.fire()
/*
* Restore previous amounts
*/
set targetId = previousTargetId
set sourceId = previousSourceId
set amount = previousAmount
//! runtextmacro optional ADV_DAMAGE_EVENT_LOC_AFTER()
/*
* Remove spell
*/
set damageType_p[runInstanceCount_p] = 0
set damageId_p[runInstanceCount_p] = 0
set instance_p[runInstanceCount_p] = 0
set runInstanceCount_p = runInstanceCount_p - 1
set runAttackCount_p = runAttackCount_p - 1
endif
return false
endmethod
private static method index takes nothing returns boolean
call UnitIndex(GetIndexedUnitId()).lock()
call DamageTriggerHeap.add(GetIndexedUnitId())
return false
endmethod
private static method deindex takes nothing returns boolean
call DamageTriggerHeap.remove(GetIndexedUnitId())
call UnitIndex(GetIndexedUnitId()).unlock()
return false
endmethod
private static method onInit takes nothing returns nothing
set enabled = true
set runInstanceCount_p = 0
set runAttackCount_p = 0
set damageCondition = Condition(function thistype.damage)
set ANY = PriorityEvent.create()
call RegisterUnitIndexEvent(Condition(function thistype.index), UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.deindex), UnitIndexer.DEINDEX)
set targetId = 0
set sourceId = 0
set amount = 0
endmethod
endmodule
struct DamageEvent extends array
implement DamageEventMod
endstruct
module ExtendDamageEvent
static if AdvDamageEvent_p.onDamage_p_core.exists then
private static delegate AdvDamageEvent advDamageEvent = 0
else
private static delegate DamageEvent damageEvent = 0
endif
endmodule
module DamageEvent
implement ExtendDamageEvent
static if thistype.onDamage.exists then
private static method onDamage_p takes nothing returns boolean
static if thistype.filter.exists then
if (filter()) then
call onDamage()
endif
else
call onDamage()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
static if thistype.PRIORITY.exists then
call DamageEvent.ANY.register(Condition(function thistype.onDamage_p), PRIORITY())
else
call DamageEvent.ANY.register(Condition(function thistype.onDamage_p), 0)
endif
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
Lightning Utilities v1.01
by Adiktuz
A set of methods that can help you make more out of the
lightning system.
I chose to separate it from the main system because these are
extra methods only to extend the capabilities of the LS.
This way, the user has the option of not using LU library
as the LS will work fine without it.
How to use:
LightningUtils.method(parameters)
Methods:
enumUnits(group g, Lightning light, real radius)
Parameters:
group g -> group that you want to fill with the units between the Lightning's ends
Lightning light -> the Lightning object
real radius -> radius around the Lightning in which unit's will be picked
Note: the following Gradient methods are only for timed lightnings
registerGradientKey(integer eventkey)
->eventkey of the Lightning's that you want to have a color/alpha gradient
Notes:
->it is important that you register first the eventkey using this method
before using the next method which sets the gradient of each Lightning
instance.
->Do this registration only once per eventkey
addLightningGradient(Lightning light, real red1, real blue1, real green1, real alpha1,real red2, real blue2, real green2, real alpha2)
Parameters:
Lightning light -> the Lightning instance that you want to have a gradient
real red1,blue1,green1,alpha1 -> the initial values of the colors/alpha of the Lightning
real red2,blue2,green2,alpha2 -> the final values of the colors/alpha of the Lightning
->these reals are from 0.0 up to 1.0
Notes:
->Make sure that the eventkey used by this Lightning is already registered using the
method above (registerGradientKey)
->This method automatically fetches the duration of the Lightning
->Do not modify the Lightning's duration afterwards else it might look bad
See the Example trigger for an example of how to use these methods.
*/
library LightningUtilities requires LightningSystem
private module init
static method onInit takes nothing returns nothing
set thistype.redTable = Table.create()
set thistype.blueTable = Table.create()
set thistype.greenTable = Table.create()
set thistype.alphaTable = Table.create()
set thistype.redCTable = Table.create()
set thistype.blueCTable = Table.create()
set thistype.greenCTable = Table.create()
set thistype.alphaCTable = Table.create()
endmethod
endmodule
struct LightningUtils extends array
private static group tmpGroup = CreateGroup()
private static unit tmpUnit = null
private static Table redTable
private static Table blueTable
private static Table greenTable
private static Table alphaTable
private static Table redCTable
private static Table blueCTable
private static Table greenCTable
private static Table alphaCTable
static method enumUnits takes group g, Lightning light, real radius returns nothing
local real tdy = (light.targetY-light.sourceY)
local real tdx = (light.targetX-light.sourceX)
local real angle = Atan2(tdy,tdx)
local real distance = (tdy*tdy)+(tdx*tdx)
local integer iend = R2I(distance/(radius*radius))
local real dx = tdx/iend
local real dy = tdy/iend
//we only get units in between the lightning
local integer i = 1
set iend = iend - 1
call GroupClear(g)
loop
call GroupEnumUnitsInRange(tmpGroup,light.sourceX+i*dx,light.sourceY+i*dy,radius,null)
loop
set tmpUnit = FirstOfGroup(tmpGroup)
exitwhen tmpUnit == null
if not IsUnitInGroup(tmpUnit,g) then
call GroupAddUnit(g,tmpUnit)
endif
call GroupRemoveUnit(tmpGroup,tmpUnit)
endloop
set i = i + 1
exitwhen i > iend
endloop
endmethod
//I used Table to save the RGBA values because if I use the GeLightningColor natives, it returns
//rounded down values, resulting to bad behavior at durations > 4.0
static method updateGradient takes nothing returns nothing
set redCTable.real[Lightning.instance] = redCTable.real[Lightning.instance] + redTable.real[Lightning.instance]
set blueCTable.real[Lightning.instance] = blueCTable.real[Lightning.instance] + blueTable.real[Lightning.instance]
set greenCTable.real[Lightning.instance] = greenCTable.real[Lightning.instance] + greenTable.real[Lightning.instance]
set alphaCTable.real[Lightning.instance] = alphaCTable.real[Lightning.instance] + alphaTable.real[Lightning.instance]
call SetLightningColor(Lightning.instance.light, redCTable.real[Lightning.instance],greenCTable.real[Lightning.instance],blueCTable.real[Lightning.instance],alphaCTable.real[Lightning.instance])
endmethod
static method addLightningGradient takes Lightning light, real red1, real blue1, real green1, real alpha1,real red2, real blue2, real green2, real alpha2 returns nothing
set redCTable.real[light] = red1
set blueCTable.real[light] = blue1
set greenCTable.real[light] = green1
set alphaCTable.real[light] = alpha1
set redTable.real[light] = (((red2-red1)/light.duration)*T32_PERIOD)
set blueTable.real[light] = (((blue2-blue1)/light.duration)*T32_PERIOD)
set greenTable.real[light] = (((green2-green1)/light.duration)*T32_PERIOD)
set alphaTable.real[light] = (((alpha2-alpha1)/light.duration)*T32_PERIOD)
endmethod
static method registerGradientKey takes integer eventkey returns nothing
call Lightning.registerUpdateEvent(eventkey,function thistype.updateGradient)
endmethod
implement init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library PriorityEvent /* v2.0.0.1
*************************************************************************************
*
* Creates events that fire given a priority. A higher priority means that those
* events will fire first. A priority of 0 means that those events will fire last.
*
* Priority events can only be created at map init
* Code can only be registered to priority events at map init
*
*************************************************************************************
*
* */uses/*
*
* */ AVL /* hiveworkshop.com/forums/jass-resources-412/snippet-avl-tree-203168/
*
************************************************************************************
*
* struct PriorityEvent extends array
*
* static method create takes nothing returns thistype
* method register takes boolexpr func, integer priority returns nothing
* method fire takes nothing returns nothing
*
************************************************************************************/
private struct PriorityEventTree extends array
method lessThan takes thistype value returns boolean
return integer(this) < integer(value)
endmethod
method greaterThan takes thistype value returns boolean
return integer(this) > integer(value)
endmethod
implement AVL
endstruct
private module PriorityEventMod
private static integer instanceCount = 0
/*
* A queue of code registered with the same priority
*/
private thistype next_p
private thistype last_p
private thistype first_p
/*
* The priorities are stored in the tree list
*/
/*
* Iterate from 0 to count to go over all created events
*/
private static PriorityEventTree count = 0
private static PriorityEventTree array tree
/*
* This is a temporary trigger to store all code of the same priority
* Once the game has started, all code will be merged on to one trigger
*/
private trigger event
/*
* Need to store the code in order to merge all it all on to one trigger
*/
private boolexpr code
/*
* All code is merged on this
*/
private trigger allEvent
/*
* Has the code all been merged?
*/
private static boolean merged = false
static method create takes nothing returns thistype
local thistype this
debug if (merged) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Priority Event Error: Can Only Create Events On Game Init")
debug set this = 1/0
debug endif
/*
* Allocate new event
*/
set this = PriorityEventTree.create()
/*
* Add to array for merging later
*/
set tree[count] = this
set count = count + 1
/*
* Create the merging trigger
*/
set thistype(count).allEvent = CreateTrigger()
return count
endmethod
method register takes boolexpr func, integer priority returns nothing
local thistype node
debug if (merged) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Priority Event Error: Can Only Register Code On Game Init")
debug set node = 1/0
debug endif
/*
* Allocate a new node to store the function
*/
set node = instanceCount + 1
set instanceCount = node
set node.code = func
/*
* Retrieve the priority. This will act as the pointer to
* the queue that the node will be added to.
*/
set this = PriorityEventTree(this).add(priority)
if (null == event) then
/*
* If the queue hasn't been created yet, create it
*/
set event = CreateTrigger()
set first_p = node
set last_p = node
else
/*
* Add node to queue
*/
set last_p.next_p = node
set last_p = node
endif
call TriggerAddCondition(event, func)
endmethod
method fire takes nothing returns nothing
if (merged) then
/*
* If the code has all been merged (game started), evaluate the trigger that contains all code
*/
call TriggerEvaluate(allEvent)
else
/*
* If the code hasn't been merged yet, evaluate all of the triggers along the priority queue
*/
loop
set this = PriorityEventTree(this).prev
exitwhen PriorityEventTree(this).head
call TriggerEvaluate(event)
endloop
endif
endmethod
/*
* This is called when the game starts. It merges all of the registered code
* for each event on to single triggers to improve performance
*/
private static method merge takes nothing returns nothing
local thistype this
local integer current = count
local PriorityEventTree priority
local thistype node
set merged = true
/*
* Iterate over all events
*/
loop
exitwhen 0 == current
set current = current - 1
set this = tree[current]
/*
* Iterate over all priorities
*/
set priority = this
loop
set priority = priority.prev
exitwhen priority.head
/*
* Clean up temporary priority event trigger
*/
call TriggerClearConditions(thistype(priority).event)
call DestroyTrigger(thistype(priority).event)
set thistype(priority).event = null
/*
* Iterate over all registered code on the priority trigger
*/
set node = thistype(priority).first_p
loop
exitwhen 0 == node
/*
* Add to main trigger
*/
call TriggerAddCondition(allEvent, node.code)
set node = node.next_p
endloop
endloop
endloop
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function thistype.merge)
endmethod
endmodule
struct PriorityEvent extends array
implement PriorityEventMod
endstruct
endlibrary
//TESH.scrollpos=84
//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=302
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.1.0
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WorldBounds
//struct WorldBounds extends array
//static readonly rect world
// same as GetWorldBounds()
//static readonly region worldRegion
// contains world for triggers
//static readonly real maxX
//static readonly real maxY
//static readonly real minX
//static readonly real minY
//static readonly real centerX
//static readonly real centerY
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=GetRectMaxX(world)
set maxY=GetRectMaxY(world)
set minX=GetRectMinX(world)
set minY=GetRectMinY(world)
set centerX=(maxX+minX)/2
set centerY=(minY+maxY)/2
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static real maxX
readonly static real maxY
readonly static real minX
readonly static real minY
readonly static real centerX
readonly static real centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=159
//TESH.alwaysfold=0
library KnockbackTools uses TimerUtils, Event, DestructableLib, UnitIndexer
/*****************************************************************************
*
* KnockbackTools
* _______________
* v1.0.0.1
* by Thelordmarshall
*
* Well another fucking Knockback system u.u...
*
* Pros:
* _____
* You can:
* - Use slide speed or not.
* - Use fly on launch targets.
* - Create functions for loop/stop events.
* - And more....
*
* API:
* ____
* struct Knockback extends array:
* - method launch takes real angle, real distance returns nothing
* - Launch the target.
* method operator:
* - useSlideSpeed (boolean)
* - speed (real)
* - timerSpeed (real)
* - arc (real)
* - fx (string)
* - pathing (boolean)
* - pause (boolean)
* - fly (boolean)
* - hitTrees (boolean)
*
* Functions:
* __________
* - function Parabola takes real arc, real d, real x returns real
* - function ParabolicMovement takes real h, real d, real x returns real
*
* Credits:
* ________
* - Vexorian: TimerUtils.
* - Nestharus: UnitIndexer,Event.
* - PitzerMike: DestructableLib.
* - moyackx: Parabolic formulas.
*
* Copyright © 2014.
*****************************************************************************/
//CONFIGURATION
globals
private constant real ARC_LIMIT = 1.
private constant real RATIO = 200.
endglobals
//ENDCONFIGURATION
function Parabola takes real arc, real d, real x returns real
return (4*arc)*(d-x)*(x/d)
endfunction
function ParabolicMovement takes real h, real d, real x returns real
local real a = -4*h/(d*d)
local real b = 4*h/d
return a*x*x + b*x
endfunction
struct Knockback extends array
static constant real PERIOD=.03125
static unit lastStopedUnit
static unit loopEventUnit
readonly static Event LOOP
readonly static Event STOP
private static rect treesRect
private unit u
private real d1
private real d2
private real sin
private real cos
private real x
private real y
//private real flyHeight
private real kb_distance
private real kb_speed
private real kb_timerSpeed
private real kb_arc
private string kb_fx
private boolean kb_fly
private boolean kb_pathing
private boolean kb_pause
private boolean kb_useSlideSpeed
private boolean kb_hitTrees
implement UnitIndexStruct
private method destroy takes nothing returns nothing
set lastStopedUnit=.u
call FireEvent(Knockback.STOP)
set lastStopedUnit=null
if .kb_pause then
call PauseUnit(.u,false)
endif
if not .kb_pathing then
call SetUnitPathing(.u,true)
endif
if .kb_fly then
call SetUnitFlyHeight(.u,GetUnitDefaultFlyHeight(.u),0.)//.flyHeight,0.)
endif
set .u=null
endmethod
private static method killTrees takes nothing returns nothing
local destructable d=GetEnumDestructable()
if not IsDestructableIsDead(d) and IsDestructableIsTree(d) then
call KillDestructable(d)
endif
set d=null
endmethod
private static method onLoop takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
local real x=GetUnitX(.u)
local real y=GetUnitY(.u)
local real d=SquareRoot(Pow(.x-x,2)+Pow(.y-y,2))
local real f
set loopEventUnit=.u
call FireEvent(Knockback.LOOP)
set loopEventUnit=null
if .kb_pause and not IsUnitPaused(.u) then
call PauseUnit(.u,true)
endif
if (.kb_fx!=null) or (.kb_fx!="") then
call DestroyEffect(AddSpecialEffect(.kb_fx,x,y))
endif
if .kb_useSlideSpeed then
set x=x+.d1*.cos
set y=y+.d1*.sin
set .d1=.d1-.d2
else
set x=x+.cos
set y=y+.sin
endif
call SetUnitPosition(.u,x,y)
if .kb_fly then
if .kb_arc>-ARC_LIMIT and .kb_arc<ARC_LIMIT then
set f=Parabola(.kb_arc,.kb_distance,d)
else
set f=ParabolicMovement(.kb_arc,.kb_distance,d)
endif
call SetUnitFlyHeight(.u,f,0.)
endif
if .kb_hitTrees then
call MoveRectTo(treesRect,x,y)
call EnumDestructablesInRect(treesRect,null,function thistype.killTrees)
endif
if .kb_useSlideSpeed and (.d1<=0.) then
call .destroy()
call ReleaseTimer(t)
elseif not .kb_useSlideSpeed and (d>=.kb_distance) then
call .destroy()
call ReleaseTimer(t)
endif
set t=null
endmethod
method launch takes real angle, real distance returns nothing
local integer q=R2I(.kb_timerSpeed/PERIOD)
set .u=.unit
set .kb_distance=distance
set .x=GetUnitX(.u)
set .y=GetUnitY(.u)
if .kb_useSlideSpeed then
set .d1=2*distance/(q+1)
set .d2=.d1/q
set .sin=Sin(angle)
set .cos=Cos(angle)
else
set .sin=kb_speed*Sin(angle)
set .cos=kb_speed*Cos(angle)
endif
if .kb_fly then
//set .flyHeight=GetUnitFlyHeight(.u)
call UnitAddAbility(.u,'Arav')
call UnitRemoveAbility(.u,'Arav')
endif
call SetUnitPathing(.u,.kb_pathing)
call TimerStart(NewTimerEx(this),PERIOD,true,function thistype.onLoop)
endmethod
//! textmacro kb_operator takes OPERATOR_NAME,VAR_TYPE,VAR_NAME
method operator $OPERATOR_NAME$= takes $VAR_TYPE$ $VAR_NAME$ returns nothing
set .kb_$OPERATOR_NAME$=$VAR_NAME$
endmethod
//! endtextmacro
//! runtextmacro kb_operator("useSlideSpeed","boolean","b")
//! runtextmacro kb_operator("speed","real","s")
//! runtextmacro kb_operator("timerSpeed","real","s")
//! runtextmacro kb_operator("arc","real","a")
//! runtextmacro kb_operator("fx","string","f")
//! runtextmacro kb_operator("pathing","boolean","b")
//! runtextmacro kb_operator("pause","boolean","b")
//! runtextmacro kb_operator("fly","boolean","b")
//! runtextmacro kb_operator("hitTrees","boolean","b")
static method registerEvent takes boolexpr c, Event ev returns nothing
call ev.register(c)
endmethod
private static method onInit takes nothing returns nothing
set treesRect=Rect(-RATIO,-RATIO,RATIO,RATIO)
set LOOP=CreateEvent()
set STOP=CreateEvent()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xepreload initializer init requires xebasic, optional TimerUtils
//******************************************************************************
// xepreload 0.8
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//******************************************************************************
//==============================================================================
globals
private unit dum=null
endglobals
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
static if DEBUG_MODE then
if(dum==null) then
call BJDebugMsg("XE_PreloadAbility: do not load abilities after map init or during structs' onInit")
elseif GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private function init takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITID, 0,0,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITID ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITID)+") not added correctly to the map.")
endif
static if (LIBRARY_TimerUtils) then
set t=NewTimer()
else
set t=CreateTimer()
endif
call TimerStart(t,0.0,false,function kill)
set t=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AbilitiesPreload initializer Init uses xepreload
private function Init takes nothing returns nothing
call XE_PreloadAbility('A00A')
call XE_PreloadAbility('A00B')
call XE_PreloadAbility('A00C')
call XE_PreloadAbility('A00E')
call XE_PreloadAbility('A003')
call XE_PreloadAbility('A004')
call XE_PreloadAbility('A00N')
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ChosenOfTheStorm
//Todas las estadisticas de la habilidad han sido sacadas de:
//http://gameinfo.lan.leagueoflegends.com/es/game-info/champions/volibear/
//CONFIG
//==========================================================
globals
private constant integer SPELL_ID = 'A007'
private constant integer DUMMY_SPELL_ID = 'A009'
private constant integer DUMMY_ID = 'e000'
private constant string DUMMY_ORDER_STRING = "innerfire"
private constant string EFECT_FX = "Abilities\\Heal_Green.mdl"
private constant real BUFF_DURATION = 6.
private constant real SPELL_CD = 120.
private constant real LIFE_SPEED_RATE = 1.8
private constant real LIFE_PERCENT = 30.
endglobals
//==========================================================
//ENDCONFIG
private function GetLifePercent takes unit u returns real
return GetWidgetLife(u)/GetUnitState(u,UNIT_STATE_MAX_LIFE)*100.
endfunction
private function GetLifeSpecificPercent takes unit u returns real
return GetUnitState(u,UNIT_STATE_MAX_LIFE)*LIFE_PERCENT/100.
endfunction
private struct ChosenOfTheStorm
static xecast caster
unit u
real currentLife
real totalLife
real duration
boolean onCD
static method onLoop takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
local real l=GetWidgetLife(.u)+LIFE_SPEED_RATE
set .duration=.duration-.03125
call SetWidgetLife(.u,l)
if l>=.totalLife or .duration<=0. then
set .u=null
call ReleaseTimer(t)
endif
set t=null
endmethod
method setAlive takes nothing returns nothing
set .totalLife=GetLifeSpecificPercent(.u)+.currentLife
call SetWidgetLife(.u,.currentLife)
call TimerStart(NewTimerEx(this),.03125,true,function thistype.onLoop)
endmethod
method reincarnate takes nothing returns nothing
local unit u=GetEventUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local player p=GetOwningPlayer(u)
call SelectUnit(u,GetLocalPlayer()==p)
call TimedEffect.createOnUnit(EFECT_FX,u,"overhead",BUFF_DURATION)
set caster.owningplayer=p
call caster.castOnTarget(u)
call .setAlive()
set u=null
endmethod
implement UnitEventStruct
static method cooldown takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
set .onCD=false
call ReleaseTimer(t)
set t=null
endmethod
static method filter takes nothing returns boolean
local thistype this=DamageEvent.targetId
local player p1=GetOwningPlayer(DamageEvent.target)
local player p2=GetOwningPlayer(DamageEvent.source)
return IsPlayerEnemy(p1,p2) and GetUnitAbilityLevel(DamageEvent.target,SPELL_ID)>0 and GetLifePercent(DamageEvent.target)<=LIFE_PERCENT and not .onCD
endmethod
static method onDamage takes nothing returns nothing
local thistype this=DamageEvent.targetId
local unit d=CreateUnit(GetOwningPlayer(DamageEvent.source),DUMMY_ID,0.,0.,0.)
set .u=DamageEvent.target
set .onCD=true
set .duration=BUFF_DURATION
set .currentLife=GetWidgetLife(.u)
call UnitDamageTarget(d,.u,99999.,false,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_NORMAL,null)
call TimerStart(NewTimerEx(this),SPELL_CD,false,function thistype.cooldown)
call RemoveUnit(d)
set d=null
endmethod
implement DamageEvent
static method onInit takes nothing returns nothing
set caster=xecast.createBasic(DUMMY_SPELL_ID,OrderId(DUMMY_ORDER_STRING),Player(15))
set caster.recycledelay=.1
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope RollingThunder
globals
private real array dmgLvl
private real array speedLvl
endglobals
//Todas las estadisticas de la habilidad han sido sacadas de:
//http://gameinfo.lan.leagueoflegends.com/es/game-info/champions/volibear/
//CONFIG
//==========================================================
globals
private constant integer SPELL_ID = 'A008'
private constant integer BUFF_ID = 'B002'
private constant string FX_1 = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
private constant string FX_2 = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant real BUFF_DURATION = 4.
private constant real AoE = 1700.
private constant real TREES_RATIO = 200.
private constant real SPEED_RATE = 15.
private constant damagetype DMG_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function DamangeConfig takes nothing returns nothing
set dmgLvl[1] = 30.
set dmgLvl[2] = 60.
set dmgLvl[3] = 90.
set dmgLvl[4] = 120.
set dmgLvl[5] = 150.
endfunction
private function SpeedConfig takes nothing returns nothing
set speedLvl[1] = 30.
set speedLvl[2] = 35.
set speedLvl[3] = 40.
set speedLvl[4] = 45.
set speedLvl[5] = 50.
endfunction
private function FilterConfig takes unit u returns boolean
return IsUnitType(u,UNIT_TYPE_HERO) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//==========================================================
//ENDCONFIG
private struct RollingThunder
static rect treesRect
static unit f=null
static thistype ta_this
unit c
player p
real duration
real speed
real dmg
real extTotal
boolean hit
private static method killTrees takes nothing returns nothing
local destructable d=GetEnumDestructable()
if not IsDestructableIsDead(d) and IsDestructableIsTree(d) then
call KillDestructable(d)
endif
set d=null
endmethod
static method onKnockbackStop takes nothing returns boolean
call DestroyEffect(AddSpecialEffectTarget(FX_1,Knockback.lastStopedUnit,"origin"))
call MoveRectTo(treesRect,GetUnitX(Knockback.lastStopedUnit),GetUnitY(Knockback.lastStopedUnit))
call EnumDestructablesInRect(treesRect,null,function thistype.killTrees)
return false
endmethod
static method filter takes nothing returns boolean
local thistype this=DamageEvent.sourceId
local player p1=GetOwningPlayer(DamageEvent.target)
local player p2=GetOwningPlayer(DamageEvent.source)
return IsPlayerEnemy(p1,p2) and not IsUnitType(DamageEvent.target,UNIT_TYPE_MAGIC_IMMUNE) and GetUnitAbilityLevel(DamageEvent.source,BUFF_ID)>0 and .hit
endmethod
static method onDamage takes nothing returns nothing
local thistype this=DamageEvent.sourceId
local unit u=DamageEvent.target
local real x1=GetUnitX(.c)
local real y1=GetUnitY(.c)
local real x2=GetUnitX(u)
local real y2=GetUnitY(u)
local real d=SquareRoot(Pow(x2-x1,2)+Pow(y2-y1,2))
local real a=Atan2(y1-y2,x1-x2)
local unit dum=CreateUnit(.p,XE_DUMMY_UNITID,x1,y1,0.)
call UnitDamageTarget(dum,u,.dmg,false,false,ATTACK_TYPE_HERO,DMG_TYPE,null)
call DestroyEffect(AddSpecialEffectTarget(FX_2,u,"origin"))
if not IsUnitType(u,UNIT_TYPE_STRUCTURE) then
set Knockback[u].speed=20.
set Knockback[u].arc=.9
set Knockback[u].fx=""
set Knockback[u].fly=true
set Knockback[u].pause=true
set Knockback[u].pathing=false
set Knockback[u].hitTrees=false
set Knockback[u].useSlideSpeed=false
call Knockback[u].launch(a,d*2.+20.)
endif
set .hit=false
call RemoveUnit(dum)
set dum=null
set u=null
endmethod
implement DamageEvent
static method filterGroup takes nothing returns boolean
local thistype this=ta_this
set f=GetFilterUnit()
return IsPlayerEnemy(GetOwningPlayer(f),.p) and GetWidgetLife(f)>.405 and FilterConfig(f) and IsUnitVisible(f,.p)
endmethod
static method onLoop takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
local real x=GetUnitX(.c)
local real y=GetUnitY(.c)
set ta_this=this
call GroupEnumUnitsInArea(ENUM_GROUP,x,y,AoE,Filter(function thistype.filterGroup))
if not IsUnitGroupEmptyBJ(ENUM_GROUP) then
if .speed<.extTotal then
set .speed=.speed+.6
call SetUnitMoveSpeed(.c,.speed)
endif
endif
set .duration=.duration-.03125
if .duration<=0. then
call SetUnitMoveSpeed(.c,GetUnitDefaultMoveSpeed(.c))
set .c=null
set .p=null
call ReleaseTimer(t)
endif
set t=null
endmethod
static method onCast takes nothing returns nothing
local unit c=GetTriggerUnit()
local thistype this=GetUnitId(c)
local integer lvl=GetUnitAbilityLevel(c,SPELL_ID)
local real ex=(GetUnitDefaultMoveSpeed(c)*SPEED_RATE)/100.
if .c==null then
set .c=c
set .p=GetTriggerPlayer()
endif
set .speed =GetUnitDefaultMoveSpeed(.c)+ex
set .extTotal=(.speed*speedLvl[lvl])/100.+GetUnitDefaultMoveSpeed(.c)
set .dmg =dmgLvl[lvl]
set .duration=BUFF_DURATION
set .hit =true
call SetUnitMoveSpeed(.c,GetUnitDefaultMoveSpeed(.c))
call TimerStart(NewTimerEx(this),.03125,true,function thistype.onLoop)
set c=null
endmethod
static method onInit takes nothing returns nothing
call DamangeConfig()
call SpeedConfig()
set treesRect=Rect(-TREES_RATIO,-TREES_RATIO,TREES_RATIO,TREES_RATIO)
call Knockback.registerEvent(Condition(function thistype.onKnockbackStop),Knockback.STOP)
call RegisterSpellEffectEvent(SPELL_ID,function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Frenesi
globals
private real array dmgLvl
private integer array buffLvl
endglobals
//Todas las estadisticas de la habilidad han sido sacadas de:
//http://gameinfo.lan.leagueoflegends.com/es/game-info/champions/volibear/
//CONFIG
//==========================================================
globals
private constant integer VOLIBEAR_DUMMY = 'h003'
private constant integer PASSIVE_SPELL_ID = 'A006'
private constant integer SPELL_ID = 'A005' //Mordisco
private constant integer BUFF_ID = 'B003'
private constant string BUFF_ORDER_ID = "unholyfrenzy"
private constant string FX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant real ADDITIONAL_LIFE = 15.
private constant real AoE_ON_CAST = 250.
private constant real BUFF_DURATION = 4.
private constant damagetype DMG_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function DamangeConfig takes nothing returns nothing
set dmgLvl[1] = 80.
set dmgLvl[2] = 125.
set dmgLvl[3] = 170.
set dmgLvl[4] = 215.
set dmgLvl[5] = 260.
endfunction
private function BuffConfig takes nothing returns nothing
set buffLvl[1] = 'A00A'
set buffLvl[2] = 'A00B'
set buffLvl[3] = 'A00C'
endfunction
private function GetLifePercent takes unit u returns real
return GetWidgetLife(u)/GetUnitState(u,UNIT_STATE_MAX_LIFE)*100.
endfunction
private function GetAdditionalLife takes unit u returns real
return (I2R(GetHeroStr(u,true)-GetHeroStr(u,false))*ADDITIONAL_LIFE)/100.
endfunction
private function FilterConfig takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u,UNIT_TYPE_STRUCTURE)
endfunction
//==========================================================
//ENDCONFIG
private struct Frenesi
static xecast caster
unit c
unit u
player p
integer count
integer chargue
real duration
real dur
real dmg
boolean up
boolean haveBite
static method recycle takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
call SetPlayerAbilityAvailable(.p,SPELL_ID,false)
call SetPlayerAbilityAvailable(.p,PASSIVE_SPELL_ID,true)
set .haveBite=false
call ReleaseTimer(t)
set t=null
endmethod
static method onPeriodic takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
set .dur=.dur+.03125
call IssueImmediateOrderById(.c,851972)
if .dur>=.3 then
call SetUnitTimeScale(.c,1.)
if GetWidgetLife(.u)>.405 then
call IssueTargetOrderById(.c,851983,.u)
endif
call ReleaseTimer(t)
endif
set t=null
endmethod
static method onBiteCast takes nothing returns nothing
local thistype this=GetUnitId(GetTriggerUnit())
local integer lvl=GetUnitAbilityLevel(.c,PASSIVE_SPELL_ID)
local real addDmg
local real d=SquareRoot(Pow(GetUnitX(.u)-GetUnitX(.c),2)+Pow(GetUnitY(.u)-GetUnitY(.c),2))
call TimerStart(NewTimerEx(this),.1,false,function thistype.recycle)
if d<=AoE_ON_CAST and GetWidgetLife(.u)>.405 then
call DestroyEffect(AddSpecialEffectTarget(FX,.u,"origin"))
set addDmg=100.-GetLifePercent(.u)
set .dmg=dmgLvl[lvl]+addDmg+GetAdditionalLife(.c)
set .dur=0.
call IssueImmediateOrderById(.c,851972)
call SetUnitAnimation(.c,"Attack")
call SetUnitTimeScale(.c,2.)
call TimerStart(NewTimerEx(this),.03125,true,function thistype.onPeriodic)
call UnitDamageTarget(.c,.u,.dmg,false,false,ATTACK_TYPE_HERO,DMG_TYPE,null)
endif
set .count=3
endmethod
method executeCaster takes integer abilId, integer lvl returns nothing
set caster.owningplayer = .p
set caster.abilityid = abilId
set caster.level = lvl
set caster.orderstring = BUFF_ORDER_ID
call caster.castOnTarget(.c)
endmethod
method destroy takes nothing returns nothing
if .haveBite then
call SetPlayerAbilityAvailable(.p,SPELL_ID,false)
call SetPlayerAbilityAvailable(.p,PASSIVE_SPELL_ID,true)
set .haveBite=false
endif
set .c=null
set .u=null
set .p=null
set .count=0
endmethod
static method onLoop takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
set .duration=.duration-.03125
if .duration==0. then
call .destroy()
call ReleaseTimer(t)
endif
set t=null
endmethod
static method filter takes nothing returns boolean
local thistype this=DamageEvent.sourceId
local player p1=GetOwningPlayer(DamageEvent.target)
local player p2=GetOwningPlayer(DamageEvent.source)
return IsPlayerEnemy(p1,p2) and FilterConfig(DamageEvent.target) and GetUnitAbilityLevel(DamageEvent.source,PASSIVE_SPELL_ID)>0
endmethod
static method onDamage takes nothing returns nothing
local thistype this=DamageEvent.sourceId
local integer lvl=GetUnitAbilityLevel(DamageEvent.source,PASSIVE_SPELL_ID)
local real x=GetUnitX(DamageEvent.source)
local real y=GetUnitY(DamageEvent.source)
set .count=.count+1
set .duration=BUFF_DURATION
if .count>=3 and .count<4 then
if GetUnitAbilityLevel(.c,BUFF_ID)==0 then
call .executeCaster(buffLvl[1],lvl)
endif
endif
if .count>=4 and .count<7 then
if .up then
set .chargue=.chargue+1
endif
call .executeCaster(buffLvl[.chargue],lvl)
endif
if .count==6 then
if not .haveBite then
call SetPlayerAbilityAvailable(.p,PASSIVE_SPELL_ID,false)
call SetPlayerAbilityAvailable(.p,SPELL_ID,true)
set .haveBite=true
endif
set .count=3
set .up=false
set .chargue=3
endif
if .u!=null then
set .u=null
endif
set .u=DamageEvent.target
if .c==null then
set .c=DamageEvent.source
set .p=GetOwningPlayer(.c)
set .chargue=0
set .up=true
call TimerStart(NewTimerEx(this),.03125,true,function thistype.onLoop)
endif
endmethod
implement DamageEvent
static method onLearnedSkill takes nothing returns nothing
local unit u=GetTriggerUnit()
if (GetLearnedSkill()==PASSIVE_SPELL_ID) and GetUnitAbilityLevel(u,PASSIVE_SPELL_ID)==1 then
call UnitAddAbility(u,SPELL_ID)
call SetPlayerAbilityAvailable(GetTriggerPlayer(),SPELL_ID,false)
endif
set u=null
endmethod
static method onInit takes nothing returns nothing
set caster=xecast.create()
set caster.recycledelay=.1
call DamangeConfig()
call BuffConfig()
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL,function thistype.onLearnedSkill)
call RegisterSpellEffectEvent(SPELL_ID,function thistype.onBiteCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MajesticRoar
globals
private real array dmgLvl
endglobals
//Todas las estadisticas de la habilidad han sido sacadas de:
//http://gameinfo.lan.leagueoflegends.com/es/game-info/champions/volibear/
//CONFIG
//==========================================================
globals
private constant integer SPELL_ID = 'A00E'
private constant integer DUMMY_ID = 'e000'
private constant integer DUMMY_EFECT_ID = 'h002'
private constant integer DUMMY_SPELL_ID = 'A004'
private constant string DUMMY_ORDER_STRING = "slow"
private constant string EFECT_FX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
private constant real EFECT_DURATION = .40
private constant real AoE = 425.
private constant real AP_POWER = 60.
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
private constant boolean GET_ATT_BONUS = true
endglobals
private function DamangeConfig takes nothing returns nothing
set dmgLvl[1] = 60.
set dmgLvl[2] = 105.
set dmgLvl[3] = 150.
set dmgLvl[4] = 195.
set dmgLvl[5] = 240.
endfunction
private function GetAttribute takes unit u, boolean bonus returns real
return (I2R(GetHeroInt(u,bonus))*AP_POWER)/100.
endfunction
private function FilterConfig takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//==========================================================
//ENDCONFIG
private struct MajesticRoar
static xecast caster
static unit f=null
static method destroyEffect takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
call RemoveUnit(GetUnitById(this))
call ReleaseTimer(t)
set t=null
endmethod
static method filter takes nothing returns boolean
local player p=GetTriggerPlayer()
set f=GetFilterUnit()
return IsPlayerEnemy(GetOwningPlayer(f),p) and GetWidgetLife(f)>.405 and FilterConfig(f)
endmethod
static method onCast takes nothing returns nothing
local unit c =GetTriggerUnit()
local player p =GetTriggerPlayer()
local real x =GetUnitX(c)
local real y =GetUnitY(c)
local integer lvl=GetUnitAbilityLevel(c,SPELL_ID)
local real dmg =GetAttribute(c,GET_ATT_BONUS)+dmgLvl[lvl]
local unit d =CreateUnit(p,DUMMY_ID,x,y,0.)
local unit m =CreateUnit(p,DUMMY_EFECT_ID,x,y,0.)
local thistype id=GetUnitId(m)
local integer duration=R2I(EFECT_DURATION*1000.)
local real spaceWave =2.*duration/10.
local real timeWave =2.*AoE/200.
local real radiusRatio=AoE/50.
local unit u
call TimerStart(NewTimerEx(id),EFECT_DURATION,false,function thistype.destroyEffect)
call DestroyEffect(AddSpecialEffect(EFECT_FX,x,y))
call TerrainDeformStop(TerrainDeformRipple(x,y,AoE,200.,duration,2,spaceWave,timeWave,radiusRatio,true),duration)
call GroupEnumUnitsInArea(ENUM_GROUP,x,y,AoE,Filter(function thistype.filter))
set caster.owningplayer=p
set caster.level=lvl
loop
set u=FirstOfGroup(ENUM_GROUP)
call GroupRemoveUnit(ENUM_GROUP,u)
call UnitDamageTarget(d,u,dmg,false,false,ATTACK_TYPE_HERO,DMG_TYPE,null)
call caster.castOnTarget(u)
exitwhen u==null
endloop
call RemoveUnit(d)
set c=null
set d=null
set m=null
endmethod
static method onInit takes nothing returns nothing
call DamangeConfig()
set caster=xecast.createBasic(DUMMY_SPELL_ID,OrderId(DUMMY_ORDER_STRING),Player(15))
set caster.recycledelay=.1
call RegisterSpellEffectEvent(SPELL_ID,function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ThunderClaws
globals
private real array dmgLvl
endglobals
//Todas las estadisticas de la habilidad han sido sacadas de:
//http://gameinfo.lan.leagueoflegends.com/es/game-info/champions/volibear/
//CONFIG
//==========================================================
globals
//Lo basico:
private constant integer DUMMY_MISSILE_ID = 'h001'
private constant integer SPELL_ID = 'A003'
//Configuraciones de habilidad y buff casteados por el dummy:
private constant integer DUMMY_SPELL_ID = 'A00N'
private constant integer BUFF_ID = 'B008'
private constant string DUMMY_ORDER_STRING = "spiritlink"
//Efectos:
private constant string EFECT_FX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant string FX_ON_CAST = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdl"
//Tipo de rayo:
private constant string LIGHTNING_EFECT = "CLPB"
//Reales:
private constant real AoE = 300.
private constant real AP_POWER = 30.
//Valores de escala:
private constant real DEFAULT_SCALE = 1.05
private constant real MAX_SCALE_PERCENT = 46.
private constant real MAX_LIGHTNING_Z = 170.
//Configuraciones relacionadas con el daño provocado:
private constant integer MAX_HITS = 4
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
private constant boolean GET_ATT_BONUS = true
endglobals
private function DamangeConfig takes nothing returns nothing
set dmgLvl[1] = 75.
set dmgLvl[2] = 115.
set dmgLvl[3] = 155.
endfunction
private function GetAttribute takes unit u, boolean bonus returns real
return (I2R(GetHeroInt(u,bonus))*AP_POWER)/100.
endfunction
private function FilterConfig takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//==========================================================
//ENDCONFIG
private struct ThunderClaws
static constant location loc1=Location(0.,0.)
static constant location loc2=Location(0.,0.)
static unit f=null
static xecast caster
real scaleDefault
real scale
unit c
static method recycleDummy takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
call RemoveUnit(GetUnitById(this))
call ReleaseTimer(t)
set t=null
endmethod
static method scaleLoop takes nothing returns nothing
local timer t=GetExpiredTimer()
local thistype this=GetTimerData(t)
if GetUnitAbilityLevel(.c,BUFF_ID)>0 then
if .scale<(DEFAULT_SCALE*MAX_SCALE_PERCENT)/100.+DEFAULT_SCALE then
set .scale=.scale+.01
call SetUnitScale(.c,.scale,.scale,.scale)
endif
else
set .scale=.scale-.01
call SetUnitScale(.c,.scale,.scale,.scale)
if .scale<=DEFAULT_SCALE then
call SetUnitScale(.c,DEFAULT_SCALE,DEFAULT_SCALE,DEFAULT_SCALE)
set .c=null
call ReleaseTimer(t)
endif
endif
set t=null
endmethod
static method filterGroup takes nothing returns boolean
local player p=GetOwningPlayer(DamageEvent.source)
set f=GetFilterUnit()
return IsPlayerEnemy(GetOwningPlayer(f),p) and f!=DamageEvent.target and GetWidgetLife(f)>.405 and FilterConfig(f) and GetUnitTypeId(f)!=XE_DUMMY_UNITID and GetUnitAbilityLevel(f,'Aloc')==0
endmethod
static method filter takes nothing returns boolean
local player p1=GetOwningPlayer(DamageEvent.target)
local player p2=GetOwningPlayer(DamageEvent.source)
return IsPlayerEnemy(p1,p2) and FilterConfig(DamageEvent.target) and GetUnitAbilityLevel(DamageEvent.source,BUFF_ID) > 0
endmethod
static method onDamage takes nothing returns nothing
local unit c=DamageEvent.source
local unit t=DamageEvent.target
local player p=GetOwningPlayer(c)
local real x=GetUnitX(c)
local real y=GetUnitY(c)
local unit d=CreateUnit(p,DUMMY_MISSILE_ID,x,y,0.)
local integer lvl=GetUnitAbilityLevel(c,SPELL_ID)
local real dmg=GetAttribute(c,GET_ATT_BONUS)+dmgLvl[lvl]
local integer count=0
local real z1
local real z2
local unit u
call DestroyEffect(AddSpecialEffectTarget(EFECT_FX,c,"origin"))
call TimerStart(NewTimerEx(GetUnitId(d)),.4,false,function thistype.recycleDummy)
//! textmacro Claws takes VAR1,VAR2,VAR3,VAR4,REAL
call MoveLocation(loc1,$VAR3$,$VAR4$)
call MoveLocation(loc2,GetUnitX($VAR2$),GetUnitY($VAR2$))
set z1=GetLocationZ(loc1)
set z2=GetLocationZ(loc2)
call DestroyEffect(AddSpecialEffectTarget(EFECT_FX,t,"origin"))
call Lightning.unitToUnit($VAR1$,$VAR2$,z1+$REAL$,z2+20.,true,.3,LIGHTNING_EFECT,count)
call UnitDamageTarget(d,$VAR2$,dmg,false,false,ATTACK_TYPE_HERO,DMG_TYPE,null)
//! endtextmacro
//! runtextmacro Claws("c","t","x","y","MAX_LIGHTNING_Z")
call GroupEnumUnitsInArea(ENUM_GROUP,x,y,AoE,Filter(function thistype.filterGroup))
loop
exitwhen count==MAX_HITS-1
set u=FirstOfGroup(ENUM_GROUP)
call GroupRemoveUnit(ENUM_GROUP,u)
if u!=null then
//! runtextmacro Claws("t","u","GetUnitX(t)","GetUnitY(t)","20.")
set t=u
endif
set count=count+1
endloop
set c=null
set t=null
set d=null
set u=null
endmethod
implement DamageEvent
static method onCast takes nothing returns nothing
local unit u=GetTriggerUnit()
local thistype this=GetUnitId(u)
set .c=u
call TimedEffect.createOnUnit(FX_ON_CAST,u,"origin",.5)
if GetUnitAbilityLevel(.c,BUFF_ID)==0 then
set .scale=DEFAULT_SCALE
call TimerStart(NewTimerEx(this),.03125,true,function thistype.scaleLoop)
endif
call caster.castOnTarget(u)
set u=null
endmethod
static method onInit takes nothing returns nothing
call DamangeConfig()
set caster=xecast.createBasic(DUMMY_SPELL_ID,OrderId(DUMMY_ORDER_STRING),Player(15))
set caster.recycledelay=.1
call RegisterSpellEffectEvent(SPELL_ID,function thistype.onCast)
endmethod
endstruct
endscope