Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Refreash initializer init
private function Actions takes nothing returns nothing
call SetUnitState(gg_unit_U000_0001,UNIT_STATE_LIFE,GetUnitState(gg_unit_U000_0001,UNIT_STATE_MAX_LIFE))
call SetUnitState(gg_unit_U000_0001,UNIT_STATE_MANA,GetUnitState(gg_unit_U000_0001,UNIT_STATE_MAX_MANA))
call UnitResetCooldown(gg_unit_U000_0001)
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger trig=CreateTrigger()
local integer index
set index=0
loop
exitwhen index==bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerEvent(trig,Player(index),EVENT_PLAYER_END_CINEMATIC)
set index=index+1
endloop
call TriggerAddAction(trig,function Actions)
set trig=null
endfunction
endlibrary
//TESH.scrollpos=199
//TESH.alwaysfold=0
//
//
// Spell Struct
// By Jesus4Lyf.
// Version 1.0.7.
//
// What is SpellStruct?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// SpellStruct is a system designed for the sake of rapid spell development.
// It grants features which can be entirely encapsulated in a struct type per
// ability. It handles event response creation, timer attachment, trigger
// attachment, area of effect (AoE) enumeration, unit attachment, and all
// spells made using it should be automatically MUI/leakless and rather efficient.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called SpellStruct, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// _______________________
// || ||
// || SpellStruct Usage ||
// || ||
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Writing a Simple SpellStruct:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - To use SpellStruct, write SpellStructs. These are structs which extend
// SpellStruct, and implement SpellStruct.
//
// - Everything is optional to implement/use, except setting thistype.abil='Axxx'
// in static method onInit.
//
// - Example:
//
/* struct MySpell extends SpellStruct
implement SpellStruct
private static method onInit takes nothing returns nothing
set thistype.abil='A000'
endmethod
endstruct
*/
// Event Responses:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - The following is a list of event responses that come with SpellStructs.
//
// - Event responses are stored on your struct as members.
/*
// Members of your SpellStruct:
unit caster // The casting unit.
integer level // The level of the ability on the casting unit.
integer order // The order id of the caster when it began casting the spell.
unit targetUnit // The target unit of the ability being cast (null if none).
item targetItem // The target item of the ability being cast (null if none).
destructable targetDest // The target destructable of the ability being cast (null if none).
widget targetWidget // The target unit, item or destructable (see above).
real targetX // The point targetted when channeling began (X/Y coordinates).
real targetY // If spell is targetted, this is the original position of the target.
player owner // The owner of the casting unit, when channeling began.
integer abilId // The ability id of the ability being cast.
// Extra methods:
real casterX // The caster's X coordinate (inlines to GetUnitX(this.caster))
real casterY // The caster's Y coordinate (inlines to GetUnitY(this.caster))
*/
// - The above event response list may be used anywhere in your SpellStruct,
// and at any time.
//
// - You may implement methods which are called when the normal Warcraft III
// spell events would fire. Some of the Warcraft III event responses are
// broken for certain events, sometimes intermittently, but these are fixed
// when using SpellStruct.
// Also, usually in Warcraft III, these are implemented in a way that cycles
// through all triggered abilities to see if the spell cast is the spell the
// trigger is for. In SpellStruct, this is changed so that Warcraft III will
// jump straight to the method for the spell that was cast.
//
// - These methods, which are called when events fire, are non-static. This means
// any members you add in your SpellStruct can be accessed from within the method.
// This is achieved with unit attachment (internally).
//
// - Example:
//
/* struct MySpell extends SpellStruct
implement SpellStruct
// Reserved method names for event methods, in order of firing:
method onCreate takes nothing returns nothing // fires just before channel
// If this is an AoE spell...
set this.aoe=this.level*200
// Maybe you want to control the lifetime of the struct yourself:
set this.autoDestroy=false // there is a thistype.autoDestroyDefault too,
// which is true by default.
// So you can conditionally control the lifetime.
// Do not destroy structs before the spell ends.
endmethod
method onChannel takes nothing returns nothing // Unit starts channeling a spell.
endmethod
method onStartCast takes nothing returns nothing // Fires much as the same as channel.
endmethod
method onEffect takes nothing returns nothing // When the spell has successfully cast, mana deducted.
endmethod // Will only fire if channeling successfully completed.
method onFinish takes nothing returns nothing // When the effect finishes (or is interrupted).
endmethod // This will only fire if the effect fired.
method onStopCast takes nothing returns nothing // When the spell stops being cast, or is cancelled.
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
set thistype.autoDestroyDefault=true // set to true by default.
// may be overridden for each
// instance by setting
// this.autoDestroy=true/false
// AutoDestroyed structs are destroyed after the spell stops casting.
set thistype.defaultAoE=200.0 // optional.
endmethod
endstruct
*/
// Disabling Auto-Destruction:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - SpellStructs are created just before the .onChannel event method is
// called.
//
// - By default, SpellStructs are automatically destroyed, and this occurs
// just after the method .onStopCast is called.
//
// - You may extend this lifetime in two ways. If you wish to manually
// manage the lifetime of a SpellStruct, you may do either of the following:
/* struct MySpell extends SpellStruct
implement SpellStruct
method onStopCast takes nothing returns nothing
call SpellStruct(this).destroy() // can call this at any time. JassHelper bug requires SpellStruct(this).
endmethod // no more event methods fire after destroying a SpellStruct.
method onCreate takes nothing returns nothing
set this.autoDestroy=false // Either this
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
set thistype.autoDestroyDefault=false // or this.
endmethod
endstruct
*/
// - Setting thistype.autoDestroyDefault to false causes .autoDestroy to
// be set to false just before onCreate is called, for all new instances.
//
// - Setting this.autoDestroy to false stops just the current instance
// from auto destruction. Setting it back to true will cause the struct
// to be destroyed if it usually would have been by then, else continue
// to monitor it for auto destruction.
//
// Locking:
// ¯¯¯¯¯¯¯¯¯¯
// - You may also add locks to an instance. This is done using this.addLock(),
// and this.removeLock(). Calling this.addLock increments a counter, and
// calling this.removeLock() decrements it. While it is greater than 0,
// a SpellStruct will not be autodestroyed. Decrementing it back to 0
// after the .onStopCast method has fired while .autoDestroy is true will
// destroy the struct automatically. This is a garbage collection mechanism.
//
// - You may check if a struct has no locks on it by using .isNotLocked.
//
// - Example:
//
/* struct MySpell extends SpellStruct
implement SpellStruct
private integer ticks=57
// T32x example.
private method periodic takes nothing returns nothing
set this.ticks=this.ticks-1
if this.ticks==0 then
call BJDebugMsg("Fired 57 times")
call this.stopPeriodic()
call this.removeLock() // Remove lock called here.
endif // will only destroy the struct if casting finished,
endmethod // so that event methods will continue to fire and
implement T32x // all data is available.
method onEffect takes nothing returns nothing
call this.startPeriodic()
call this.addLock() // We want the struct to exist until .stopPeriodic is called.
endmethod // Locks are useful because we could attach this instance to any
// number of things, which can each unlock it when they are done with it.
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
endmethod
endstruct
*/
// Timers:
// ¯¯¯¯¯¯¯¯¯
// - If you have TimerUtils in your map, SpellStruct will operate using
// TimerUtils data attachment for timers.
// - If you don't have TimerUtils, but have Recycle, SpellStruct will
// attach to Recycled timers using GetHandleId and a hashtable.
// - If you have neither, SpellStruct will create timers dynamically,
// and pause and destroy them when they are done with. Attachment
// will be done with GetHandleId and a hashtable.
//
// - .startTimer(method, period) will start a timer for the given method,
// for the current spell instance. This means all spell event responses
// will be available from within the callback. This timer will keep
// firing until you stop it using .stopTimer(method).
//
// - Starting a timer in this way automatically adds a lock to the struct,
// and stopping a timer removes a lock. This is to guarantee that when
// a timer method fires, all data is available and valid (while a struct
// has locks, it will not be auto destroyed).
//
// - Manually calling .destroy on a spell struct will stop all timers for
// that struct automatically.
//
// - Because it attaches both the method to call and the struct instance
// to the timer, and then fires the method with .execute(), it is
// recommended that you use this only for timers that are reasonably
// infrequent. Using T32x with SpellStruct is recommended for high
// frequency (low period) timers.
//
// - Example:
//
/* struct MySpell extends SpellStruct
implement SpellStruct
private method whenTimerExpires takes nothing returns nothing
call KillUnit(this.targetUnit) // Kill the target of the spell.
call this.stopTimer(thistype.whenTimerExpires)
endmethod
method onEffect takes nothing returns nothing
call this.startTimer(thistype.whenTimerExpires,5.0) // Run .whenTimerExpires in 5.0 seconds.
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
endmethod
endstruct
*/
// Triggers:
// ¯¯¯¯¯¯¯¯¯¯¯
// - Trigger attaching works using GetHandleId and a hashtable.
//
// - .createTrigger(method) will create a trigger with the method as it's
// action. Do not use DestroyTrigger to remove this trigger, you must
// use .destroyTrigger(method) to destroy it instead. This saves having
// to store the trigger in any sort of variable, generally. Destroying
// a trigger using SpellStruct has protection against the double free
// bug in Warcraft III, even if you use TriggerSleepAction.
//
// - Creating a trigger in this way automatically adds a lock to the struct,
// and destroying it removes this lock. This is to guarantee that when
// a timer method fires, all data is available and valid (while a struct
// has locks, it will not be auto destroyed).
//
// - Manually calling .destroy on a spell struct will destroy all triggers
// for that struct automatically.
//
// - Example:
//
/* struct MySpell extends SpellStruct
implement SpellStruct
// Gets a random real between -300.0 and 300.0.
private static constant real targetOffset=300.0
private static constant method getOffset takes nothing returns real // inlines
return GetRandomReal(-thistype.targetOffset, thistype.targetOffset)
endmethod
// For skipping an order, when reissuing it.
private boolean skipOrder = false
private method onTargetPointOrder takes nothing returns nothing
if this.skipOrder then
set this.skipOrder=false
else
set this.skipOrder=true
call IssuePointOrderById(this.targetUnit, GetIssuedOrderId(), GetOrderPointX() + thistype.getOffset(), GetOrderPointY() + thistype.getOffset())
endif
endmethod
private method onEffect takes nothing returns nothing
// Blurs the aim of the target for 5.0 seconds.
call TriggerRegisterUnitEvent(this.createTrigger(thistype.onTargetPointOrder), this.targetUnit, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerSleepAction(5.0) // a lock from the trigger will stop the struct from being destroyed.
call this.destroyTrigger(thistype.onTargetPointOrder)
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
endmethod
endstruct
*/
// AoE (Area of Effect) enumeration:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - You may set the AoE of a spell instance using set this.aoe = x, or you
// may set the default value for the aoe of a spell using set
// thistype.defaultAoE = x. This will set the value of .aoe to the value
// specified before onCreate is called.
//
// - You may enumerate the units in AoE of the target point, the current
// position of the targetted object, or the caster. This requires a unit
// filter to be applied.
//
// - This enum does not clear the group before hand, like native enums.
// Actually, it should even be safe to use with dynamic groups.
//
/* struct MySpell extends SpellStruct
implement SpellStruct
private method myFilter takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD)
endmethod
private method onEffect takes nothing returns nothing
local group g = CreateGroup()
call this.enumUnitsInAoE(g, thistype.myFilter) // Like a default WC3 ability, enum in range of the point targetted when channeling began.
call this.enumUnitsInAoETarget(g, thistype.myFilter) // Enums all units current within aoe of the targetted widget (unit/item/destructable).
call this.enumUnitsInAoECaster(g, thistype.myFilter) // Enums all units current within aoe of the caster.
//...
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
set thistype.defaultAoE=500.0
endmethod
endstruct
*/
// - You may also skip groups altogether and do actions for all units within
// aoe of the target point/target/caster.
//
/* struct MySpell extends SpellStruct
implement SpellStruct
private method myAction takes unit u returns nothing
if not IsUnitType(u, UNIT_TYPE_DEAD) then
call SetUnitX(u, this.casterX)
call SetUnitY(u, this.casterY)
endif
endmethod
private method onEffect takes nothing returns nothing
set this.aoe = this.level * 100.0 + 100.0
call this.forUnitsInAoE(thistype.myAction) // Like a default WC3 ability, units in range of the point targetted when channeling began.
call this.forUnitsInAoETarget(thistype.myAction) // For all units current within aoe of the targetted widget (unit/item/destructable).
call this.forUnitsInAoECaster(thistype.myAction) // For all units current within aoe of the caster.
//...
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil='Axxx'
endmethod
endstruct
*/
// - You may also check, for a single unit, to see if it is within the AoE
// of the target point, target or caster, using the following:
// - this.isUnitInAoE(myUnit) // within aoe of target point.
// - this.isUnitInAoETarget(myUnit) // within aoe of target.
// - this.isUnitInAoECaster(myUnit) // within aoe of caster.
// - The above return booleans.
//
// - All AoE functionality takes into account the collision size of the
// enumerated units. This matches better with Warcraft III AoE detection,
// which highlights units the spell will hit in green for AoE abilities.
//
// Miscellaneous:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - call this.forGroup(g, callback method takes unit)
// Example:
//
/* method myCallback takes unit u returns nothing
call SetWidgetLife(u, GetWidgetLife(this.caster))
endmethod
// in some method:
call this.forGroup(g, thistype.myCallback) // set life for all units in group to the caster's life.
*/
// - real this.getDistanceToTargetWidget() // The distance between the caster and the target widget.
// - real this.getDistanceToTargetPoint() // The distance between the caster and the original point targetted.
// - real this.getAngleToTargetWidget() // The angle from the caster to the target object (in radians).
// - real this.getAngleToTargetPoint() // The angle from the caster to the target location (in radians).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for helping me with the interface hint to make the methods
// optional.
//
library SpellStruct uses optional TimerUtils, optional Recycle
//===========================================================================
// Configurables
//
globals
private constant real MAX_UNIT_COLLISION_SIZE=256.0
endglobals
//===========================================================================
// Header Declarations
//
globals
private group GROUP=CreateGroup()
private hashtable STORE=InitHashtable() // attaches to:
// abilid, methodkey
// abilid, unit
// timer, key
// trigger, key
// spellstruct, method (timer)
// spellstruct, method (trigger)
endglobals
private function interface Method takes integer this returns nothing
private function interface UnitMethodFilter takes integer this, unit u returns boolean
private function interface UnitMethod takes integer this, unit u returns nothing
private function interface Allocator takes nothing returns integer
//===========================================================================
// Recursion stack
//
private module Stack
static thistype top=0 // thistype(0) throws a syntax error, does not compile
static method increment takes nothing returns nothing
set thistype.top=thistype(thistype.top+1)
endmethod
static method decrement takes nothing returns nothing
set thistype.top=thistype(thistype.top-1)
endmethod
endmodule
//===========================================================================
// Defaults
//
private interface DefaultsInterface
// For methods that are not implemented.
// In order of firing.
method onCreate takes nothing returns nothing defaults nothing
method onChannel takes nothing returns nothing defaults nothing
method onStartCast takes nothing returns nothing defaults nothing
method onEffect takes nothing returns nothing defaults nothing // May not always fire in casting a given spell.
method onFinish takes nothing returns nothing defaults nothing // May not fire in casting a spell, Blizzard's event response fails.
method onStopCast takes nothing returns nothing defaults nothing // Blizzard's event response sometimes fails.
method cleanup takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
// Event Handlers
//
globals
private constant key ALLOCATOR
endglobals
globals//locals
private DefaultsInterface ThisCastData
private integer CastingAbility
endglobals
//! textmacro SpellStruct__EventResponse takes METHOD_NAME, FUNCTION_NAME, FIRST_EVENT, LAST_EVENT
private function $FUNCTION_NAME$ takes nothing returns boolean
set CastingAbility=GetSpellAbilityId()
static if $FIRST_EVENT$ then
set ThisCastData=DefaultsInterface(LoadInteger(STORE,CastingAbility,ALLOCATOR))
if ThisCastData==0 then
return false
endif
set ThisCastData=DefaultsInterface(Allocator(LoadInteger(STORE,CastingAbility,ALLOCATOR)).evaluate())
call SaveInteger(STORE,CastingAbility,GetHandleId(GetTriggerUnit()),ThisCastData)
else
set ThisCastData=DefaultsInterface(LoadInteger(STORE,CastingAbility,GetHandleId(GetTriggerUnit())))
endif
//call BJDebugMsg("$FUNCTION_NAME$ - Method: "+I2S(LoadInteger(STORE,CastingAbility,$METHOD_KEY$))+" for struct "+I2S(ThisCastData))
if ThisCastData!=0 then
call ThisCastData.$METHOD_NAME$.execute()
static if $LAST_EVENT$ then
call ThisCastData.cleanup.evaluate()
endif
endif
return false
endfunction
//! endtextmacro
//! runtextmacro SpellStruct__EventResponse("onChannel","OnChannel","true","false")
//! runtextmacro SpellStruct__EventResponse("onStartCast","OnStartCast","false","false")
//! runtextmacro SpellStruct__EventResponse("onEffect","OnEffect","false","false")
//! runtextmacro SpellStruct__EventResponse("onFinish","OnFinish","false","false")
//! runtextmacro SpellStruct__EventResponse("onStopCast","OnStopCast","false","true")
//===========================================================================
// Attachment.
//
private struct ChainAttach
trigger trigger
timer timer
integer instance
Method callback
thistype next
thistype prev
endstruct
//===========================================================================
// Timers (requires Attachment).
//
globals
private constant key TIMER_DATA
endglobals
private function SetTimerStruct takes timer t, integer data returns nothing
static if LIBRARY_TimerUtils then
call SetTimerData(t,data)
else
call SaveInteger(STORE,GetHandleId(t),TIMER_DATA,data)
endif
endfunction
private function GetTimerStruct takes timer t returns integer
static if LIBRARY_TimerUtils then
return GetTimerData(t)
else
return LoadInteger(STORE,GetHandleId(t),TIMER_DATA)
endif
endfunction
//! textmacro SpellStruct__GetTimer takes VARIABLE
static if LIBRARY_TimerUtils then
set $VARIABLE$=NewTimer()
elseif LIBRARY_Recycle then
set $VARIABLE$=Timer.get()
else
set $VARIABLE$=CreateTimer()
endif
//! endtextmacro
//! textmacro SpellStruct__ReleaseTimer takes TIMER
static if LIBRARY_TimerUtils then
call ReleaseTimer($TIMER$)
elseif LIBRARY_Recycle then
call RemoveSavedInteger(STORE,GetHandleId($TIMER$),TIMER_DATA)
call Timer.release($TIMER$)
else
call RemoveSavedInteger(STORE,GetHandleId($TIMER$),TIMER_DATA)
call PauseTimer($TIMER$)
call DestroyTimer($TIMER$)
//set $TIMER$=null // using globals, unnecessary.
endif
//! endtextmacro
//===========================================================================
// Triggers (requires Attachment).
//
globals
private constant key TRIGGER_DATA
endglobals
private function SetTriggerStruct takes trigger t, integer data returns nothing
call SaveInteger(STORE,GetHandleId(t),TRIGGER_DATA,data)
endfunction
private function GetTriggerStruct takes trigger t returns integer
return LoadInteger(STORE,GetHandleId(t),TRIGGER_DATA)
endfunction
//! textmacro SpellStruct__FlushTrigger takes TRIGGER
call RemoveSavedInteger(STORE,GetHandleId($TRIGGER$),TRIGGER_DATA)
//! endtextmacro
//===========================================================================
// AoE enumeration.
//
private struct EnumStack extends array
implement Stack
integer instance
real x
real y
real range
integer callback
group for
endstruct
private function InternalEnum takes nothing returns boolean // Could be recursive.
local unit u=GetFilterUnit() // can't be a global due to recursion.
if IsUnitInRangeXY(u,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range) then // Factors in collision sizes.
if UnitMethodFilter(EnumStack.top.callback).evaluate(EnumStack.top.instance,u) then
call GroupAddUnit(EnumStack.top.for,u)
endif
endif
set u=null
return false
endfunction
private function InternalFor takes nothing returns boolean // Could be recursive.
local unit u=GetFilterUnit() // can't be a global due to recursion.
if IsUnitInRangeXY(u,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range) then // Factors in collision sizes.
call UnitMethod(EnumStack.top.callback).execute(EnumStack.top.instance,u)
endif
set u=null
return false
endfunction
//===========================================================================
// Exposed Interface - SpellStruct
//
struct SpellStruct extends DefaultsInterface
//===========================================================================
// Various Spell-based Methods.
//
method getDistanceToTargetWidget takes nothing returns real
local real x=GetWidgetX(this.targetWidget)-this.casterX
local real y=GetWidgetY(this.targetWidget)-this.casterY
return SquareRoot(x*x+y*y)
endmethod
method getDistanceToTargetPoint takes nothing returns real
local real x=this.targetX-this.casterX
local real y=this.targetY-this.casterY
return SquareRoot(x*x+y*y)
endmethod
method getAngleToTargetWidget takes nothing returns real // radians.
return Atan2(GetWidgetY(this.targetWidget)-this.casterY,GetWidgetX(this.targetWidget)-this.casterX)
endmethod
method getAngleToTargetPoint takes nothing returns real // radians.
return Atan2(this.targetY-this.casterY,this.targetX-this.casterX)
endmethod
//===========================================================================
// ForGroup.
//
private static method forGroupCallback takes nothing returns nothing
call UnitMethod(EnumStack.top.callback).execute(EnumStack.top.instance,GetEnumUnit()) // bigger crimes have been committed, but not much bigger.
endmethod
method forGroup takes group g, UnitMethod callback returns nothing
call EnumStack.increment() // just borrowing that stack... would break if event reponses accessed the stack directly, but they don't.
set EnumStack.top.instance=this
set EnumStack.top.callback=callback
call ForGroup(g,function thistype.forGroupCallback)
call EnumStack.decrement()
endmethod
//===========================================================================
// AoE enumeration.
//
real aoe
method enumUnitsInAoE takes group whichGroup, UnitMethodFilter filter returns nothing /*eg: method filter takes unit u returns boolean*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=this.targetX
set EnumStack.top.y=this.targetY
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=filter
set EnumStack.top.for=whichGroup
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalEnum))
call EnumStack.decrement()
endmethod
method enumUnitsInAoETarget takes group whichGroup, UnitMethodFilter filter returns nothing /*eg: method filter takes unit u returns boolean*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=GetWidgetX(this.targetWidget)
set EnumStack.top.y=GetWidgetY(this.targetWidget)
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=filter
set EnumStack.top.for=whichGroup
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalEnum))
call EnumStack.decrement()
endmethod
method enumUnitsInAoECaster takes group whichGroup, UnitMethodFilter filter returns nothing /*eg: method filter takes unit u returns boolean*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=GetUnitX(this.caster)
set EnumStack.top.y=GetUnitY(this.caster)
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=filter
set EnumStack.top.for=whichGroup
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalEnum))
call EnumStack.decrement()
endmethod
method forUnitsInAoE takes UnitMethod callback returns nothing /*eg: method callback takes unit u returns nothing*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=this.targetX
set EnumStack.top.y=this.targetY
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=callback
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalFor))
call EnumStack.decrement()
endmethod
method forUnitsInAoETarget takes UnitMethod callback returns nothing /*eg: method callback takes unit u returns nothing*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=GetWidgetX(this.targetWidget)
set EnumStack.top.y=GetWidgetY(this.targetWidget)
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=callback
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalFor))
call EnumStack.decrement()
endmethod
method forUnitsInAoECaster takes UnitMethod callback returns nothing /*eg: method callback takes unit u returns nothing*/
call EnumStack.increment()
set EnumStack.top.instance=this
set EnumStack.top.x=GetUnitX(this.caster)
set EnumStack.top.y=GetUnitY(this.caster)
set EnumStack.top.range=this.aoe
set EnumStack.top.callback=callback
call GroupEnumUnitsInRange(GROUP,EnumStack.top.x,EnumStack.top.y,EnumStack.top.range+MAX_UNIT_COLLISION_SIZE,Filter(function InternalFor))
call EnumStack.decrement()
endmethod
method isUnitInAoE takes unit u returns boolean
return IsUnitInRangeXY(u,this.targetX,this.targetY,this.aoe)
endmethod
method isUnitInAoETarget takes unit u returns boolean
return IsUnitInRangeXY(u,GetWidgetX(this.targetWidget),GetWidgetY(this.targetWidget),this.aoe)
endmethod
method isUnitInAoECaster takes unit u returns boolean
return IsUnitInRangeXY(u,GetUnitX(this.caster),GetUnitY(this.caster),this.aoe)
endmethod
//===========================================================================
// Auto Cleanup.
//
private boolean doAutoDestroy // set in module's create method.
/*protected*/ boolean hasStoppedCasting=false
method operator autoDestroy= takes boolean flag returns nothing
if flag then
if this.hasStoppedCasting and this.isNotLocked then
set this.hasStoppedCasting=false // double free safety
call this.destroy()
else
set this.doAutoDestroy=true
endif
else
set this.doAutoDestroy=false
endif
endmethod
method operator autoDestroy takes nothing returns boolean
return this.doAutoDestroy
endmethod
//===========================================================================
// Locking.
//
private integer lockLevel=0
method operator isLocked takes nothing returns boolean
return this.lockLevel>0
endmethod
method operator isNotLocked takes nothing returns boolean
return this.lockLevel==0
endmethod
method addLock takes nothing returns nothing
set this.lockLevel=this.lockLevel+1
endmethod
method removeLock takes nothing returns nothing
set this.lockLevel=this.lockLevel-1
if this.hasStoppedCasting and this.isNotLocked and this.doAutoDestroy then
set this.hasStoppedCasting=false // double free safety
call this.destroy()
endif
endmethod
//===========================================================================
// Attachment.
//
private static ChainAttach attachNode
private static ChainAttach attachHead
//! textmacro SpellStruct__CreateAttachmentHead
set thistype.attachHead=ChainAttach.create()
set thistype.attachHead.next=thistype.attachHead
set thistype.attachHead.prev=thistype.attachHead
//! endtextmacro
//! textmacro SpellStruct__CreateAttachmentNode takes HEAD
// Create node.
set thistype.attachNode=ChainAttach.create()
// Link node (at end of list).
set thistype.attachHead=$HEAD$
set thistype.attachHead.prev.next=thistype.attachNode
set thistype.attachNode.prev=thistype.attachHead.prev
set thistype.attachHead.prev=thistype.attachNode
set thistype.attachNode.next=thistype.attachHead
//! endtextmacro
//! textmacro SpellStruct__AttachmentChainLoop takes HEAD
set thistype.attachHead=$HEAD$
set thistype.attachNode=thistype.attachHead.next
loop
exitwhen thistype.attachNode==thistype.attachHead
//! endtextmacro
//! textmacro SpellStruct__AttachmentChainEndloop
set thistype.attachNode=thistype.attachNode.next
endloop
//! endtextmacro
//===========================================================================
// Timers (requires Locking).
//
private ChainAttach timerAttachments
private static method timerCallback takes nothing returns nothing
set thistype.attachNode=GetTimerStruct(GetExpiredTimer()) // first time since h2i/gamecache I've needed this.
call thistype.attachNode.callback.execute(thistype.attachNode.instance) // good reason to use T32 instead.
// cannot use thistype.attachNode anymore, value may have changed.
endmethod
method startTimer takes Method callback, real period returns nothing
debug if HaveSavedInteger(STORE,this,callback) then
debug call BJDebugMsg("SpellStruct Error: started periodic method twice for struct.")
debug endif
// Make node and attach data.
//! runtextmacro SpellStruct__CreateAttachmentNode("this.timerAttachments")
set thistype.attachNode.instance=this
set thistype.attachNode.callback=callback
// Create timer and attach data.
//! runtextmacro SpellStruct__GetTimer("thistype.attachNode.timer")
call SetTimerStruct(thistype.attachNode.timer,thistype.attachNode)
// Attach node to struct/method.
call SaveInteger(STORE,this,callback,thistype.attachNode)
// Start timer.
call TimerStart(thistype.attachNode.timer,period,true,function thistype.timerCallback)
call this.addLock()
endmethod
method stopTimer takes Method callback returns nothing
set thistype.attachNode=ChainAttach(LoadInteger(STORE,this,callback))
// Unchain attachment
set thistype.attachNode.next.prev=thistype.attachNode.prev
set thistype.attachNode.prev.next=thistype.attachNode.next
// Release Timer.
//! runtextmacro SpellStruct__ReleaseTimer("thistype.attachNode.timer")
call RemoveSavedInteger(STORE,this,callback)
call this.removeLock()
endmethod
//===========================================================================
// Triggers (requires Locking).
//
private ChainAttach triggerAttachments
private static method triggerCallback takes nothing returns boolean
set thistype.attachNode=GetTriggerStruct(GetTriggeringTrigger()) // first time since h2i/gamecache I've needed this.
call thistype.attachNode.callback.execute(thistype.attachNode.instance) // good reason to use T32 instead.
// cannot use thistype.attachNode anymore, value may have changed.
return false
endmethod
method createTrigger takes Method callback returns trigger
debug if HaveSavedInteger(STORE,this,callback) then
debug call BJDebugMsg("SpellStruct Error: created two triggers for the same method for struct.")
debug endif
// Make node and attach data.
//! runtextmacro SpellStruct__CreateAttachmentNode("this.triggerAttachments")
set thistype.attachNode.instance=this
set thistype.attachNode.callback=callback
// Create timer and attach data.
set thistype.attachNode.trigger=CreateTrigger()
call SetTriggerStruct(thistype.attachNode.trigger,thistype.attachNode)
// Attach node to struct/method.
call SaveInteger(STORE,this,callback,thistype.attachNode)
// Init trigger.
call TriggerAddCondition(thistype.attachNode.trigger,Filter(function thistype.triggerCallback))
call this.addLock()
return thistype.attachNode.trigger
endmethod
method destroyTrigger takes Method callback returns nothing
set thistype.attachNode=ChainAttach(LoadInteger(STORE,this,callback))
// Unchain attachment
set thistype.attachNode.next.prev=thistype.attachNode.prev
set thistype.attachNode.prev.next=thistype.attachNode.next
// Destroy trigger
//! runtextmacro SpellStruct__FlushTrigger("thistype.attachNode.trigger")
call DestroyTrigger(thistype.attachNode.trigger)
call RemoveSavedInteger(STORE,this,callback)
call this.removeLock()
endmethod
//===========================================================================
// Event responses.
//
readonly integer abilId
readonly unit caster
readonly integer level
readonly integer order
readonly unit targetUnit
readonly widget targetWidget
readonly destructable targetDest
readonly item targetItem
readonly location targetLoc
readonly real targetX // for target point of AoE spells.
readonly real targetY // for target point of AoE spells.
readonly player owner
method operator casterX takes nothing returns real
return GetUnitX(this.caster)
endmethod
method operator casterY takes nothing returns real
return GetUnitY(this.caster)
endmethod
private static location loc
static method create takes nothing returns thistype
local thistype this=thistype.allocate()
//===========================================================================
// Event responses.
//
set this.abilId=GetSpellAbilityId()
set this.caster=GetTriggerUnit()
set this.owner=GetOwningPlayer(this.caster)
set this.level=GetUnitAbilityLevel(this.caster,this.abilId)
set this.order=GetUnitCurrentOrder(this.caster)
// Target stuff
set this.targetUnit=GetSpellTargetUnit()
if this.targetUnit==null then
set this.targetDest=GetSpellTargetDestructable()
if this.targetDest==null then
set this.targetItem=GetSpellTargetItem()
if this.targetItem==null then
set this.targetWidget=null
set thistype.loc=GetSpellTargetLoc()
if thistype.loc==null then
set this.targetX=GetUnitX(this.caster)
set this.targetY=GetUnitY(this.caster)
else
set this.targetX=GetLocationX(thistype.loc)
set this.targetY=GetLocationY(thistype.loc)
call RemoveLocation(thistype.loc)
set thistype.loc=null // worthwhile
endif
else
set this.targetWidget=this.targetItem
set this.targetX=GetItemX(this.targetItem)
set this.targetY=GetItemY(this.targetItem)
endif
else
set this.targetWidget=this.targetDest
set this.targetItem=null
set this.targetX=GetWidgetX(this.targetDest) // shorter
set this.targetY=GetWidgetY(this.targetDest)
endif
else
set this.targetWidget=this.targetUnit
set this.targetDest=null
set this.targetItem=null
set this.targetX=GetUnitX(this.targetUnit)
set this.targetY=GetUnitY(this.targetUnit)
endif
//===========================================================================
// Attachment (timer & trigger).
//
//! runtextmacro SpellStruct__CreateAttachmentHead()
set this.timerAttachments=thistype.attachHead
//! runtextmacro SpellStruct__CreateAttachmentHead()
set this.triggerAttachments=thistype.attachHead
return this
endmethod
method destroy takes nothing returns nothing
if this.hasStoppedCasting then
set this.hasStoppedCasting=false // random double free protection on autoDestroy stuff.
else
call RemoveSavedInteger(STORE,this.abilId,GetHandleId(this.caster))
endif
//===========================================================================
// Timers
//
//! runtextmacro SpellStruct__AttachmentChainLoop("this.timerAttachments")
//! runtextmacro SpellStruct__ReleaseTimer("thistype.attachNode.timer")
call RemoveSavedInteger(STORE,this,thistype.attachNode.callback)
//! runtextmacro SpellStruct__AttachmentChainEndloop()
call this.timerAttachments.destroy()
//===========================================================================
// Triggers
//
//! runtextmacro SpellStruct__AttachmentChainLoop("this.triggerAttachments")
//! runtextmacro SpellStruct__FlushTrigger("thistype.attachNode.trigger")
call DestroyTrigger(thistype.attachNode.trigger)
call RemoveSavedInteger(STORE,this,thistype.attachNode.callback)
//! runtextmacro SpellStruct__AttachmentChainEndloop()
call this.triggerAttachments.destroy()
call this.deallocate()
endmethod
endstruct
//===========================================================================
// Exposed Module - SpellStruct
//
module SpellStruct
//===========================================================================
// AoE enumeration.
//
static real defaultAoE=0.
//===========================================================================
// Setting up the struct.
//
private static integer currentAbil=0
static method operator abil= takes integer abilId returns nothing
if thistype.currentAbil!=0 then
call RemoveSavedInteger(STORE,thistype.currentAbil,ALLOCATOR)
endif
set thistype.currentAbil=abilId
if abilId!=0 then
call SaveInteger(STORE,abilId,ALLOCATOR,thistype.create)
endif
endmethod
static method operator abil takes nothing returns integer
return thistype.currentAbil
endmethod
//===========================================================================
// Auto Cleanup.
//
private static boolean doAutoDestroyDefault=true
static method operator autoDestroyDefault= takes boolean flag returns nothing
set thistype.doAutoDestroyDefault=flag
endmethod
static method operator autoDestroyDefault takes nothing returns boolean
return thistype.doAutoDestroyDefault
endmethod
method cleanup takes nothing returns nothing // only runs if not destroyed
call RemoveSavedInteger(STORE,this.abilId,GetHandleId(this.caster))
set this.hasStoppedCasting=true // can't be readonly because of this.
if this.autoDestroy and this.isNotLocked then
call SpellStruct(this).destroy() // Jasshelper bug? Had to typecast "this".
endif
endmethod
//===========================================================================
// Struct Allocation.
//
private static method create takes nothing returns thistype
local thistype this=thistype.allocate()
// General stuff
set this.autoDestroy=thistype.autoDestroyDefault
set this.aoe=thistype.defaultAoE
//static if thistype.onCreate!=DEFAULTS.onCreate then
call this.onCreate.evaluate() // in case of thread terminate
//endif
return this
endmethod
endmodule
//===========================================================================
// Init
//
private struct Init extends array
private static method onInit takes nothing returns nothing
//===========================================================================
// Event Responses
//
local trigger t
//! textmacro SpellStruct__RegisterEvent takes EVENT, FUNCTION
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,$EVENT$)
call TriggerAddCondition(t,Filter(function $FUNCTION$))
//! endtextmacro
//! runtextmacro SpellStruct__RegisterEvent("EVENT_PLAYER_UNIT_SPELL_CHANNEL","OnChannel")
//! runtextmacro SpellStruct__RegisterEvent("EVENT_PLAYER_UNIT_SPELL_CAST","OnStartCast")
//! runtextmacro SpellStruct__RegisterEvent("EVENT_PLAYER_UNIT_SPELL_EFFECT","OnEffect")
//! runtextmacro SpellStruct__RegisterEvent("EVENT_PLAYER_UNIT_SPELL_FINISH","OnFinish")
//! runtextmacro SpellStruct__RegisterEvent("EVENT_PLAYER_UNIT_SPELL_ENDCAST","OnStopCast")
set t=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//
// ___ _ __ __ _ ___ ____ _______________________________
// | \ /_\ / |/ | /_\ / _\| __| || D E A L I T , ||
// | |) / _ \ / / | / |/ _ \| |/|| __| || D E T E C T I T , ||
// |___/_/ \_/_/|__/|_|_/ \_\___/|____| || B L O C K I T . ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.0.5
// What is Damage?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Damage is a damage dealing, detection and blocking system. It implements
// all such functionality. It also provides a means to detect what type
// of damage was dealt, so long as all damage in your map is dealt using
// this system's deal damage functions (except for basic attacks).
//
// It is completely recursively defined, meaning if you deal damage on
// taking damage, the type detection and other features like blocking
// will not malfunction.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Create a new trigger object called Damage, go to 'Edit -> Convert to
// Custom Text', and replace everything that's there with this script.
//
// At the top of the script, there is a '//! external ObjectMerger' line.
// Save your map, close your map, reopen your map, and then comment out this
// line. Damage is now implemented. This line creates a dummy ability used
// in the system in some circumstances with damage blocking.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function Damage_RegisterEvent takes trigger whichTrigger returns nothing
// - This registers a special "any unit takes damage" event.
// - This event supports dynamic trigger use.
// - Only triggers registered on this event may block damage.
// - Only fires when the damage dealt is not 0.
//
// function Damage_RegisterZeroEvent takes trigger whichTrigger returns nothing
// - The same as Damage_RegisterEvent, but for only zero damage events
// (which are excluded from Damage_RegisterEvent).
// - Note that getting the damage type may be unreliable, since spells
// like faerie fire trigger 0 damage, but it will count as an attack.
//
// function Damage_GetType takes nothing returns damagetype
// - This will get the type of damage dealt, like an event response,
// for when using a unit takes damage event (or the special event above).
//
// function Damage_IsPhysical takes nothing returns boolean
// function Damage_IsSpell takes nothing returns boolean
// function Damage_IsPure takes nothing returns boolean
// - Wrappers to simply check if Damage_GetType is certain types.
//
// function Damage_IsAttack takes nothing returns boolean
// - Checks if the damage is from a physical attack (so you can deal
// physical damage without it being registered as an actual attack).
//
// function Damage_Block takes real amount returns nothing
// function Damage_BlockAll takes nothing returns nothing
// - For use only with Damage_RegisterEvent.
// - Blocks 'amount' of the damage dealt.
// - Multiple blocks at once work correctly.
// - Blocking more than 100% of the damage will block 100% instead.
// - Damage_BlockAll blocks 100% of the damage being dealt.
//
// function Damage_EnableEvent takes boolean enable returns nothing
// - For disabling and re-enabling the special event.
// - Use it to deal damage which you do not want to be detected by
// the special event.
//
// function UnitDamageTargetEx takes lots of things returns boolean
// - Replaces UnitDamageTarget in your map, with the same arguments.
//
// function Damage_Physical takes unit source, unit target, real amount,
// attacktype whichType, boolean attack, boolean ranged returns boolean
// - A clean wrapper for physical damage.
// - 'attack' determines if this is to be treated as a real physical
// attack or just physical type damage.
// - 'ranged' determines if this is to be treated as a ranged or melee
// attack.
//
// function Damage_Spell takes unit source, unit target, real amount returns boolean
// - A clean wrapper for spell damage.
//
// function Damage_Pure takes unit source, unit target, real amount returns boolean
// - A clean wrapper for pure type damage (universal type, 100% damage).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for helping me find a better way to think about damage blocking.
//
library Damage uses AIDS, Event
//============================================================
//! external ObjectMerger w3a AIlz dprv anam "Life Bonus" ansf "(Damage System)" Ilif 1 500000 aite 0
globals
private constant integer LIFE_BONUS_ABIL='dprv'
endglobals
//============================================================
globals
private Event OnDamageEvent
private Event OnZeroDamageEvent
private boolean EventEnabled=true
endglobals
public function RegisterEvent takes trigger whichTrigger returns nothing
call OnDamageEvent.register(whichTrigger)
endfunction
public function RegisterZeroEvent takes trigger whichTrigger returns nothing
call OnZeroDamageEvent.register(whichTrigger)
endfunction
public function EnableEvent takes boolean enable returns nothing
set EventEnabled=enable
endfunction
//============================================================
globals
private integer TypeStackLevel=0
private damagetype array TypeStackValue
private boolean array TypeStackAttack
private real array ToBlock
endglobals
public function GetType takes nothing returns damagetype
return TypeStackValue[TypeStackLevel]
endfunction
public function IsAttack takes nothing returns boolean
return TypeStackAttack[TypeStackLevel]
endfunction
public function Block takes real amount returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+amount
endfunction
public function BlockAll takes nothing returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+GetEventDamage()
endfunction
//============================================================
globals
private integer BlockNum=0
private unit array BlockUnit
private real array BlockUnitLife
private real array BlockRedamage
private unit array BlockDamageSource
private timer BlockTimer=CreateTimer()
endglobals
//============================================================
globals
private unit array RemoveBoosted
private integer RemoveBoostedMax=0
private timer RemoveBoostedTimer=CreateTimer()
endglobals
globals//locals
private real BoostedLifeTemp
private unit BoostedLifeUnit
endglobals
private function RemoveBoostedTimerFunc takes nothing returns nothing
loop
exitwhen RemoveBoostedMax==0
set BoostedLifeUnit=RemoveBoosted[RemoveBoostedMax]
set BoostedLifeTemp=GetWidgetLife(BoostedLifeUnit)
call UnitRemoveAbility(BoostedLifeUnit,LIFE_BONUS_ABIL)
if BoostedLifeTemp>0.405 then
call SetWidgetLife(BoostedLifeUnit,BoostedLifeTemp)
endif
set RemoveBoostedMax=RemoveBoostedMax-1
endloop
endfunction
//============================================================
private keyword Detector // Darn, I actually had to do this. XD
globals//locals
private unit ForUnit
private real NextHealth
endglobals
private function OnDamageActions takes nothing returns boolean
if EventEnabled then
if GetEventDamage()==0. then
call OnZeroDamageEvent.fire()
else
call OnDamageEvent.fire()
endif
if ToBlock[TypeStackLevel]!=0. then
//====================================================
// Blocking
set ForUnit=GetTriggerUnit()
set NextHealth=GetEventDamage()
if ToBlock[TypeStackLevel]>=NextHealth then
set NextHealth=GetWidgetLife(ForUnit)+NextHealth
else
set NextHealth=GetWidgetLife(ForUnit)+ToBlock[TypeStackLevel]
endif
call SetWidgetLife(ForUnit,NextHealth)
if GetWidgetLife(ForUnit)<NextHealth then
// NextHealth is over max health.
call UnitAddAbility(ForUnit,LIFE_BONUS_ABIL)
call SetWidgetLife(ForUnit,NextHealth)
set RemoveBoostedMax=RemoveBoostedMax+1
set RemoveBoosted[RemoveBoostedMax]=ForUnit
call ResumeTimer(RemoveBoostedTimer)
endif
//====================================================
set ToBlock[TypeStackLevel]=0.
endif
endif
return false
endfunction
//============================================================
function UnitDamageTargetEx takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
local boolean result
set TypeStackLevel=TypeStackLevel+1
set TypeStackValue[TypeStackLevel]=damageType
set TypeStackAttack[TypeStackLevel]=attack
set result=UnitDamageTarget(whichUnit,target,amount,attack,ranged,attackType,damageType,weaponType)
set TypeStackLevel=TypeStackLevel-1
return result
endfunction
//! textmacro Damage__DealTypeFunc takes NAME, TYPE
public function $NAME$ takes unit source, unit target, real amount returns boolean
return UnitDamageTargetEx(source,target,amount,false,false,ATTACK_TYPE_NORMAL,$TYPE$,WEAPON_TYPE_WHOKNOWS)
endfunction
public function Is$NAME$ takes nothing returns boolean
return GetType()==$TYPE$
endfunction
//! endtextmacro
//! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
//! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
// Uses different stuff, but works much the same way.
public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
return UnitDamageTargetEx(source,target,amount,attack,ranged,whichType,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
endfunction
public function IsPhysical takes nothing returns boolean
return GetType()==DAMAGE_TYPE_NORMAL
endfunction
//============================================================
private struct Detector extends array // Uses AIDS.
//! runtextmacro AIDS()
private static conditionfunc ACTIONS_COND
private trigger t
private method AIDS_onCreate takes nothing returns nothing
set this.t=CreateTrigger()
call TriggerAddCondition(this.t,thistype.ACTIONS_COND)
call TriggerRegisterUnitEvent(this.t,this.unit,EVENT_UNIT_DAMAGED)
endmethod
private method AIDS_onDestroy takes nothing returns nothing
call DestroyTrigger(this.t)
endmethod
private static method AIDS_onInit takes nothing returns nothing
set thistype.ACTIONS_COND=Condition(function OnDamageActions)
endmethod
endstruct
//============================================================
private module InitModule
private static method onInit takes nothing returns nothing
local unit abilpreload=CreateUnit(Player(15),'uloc',0,0,0)
call UnitAddAbility(abilpreload,LIFE_BONUS_ABIL)
call RemoveUnit(abilpreload)
set abilpreload=null
set OnDamageEvent=Event.create()
set OnZeroDamageEvent=Event.create()
set TypeStackValue[TypeStackLevel]=DAMAGE_TYPE_NORMAL
set TypeStackAttack[TypeStackLevel]=true
call TimerStart(RemoveBoostedTimer,0.0,false,function RemoveBoostedTimerFunc)
endmethod
endmodule
private struct InitStruct extends array
implement InitModule
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//
// _ ___ ___ ___ _______________________________________________
// /_\ |_ _| \/ __| || A D V A N C E D I N D E X I N G ||
// / _ \ | || |) \__ \ || A N D ||
// /_/ \_\___|___/|___/ || D A T A S T O R A G E ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.1.0
// What is AIDS?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// AIDS assigns unique integers between 1 and 8191 to units which enter
// the map. These can be used for arrays and data attaching.
//
// AIDS also allows you to define structs which are created automatically
// when units enter the map, and filtering which units should be indexed
// as well as for which units these structs should be created.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called AIDS, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// Save the map, close it, reopen it, and then delete the "!" from the
// FAR left side of the next lines (so "external" will line up with this line):
//! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
// At the top of the script, there is a 'UnitIndexingFilter' constant
// function. If the function returns true for the unit, then that unit
// will be automatically indexed. Setting this to true will automatically
// index all units. Setting it to false will disable automatic indexing.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function GetUnitId takes unit u returns integer
// - This returns the index of an indexed unit. This will return 0
// if the unit has not been indexed.
// - This function inlines. It does not check if the unit needs an
// index. This function is for the speed freaks.
// - Always use this if 'UnitIndexingFilter' simply returns true.
//
// function GetUnitIndex takes unit u returns integer
// - This will return the index of a unit if it has one, or assign
// an index if the unit doesn't have one (and return the new index).
// - Use this if 'UnitIndexingFilter' doesn't return true.
//
// function GetIndexUnit takes integer index returns unit
// - This returns the unit which has been assigned the 'index'.
//
// AIDS Structs:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Insert: //! runtextmacro AIDS() at the top of a struct to make it
// an AIDS struct.
// - AIDS structs cannot be created or destroyed manually. Instead, they
// are automatically created when an appropriate unit enters the map.
// - You cannot give members default values in their declaration.
// (eg: private integer i=5 is not allowed)
// - You cannot use array members.
// - AIDS structs must "extend array". This will remove some unused
// functions and enforce the above so there will be no mistakes.
// - There are four optional methods you can use in AIDS structs:
// - AIDS_onCreate takes nothing returns nothing
// - This is called when the struct is 'created' for the unit.
// - In here you can assign members their default values, which
// you would usually assign in their declarations.
// (eg: set this.i=5)
// - AIDS_onDestroy takes nothing returns nothing
// - This is called when the struct is 'destroyed' for the unit.
// - This is your substitute to the normal onDestroy method.
// - AIDS_filter takes unit u returns boolean
// - This is similar to the constant filter in the main system.
// - Each unit that enters the map will be tested by each AIDS
// struct filter. If it returns true for that unit, that unit
// will be indexed if it was not already, the AIDS struct will
// have its AIDS_onCreate method called, and later have its
// AIDS_onDestroy method called when the index is recycled.
// - Not declaring this will use the default AIDS filter instead.
// - AIDS_onInit takes nothing returns nothing
// - This is because I stole your onInit function with my textmacro.
// - You can use '.unit' from any AIDS struct to get the unit for which
// the struct is for.
// - The structs id will be the units index, so getting the struct for
// a unit inlines to a single native call, and you can typecast between
// different AIDS structs. This is the premise of AIDS.
// - Never create or destroy AIDS structs directly.
// - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
// decrease the lock level on the struct. If a struct's lock level is
// not 0, it will not be destroyed until it is reduced to 0. Locks just
// put off AIDS struct destruction in case you wish to attach to a timer
// or something which must expire before the struct data disappears.
// Hence, not freeing all locks will leak the struct (and index).
//
// PUI and AutoIndex:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - AIDS includes the PUI textmacros and the AutoIndex module, because
// these systems are not compatible with AIDS but have valid and distinct
// uses.
// - The PUI textmacros are better to use for spells than AIDS structs,
// because they are not created for all units, just those targetted by
// the spell (or whatever else is necessary).
// - The AutoData module is good for very simple array syntax for data
// attachment (although I don't recommend that people actually use it,
// it's here mostly for compatability). Note that unlike the PUI textmacros,
// units must pass the AIDS filter in order for this module to work with
// them. This is exactly as the same as in AutoIndex itself (AutoIndex
// has a filter too).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for writing 90% of this user documentation, challenging my
// interface, doing some testing, suggesting improvements and inspiring
// me to re-do my code to include GetUnitIndex as non-inlining.
// - grim001, for writing the AutoData module, and AutoIndex. I used the
// on-enter-map method that he used. Full credits for the AutoData module.
// - Cohadar, for writing his PUI textmacros. Full credits to him for these,
// except for my slight optimisations for this system.
// Also, I have used an optimised version of his PeriodicRecycler from
// PUI in this system to avoid needing a RemoveUnitEx function.
// - Vexorian, for helping Cohadar on the PUI textmacro.
// - Larcenist, for suggesting the AIDS acronym. Originally he suggested
// 'Alternative Index Detection System', but obviously I came up with
// something better. In fact, I'd say it looks like the acronym was
// an accident. Kinda neat, don't you think? :P
//
// Final Notes:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - With most systems, I wouldn't usually add substitutes for alternative
// systems. However, UnitData systems are an exception, because they
// are incompatible with eachother. Since using this system forbids
// people from using the real PUI or AutoIndex, and a lot of resources
// use either of these, it made sense not to break them all.
//
// - If this documentation confused you as to how to use the system, just
// leave everything as default and use GetUnitId everywhere.
//
// - To use this like PUI (if you don't like spamming indices) simply
// make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
//==============================================================================
// Configurables
//
globals
private constant boolean USE_PERIODIC_RECYCLER = false
private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
// Lower to be able to recycle faster.
// Only used if USE_PERIODIC_RECYCLER
// is set to true.
private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
endglobals
private function UnitIndexingFilter takes unit u returns boolean
return true
endfunction
//==============================================================================
// System code
//
globals
// The unit stored at an index.
private unit array IndexUnit
private integer array LockLevel
endglobals
//==============================================================================
globals
// Recycle stack
private integer array RecycledIndex
private integer MaxRecycledIndex = 0
// Previous highest index
private integer MaxIndex = 0
endglobals
//==============================================================================
globals
private integer array DecayingIndex
private integer MaxDecayingIndex=0
private integer DecayChecker=0
endglobals
globals
private timer UndefendTimer=CreateTimer()
private integer array UndefendIndex
private integer UndefendStackIndex=0
endglobals
globals
private integer array UndefendExpiringIndex
private integer UndefendExpiringIndexLevel=0
endglobals
//==============================================================================
globals
// The Add/Remove stack (or assign/recycle stack).
//
// Indexing can become recusive since units can be created on index
// assignment or deallocation.
// To support this, a stack is used to store the event response results.
private integer ARStackLevel=0
private integer array ARStackIndex
private unit array ARStackUnit
// A later discovery revealed that the Add/Remove stack did not need to be
// used for deallocation. The alternative used works fine...
endglobals
public constant function GetEnteringIndexUnit takes nothing returns unit
return ARStackUnit[ARStackLevel]
endfunction
public function GetIndexOfEnteringUnit takes nothing returns integer
// Called in AIDS structs when units do not pass the initial AIDS filter.
if ARStackIndex[ARStackLevel]==0 then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
endif
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
// Called in AIDS structs when units have passed the initial AIDS filter.
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetDecayingIndex takes nothing returns integer
static if USE_PERIODIC_RECYCLER then
return DecayingIndex[DecayChecker]
else
return UndefendExpiringIndex[UndefendExpiringIndexLevel]
endif
endfunction
//==============================================================================
globals
// For structs and such which need to do things on unit index assignment.
private trigger OnEnter=CreateTrigger()
// The same, but for when units pass the initial filter anyway.
private trigger OnEnterAllocated=CreateTrigger()
// For structs and such which need to do things on unit index deallocation.
private trigger OnDeallocate=CreateTrigger()
endglobals
public function RegisterOnEnter takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnter,b)
endfunction
public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnterAllocated,b)
endfunction
public function RegisterOnDeallocate takes boolexpr b returns triggercondition
return TriggerAddCondition(OnDeallocate,b)
endfunction
//==============================================================================
function GetIndexUnit takes integer index returns unit
debug if index==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
debug elseif IndexUnit[index]==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
debug endif
return IndexUnit[index]
endfunction
function GetUnitId takes unit u returns integer
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
debug elseif GetUnitUserData(u)==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
debug endif
return GetUnitUserData(u)
endfunction
globals//locals
private integer getindex
endglobals
function GetUnitIndex takes unit u returns integer // Cannot be recursive.
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
debug endif
set getindex=GetUnitUserData(u)
if getindex==0 then
// Get new index, from recycler first, else new.
// Store the current index in getindex.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set getindex=MaxIndex
else // Get from recycle stack.
set getindex=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(u,getindex)
set IndexUnit[getindex]=u
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=getindex
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Do not fire things here. No AIDS structs will be made at this point.
endif
return getindex
endfunction
//==============================================================================
public function AddLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]+1
endfunction
public function RemoveLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]-1
static if not USE_PERIODIC_RECYCLER then
if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
// Increment stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Decrement stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=index
// Null the unit.
set IndexUnit[index]=null
endif
endif
endfunction
//==============================================================================
static if USE_PERIODIC_RECYCLER then
private function PeriodicRecycler takes nothing returns nothing
if MaxDecayingIndex>0 then
set DecayChecker=DecayChecker+1
if DecayChecker>MaxDecayingIndex then
set DecayChecker=1
endif
if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
if LockLevel[DecayingIndex[DecayChecker]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
// Null the unit.
set IndexUnit[DecayingIndex[DecayChecker]]=null
// Remove index from decay list.
set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
set MaxDecayingIndex=MaxDecayingIndex-1
endif
endif
endif
endfunction
else
private function UndefendFilter takes nothing returns boolean
return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
endfunction
private function OnUndefendTimer takes nothing returns nothing
loop
exitwhen UndefendStackIndex==0
set UndefendStackIndex=UndefendStackIndex-1
set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
if IndexUnit[UndefendExpiringIndex[0]]!=null then
if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
if LockLevel[UndefendExpiringIndex[0]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
// Null the unit.
set IndexUnit[UndefendExpiringIndex[0]]=null
endif
endif
endif
endloop
endfunction
globals//locals
private integer UndefendFilterIndex
endglobals
private function OnUndefend takes nothing returns boolean
if GetIssuedOrderId()==852056 then // If undefend then...
set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
set UndefendStackIndex=UndefendStackIndex+1
call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
endif
endif
return false
endfunction
endif
//==============================================================================
public function IndexEnum takes nothing returns boolean // Can be recursive...
// Start by adding another level on the AR stack (for recursion's sake).
set ARStackLevel=ARStackLevel+1
// Store the current unit on the (new) top level of the AR stack.
set ARStackUnit[ARStackLevel]=GetFilterUnit()
if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Fire things.
call TriggerEvaluate(OnEnter)
else
// The unit did not pass the filters, so does not need to be auto indexed.
// However, for certain AIDS structs, it may still require indexing.
// These structs may index the unit on their creation.
// We flag that an index must be assigned by setting the current index to 0.
set ARStackIndex[ARStackLevel]=0
// Fire things.
call TriggerEvaluate(OnEnter)
endif
endif
// Decrement the stack.
set ARStackLevel=ARStackLevel-1
return false
endfunction
//==============================================================================
private function InitAIDS takes nothing returns nothing
local region r=CreateRegion()
local group g=CreateGroup()
local integer n=15
static if USE_PERIODIC_RECYCLER then
call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
else
local trigger t=CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
// Capture "undefend" orders.
exitwhen n==0
set n=n-1
endloop
set n=15
call TriggerAddCondition(t,Filter(function OnUndefend))
set t=null
endif
// This must be done first, due to recursion. :)
call RegionAddRect(r,GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
set r=null
loop
call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
exitwhen n==0
set n=n-1
endloop
call DestroyGroup(g)
set g=null
endfunction
//==============================================================================
public struct DEFAULT extends array
method AIDS_onCreate takes nothing returns nothing
endmethod
method AIDS_onDestroy takes nothing returns nothing
endmethod
static method AIDS_filter takes unit u returns boolean
return UnitIndexingFilter(u)
endmethod
static method AIDS_onInit takes nothing returns nothing
endmethod
endstruct
//===========================================================================
// Never create or destroy AIDS structs directly.
// Also, do not initialise members except by using the AIDS_onCreate method.
//===========================================================================
//! textmacro AIDS
// This magic line makes default methods get called which do nothing
// if the methods are otherwise undefined.
private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
//-----------------------------------------------------------------------
// Gotta know whether or not to destroy on deallocation...
private boolean AIDS_instanciated
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator unit takes nothing returns unit
// Allows structVar.unit to return the unit.
return GetIndexUnit(this)
endmethod
//-----------------------------------------------------------------------
method AIDS_addLock takes nothing returns nothing
call AIDS_AddLock(this)
endmethod
method AIDS_removeLock takes nothing returns nothing
call AIDS_RemoveLock(this)
endmethod
//-----------------------------------------------------------------------
private static method AIDS_onEnter takes nothing returns boolean
// At this point, the unit might not have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation.
set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onEnterAllocated takes nothing returns boolean
// At this point, the unit must have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation. Slightly faster!
set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onDeallocate takes nothing returns boolean
if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
// Unflag destruction on deallocation.
set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
endif
return false
endmethod
//-----------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
// Because I robbed you of your struct's onInit method.
call thistype.AIDS_onInit()
endmethod
//! endtextmacro
endlibrary
library PUI uses AIDS
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
// Switched the next two lines for optimisation.
if .pui_unit[pui] != whichUnit then
if .pui_data[pui] != 0 then
// recycled index detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
endlibrary
library AutoIndex uses AIDS
module AutoData
private static thistype array data
// Fixed up the below to use thsitype instead of integer.
static method operator []= takes unit u, thistype i returns nothing
set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
endmethod //using the module's thistype array.
static method operator [] takes unit u returns thistype
return .data[GetUnitId(u)] //Just returning the attached struct.
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~ Event ~~ By Jesus4Lyf ~~ Version 1.04 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Event?
// - Event simulates Warcraft III events. They can be created,
// registered for, fired and also destroyed.
// - Event, therefore, can also be used like a trigger "group".
// - This was created when there was an influx of event style systems
// emerging that could really benefit from a standardised custom
// events snippet. Many users were trying to achieve the same thing
// and making the same kind of errors. This snippet aims to solve that.
//
// Functions:
// - Event.create() --> Creates a new Event.
// - .destroy() --> Destroys an Event.
// - .fire() --> Fires all triggers which have been
// registered on this Event.
// - .register(trigger) --> Registers another trigger on this Event.
// - .unregister(trigger) --> Unregisters a trigger from this Event.
//
// Details:
// - Event is extremely efficient and lightweight.
// - It is safe to use with dynamic triggers.
// - Internally, it is just a linked list. Very simple.
//
// How to import:
// - Create a trigger named Event.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Builder Bob for the trigger destroy detection method.
// - Azlier for inspiring this by ripping off my dodgier code.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Event
///////////////
// EventRegs //
////////////////////////////////////////////////////////////////////////////
// For reading this far, you can learn one thing more.
// Unlike normal Warcraft III events, you can attach to Event registries.
//
// Event Registries are registrations of one trigger on one event.
// These cannot be created or destroyed, just attached to.
//
// It is VERY efficient for loading and saving data.
//
// Functions:
// - set eventReg.data = someStruct --> Store data.
// - eventReg.data --> Retreive data.
// - Event.getTriggeringEventReg() --> Get the triggering EventReg.
// - eventReg.destroy() --> Undo this registration.
//
private keyword destroyNode
struct EventReg extends array
integer data
method clear takes nothing returns nothing
set this.data=0
endmethod
method destroy takes nothing returns nothing
call Event(this).destroyNode()
endmethod
endstruct
private module Stack
static thistype top=0
static method increment takes nothing returns nothing
set thistype.top=thistype(thistype.top+1)
endmethod
static method decrement takes nothing returns nothing
set thistype.top=thistype(thistype.top-1)
endmethod
endmodule
private struct EventStack extends array
implement Stack
Event current
endstruct
struct Event
private trigger trig
private thistype next
private thistype prev
static method getTriggeringEventReg takes nothing returns EventReg
return EventStack.top.current
endmethod
static method create takes nothing returns Event
local Event this=Event.allocate()
set this.next=this
set this.prev=this
return this
endmethod
private static trigger currentTrigger
method fire takes nothing returns nothing
local thistype curr=this.next
call EventStack.increment()
loop
exitwhen curr==this
set thistype.currentTrigger=curr.trig
if IsTriggerEnabled(thistype.currentTrigger) then
set EventStack.top.current=curr
if TriggerEvaluate(thistype.currentTrigger) then
call TriggerExecute(thistype.currentTrigger)
endif
else
call EnableTrigger(thistype.currentTrigger) // Was trigger destroyed?
if IsTriggerEnabled(thistype.currentTrigger) then
call DisableTrigger(thistype.currentTrigger)
else // If trigger destroyed...
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
endif
endif
set curr=curr.next
endloop
call EventStack.decrement()
endmethod
method register takes trigger t returns EventReg
local Event new=Event.allocate()
set new.prev=this.prev
set this.prev.next=new
set this.prev=new
set new.next=this
set new.trig=t
call EventReg(new).clear()
return new
endmethod
method destroyNode takes nothing returns nothing // called on EventReg
set this.prev.next=this.next
set this.next.prev=this.prev
call this.deallocate()
endmethod
method unregister takes trigger t returns nothing
local thistype curr=this.next
loop
exitwhen curr==this
if curr.trig==t then
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
return
endif
set curr=curr.next
endloop
endmethod
method destroy takes nothing returns nothing
local thistype curr=this.next
loop
call curr.deallocate()
exitwhen curr==this
set curr=curr.next
endloop
endmethod
method chainDestroy takes nothing returns nothing
call this.destroy() // backwards compatability.
endmethod
endstruct
/////////////////////////////////////////////////////
// Demonstration Functions & Alternative Interface //
////////////////////////////////////////////////////////////////////////////
// What this would look like in normal WC3 style JASS (should all inline).
//
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function DestroyEvent takes Event whichEvent returns nothing
call whichEvent.chainDestroy()
endfunction
function FireEvent takes Event whichEvent returns nothing
call whichEvent.fire()
endfunction
function TriggerRegisterEvent takes trigger whichTrigger, Event whichEvent returns EventReg
return whichEvent.register(whichTrigger)
endfunction
// And for EventRegs...
function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
set whichEventReg.data=data
endfunction
function GetEventRegData takes EventReg whichEventReg returns integer
return whichEventReg.data
endfunction
function GetTriggeringEventReg takes nothing returns integer
return Event.getTriggeringEventReg()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=92
//TESH.alwaysfold=0
// Death Coil \\
// by: Carnerox \\
// credits to: \\
//Jesus4Lyf for SpellStruct, Damage, AIDS, and Event\\
// How to use import this spell:
// 1) Copy this trigger
// 2) Open your map, create a new trigger with the name "Death Coil".
// 3) Click Edit, and convert the trigger to custom text.
// 4) Copy the trigger, and paste it into the trigger.
// 5) Add all the systems that the spell uses.
// 6) Change the ABIL_ID, and DUMMY_ID to the correct rawcodes, and customize the spell to what you want.
scope DeathCoil
////////////////////////////////CONFIGURATION DATA!\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
globals
private constant integer ABIL_ID = 'A000' // Rawcode of the ability.
private constant integer DUMMY_ID = 'h000' // Rawcode of the obelisk.
private constant real PERIODIC = 0.01 // How often the timer will run the function.
private constant real COLOR_R = 100.00 // Lightning red
private constant real COLOR_G = 0.00 // Lightning green
private constant real COLOR_B = 0.00 // Lightning blue
private constant real COLOR_A = 10.00 // Lightning alpha
private constant real FLOAT = 45.00 // Lightning height
private constant string LIGHTNING_ID = "MFPB" // Lightning type - Mana Flare
private constant string EFFECT = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //Effect when link snaps
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL //Attack type dealt
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC //Damage type dealt
private constant boolean DAMAGE_MELEE = true //Is the damage melee?
private constant boolean DAMAGE_RANGED = false //Is the damage ranged?
private constant boolean SHARED_VISION = true //Is there shared vision of the Target?
endglobals
///////////////////// Do not change anything in this struct!\\\\\\\\\\\\\\\\\
private struct DeathCoil extends SpellStruct
unit Dummy
lightning Lightning
real x
real y
real x1
real x2
real y1
real y2
real z1
real z2
implement SpellStruct
//How much damage is dealt to the Target
method operator Damage takes nothing returns real
return 50.00*.level+25.00
endmethod
//Distance of how far the unit can move away from the Obelisk
method operator Distance takes nothing returns real
return 100*.level+400.00
endmethod
//Duration of the Obelisk
method operator Duration takes nothing returns real
return 5.00*.level+10.00
endmethod
//The z of the Obelisk, and Target
method GetUnitZ takes unit u returns real
local location Point=Location(GetUnitX(u),GetUnitY(u))
local real Real=GetLocationZ(Point)+GetUnitFlyHeight(u)
call RemoveLocation(Point)
set Point=null
return Real
endmethod
///////////////////////////END OF CONFIGURATION DATA!\\\\\\\\\\\\\\\\\\\\\\\
method periodic takes nothing returns nothing
set .x2=GetUnitX(.targetUnit)
set .y2=GetUnitY(.targetUnit)
set .z2=GetUnitZ(.targetUnit)+FLOAT
set .x=.x2-.x1
set .y=.y2-.y1
call MoveLightningEx(.Lightning,true,.x1,.y1,.z1,.x2,.y2,.z2)
if (SquareRoot(.x*.x+.y*.y)>=this.Distance) then
call .stopTimer(.periodic)
call DestroyLightning(.Lightning)
call DestroyEffect(AddSpecialEffect(EFFECT,.x1,.y1))
call DestroyEffect(AddSpecialEffect(EFFECT,.x2,.y2))
call RemoveUnit(.Dummy)
call SetUnitX(.targetUnit,.x1)
call SetUnitY(.targetUnit,.y1)
static if (LIBRARY_Damage) then
call Damage_Spell(.caster,.targetUnit,this.Damage)
else
call UnitDamageTarget(.caster,.targetUnit,this.Damage,DAMAGE_MELEE,DAMAGE_RANGED,ATTACK_TYPE,DAMAGE_TYPE,null)
endif
if (SHARED_VISION==true) then
call UnitShareVision(.targetUnit,.owner,false)
endif
set .Dummy=null
set .Lightning=null
elseif (IsUnitType(.Dummy,UNIT_TYPE_DEAD)==true) or (IsUnitType(.targetUnit,UNIT_TYPE_DEAD)==true) then
call .stopTimer(.periodic)
call DestroyLightning(.Lightning)
call DestroyEffect(AddSpecialEffect(EFFECT,.x1,.y1))
call RemoveUnit(.Dummy)
if (SHARED_VISION==true) then
call UnitShareVision(.targetUnit,.owner,false)
endif
set .Dummy=null
set .Lightning=null
endif
endmethod
method onEffect takes nothing returns nothing
set .x2=GetUnitX(.targetUnit)
set .y2=GetUnitY(.targetUnit)
set .z2=GetUnitZ(.targetUnit)+FLOAT
set .Dummy=CreateUnit(.owner,DUMMY_ID,.x2,.y2,0.00)
set x1=GetUnitX(.Dummy)
set y1=GetUnitY(.Dummy)
set .z1=GetUnitZ(.Dummy)+FLOAT
set .Lightning=AddLightningEx(LIGHTNING_ID,true,.x1,.y1,.z1,.x2,.y2,.z2)
call UnitApplyTimedLife(.Dummy,'BTLF',this.Duration)
call SetLightningColor(.Lightning,COLOR_R,COLOR_G,COLOR_B,COLOR_A)
if (SHARED_VISION==true) then
call UnitShareVision(.targetUnit,.owner,true)
endif
call .startTimer(.periodic,PERIODIC)
endmethod
private static method onInit takes nothing returns nothing
set thistype.abil=ABIL_ID
endmethod
endstruct
endscope
//TESH.scrollpos=17
//TESH.alwaysfold=0
////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Death Coil \\
// by: Carnerox \\
// credits to: \\
//Jesus4Lyf for Damage, AIDS, and Event\\
// How to use import this spell:
// 1) Copy this trigger
// 2) Open your map, create a new trigger with the name "Death Coil".
// 3) Click Edit, and convert the trigger to custom text.
// 4) Copy the trigger, and paste it into the trigger.
// 5) Add all the systems that the spell uses.
// 6) Change the ABIL_ID, and DUMMY_ID to the correct rawcodes, and customize the spell to what you want.
////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
scope DeathCoil
////////////////////////////////CONFIGURATION DATA!\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
globals
private constant integer ABIL_ID = 'A000' // Rawcode of the ability.
private constant integer DUMMY_ID = 'h000' // Rawcode of the obelisk.
private constant real PERIODIC = 0.01 // How often the timer will run the function.
private constant real COLOR_R = 100.00 // Lightning red
private constant real COLOR_G = 0.00 // Lightning green
private constant real COLOR_B = 0.00 // Lightning blue
private constant real COLOR_A = 10.00 // Lightning alpha
private constant real FLOAT = 45.00 // Lightning height
private constant string LIGHTNING_ID = "MFPB" // Lightning type - Mana Flare
private constant string EFFECT = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //Effect when link snaps
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL //Attack type dealt
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC //Damage type dealt
private constant boolean DAMAGE_MELEE = true //Is the damage melee?
private constant boolean DAMAGE_RANGED = false //Is the damage ranged?
private constant boolean SHARED_VISION = true //Is there shared vision of the Target?
endglobals
///////////////////// Do not change anything in this struct!\\\\\\\\\\\\\\\\\
private struct DeathCoil
static unit Caster
static unit Target
static unit Dummy
static player Owner
static lightning Lightning
static timer Timer=CreateTimer()
static integer Level
static real x
static real y
static real x1
static real x2
static real y1
static real y2
static real z1
static real z2
//How much damage is dealt to the Target
private static method Damage takes integer level returns real
return 50.00*level+25.00
endmethod
//Distance of how far the unit can move away from the Obelisk
private static method Distance takes integer level returns real
return 100*level+400.00
endmethod
//Duration of the Obelisk
private static method Duration takes integer level returns real
return 5.00*level+10.00
endmethod
//The z of the Obelisk, and Target
private static method GetUnitZ takes unit u returns real
local location Point=Location(GetUnitX(u),GetUnitY(u))
local real Real=GetLocationZ(Point)+GetUnitFlyHeight(u)
call RemoveLocation(Point)
set Point=null
return Real
endmethod
///////////////////////////END OF CONFIGURATION DATA!\\\\\\\\\\\\\\\\\\\\\\\
private static method periodic takes nothing returns nothing
set DeathCoil.x2=GetUnitX(DeathCoil.Target)
set DeathCoil.y2=GetUnitY(DeathCoil.Target)
set DeathCoil.z2=DeathCoil.GetUnitZ(DeathCoil.Target)+FLOAT
set DeathCoil.x=DeathCoil.x2-DeathCoil.x1
set DeathCoil.y=DeathCoil.y2-DeathCoil.y1
call MoveLightningEx(DeathCoil.Lightning,true,DeathCoil.x1,DeathCoil.y1,DeathCoil.z1,DeathCoil.x2,DeathCoil.y2,DeathCoil.z2)
if (SquareRoot(DeathCoil.x*DeathCoil.x+DeathCoil.y*DeathCoil.y)>=DeathCoil.Distance(DeathCoil.Level)) then
call PauseTimer(DeathCoil.Timer)
call DestroyTimer(DeathCoil.Timer)
call DestroyLightning(DeathCoil.Lightning)
call DestroyEffect(AddSpecialEffect(EFFECT,DeathCoil.x1,DeathCoil.y1))
call DestroyEffect(AddSpecialEffect(EFFECT,DeathCoil.x2,DeathCoil.y2))
call RemoveUnit(DeathCoil.Dummy)
call SetUnitX(DeathCoil.Target,DeathCoil.x1)
call SetUnitY(DeathCoil.Target,DeathCoil.y1)
static if (LIBRARY_Damage) then
call Damage_Spell(DeathCoil.Caster,DeathCoil.Target,DeathCoil.Damage(DeathCoil.Level))
else
call UnitDamageTarget(DeathCoil.Caster,DeathCoil.Target,DeathCoil.Damage(DeathCoil.Level),DAMAGE_MELEE,DAMAGE_RANGED,ATTACK_TYPE,DAMAGE_TYPE,null)
endif
if (SHARED_VISION==true) then
call UnitShareVision(DeathCoil.Target,DeathCoil.Owner,false)
endif
set DeathCoil.Caster=null
set DeathCoil.Target=null
set DeathCoil.Dummy=null
set DeathCoil.Lightning=null
set DeathCoil.Timer=null
elseif (IsUnitType(DeathCoil.Dummy,UNIT_TYPE_DEAD)==true) or (IsUnitType(DeathCoil.Target,UNIT_TYPE_DEAD)==true) then
call PauseTimer(DeathCoil.Timer)
call DestroyTimer(DeathCoil.Timer)
call DestroyLightning(DeathCoil.Lightning)
call DestroyEffect(AddSpecialEffect(EFFECT,DeathCoil.x1,DeathCoil.y1))
call RemoveUnit(DeathCoil.Dummy)
if (SHARED_VISION==true) then
call UnitShareVision(DeathCoil.Target,DeathCoil.Owner,false)
endif
set DeathCoil.Caster=null
set DeathCoil.Target=null
set DeathCoil.Dummy=null
set DeathCoil.Lightning=null
set DeathCoil.Timer=null
endif
endmethod
static method onCreate takes nothing returns nothing
local thistype dc=thistype.allocate()
set dc.Caster=GetTriggerUnit()
set dc.Target=GetSpellTargetUnit()
set dc.Owner=GetTriggerPlayer()
set dc.Level=GetUnitAbilityLevel(dc.Caster,ABIL_ID)
set dc.x2=GetUnitX(dc.Target)
set dc.y2=GetUnitY(dc.Target)
set dc.z2=GetUnitZ(dc.Target)+FLOAT
set dc.Dummy=CreateUnit(dc.Owner,DUMMY_ID,dc.x2,dc.y2,0.00)
set dc.x1=GetUnitX(dc.Dummy)
set dc.y1=GetUnitY(dc.Dummy)
set dc.z1=GetUnitZ(dc.Dummy)+FLOAT
set dc.Lightning=AddLightningEx(LIGHTNING_ID,true,dc.x1,dc.y1,dc.z1,dc.x2,dc.y2,dc.z2)
call UnitApplyTimedLife(dc.Dummy,'BTLF',DeathCoil.Duration(dc.Level))
call SetLightningColor(dc.Lightning,COLOR_R,COLOR_G,COLOR_B,COLOR_A)
if (SHARED_VISION==true) then
call UnitShareVision(dc.Target,dc.Owner,true)
endif
call TimerStart(dc.Timer,PERIODIC,true,function DeathCoil.periodic)
endmethod
endstruct
private function onCast takes nothing returns boolean
if (GetSpellAbilityId()==ABIL_ID) then
call DeathCoil.onCreate()
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger trig=CreateTrigger()
local integer index=0
loop
exitwhen index == bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(trig,Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set index=index+1
endloop
call TriggerAddCondition(trig,Condition(function onCast))
set trig=null
endfunction
endscope