Nestharus
o-o
- Reaction score
- 84
This is not compatible with Warcraft 3 Standard Aura Abilities. If you use any aura abilities at all, this will break.
You may only use auras that have None as the valid unit targets (no valid targets), which is what I use in the Demo Map.
You may only use auras that have None as the valid unit targets (no valid targets), which is what I use in the Demo Map.
Warcraft 3 auras break UnitInRangeEvent
Units that aren't indexed and units with locust will not fire.
JASS:
library AuraStruct /* v2.1.3.0
*************************************************************************************
*
* An efficient and easy to use module for fully custom aura design.
*
*************************************************************************************
*
* */uses/*
*
* */ UnitEvent /* hiveworkshop.com/forums/jass-functions-413/extension-unit-event-172365/
* */ UnitInRangeEvent /* hiveworkshop.com/forums/submissions-414/unitinrangeevent-205036/
* */ Tt /* hiveworkshop.com/forums/jass-functions-413/system-timer-tools-201165/
* */ RegisterPlayerUnitEvent /* hiveworkshop.com/forums/jass-functions-413/snippet-registerplayerunitevent-203338/
*
************************************************************************************
*
* static method getActiveAuraCount takes UnitIndex unitId returns integer
* static method getAuraCount takes UnitIndex unitId returns integer
* static method getUnitsUnderAura takes UnitIndex sourceId returns integer
* static method getUnitsUnderActiveAura takes UnitIndex sourceId returns integer
*
* static method createAura takes UnitIndex sourceId returns nothing
* - Call this when the unit initially gets the aura. This can also be
* - called whenever the aura's level changes for an instant update.
*
* Interface
*
* private static constant boolean ANIMATED_BESTOW_AURA
* (required) - Determine whether animated units bestow auras or not
*
* private static constant real TIMEOUT
* (required) - How often the aura effect runs
*
* private static constant boolean STACKS
* (required) - Does the aura stack?
*
* private static method getLevel takes UnitIndex sourceId returns integer
* (required) - Returns the level of the aura on the unit
*
* private static method onLevel takes UnitIndex source, integer level returns nothing
* (optional) - Runs when aura levels up
*
* private static method getRange takes UnitIndex source, integer level returns real
* (required) - Should return the range of the aura
*
* private method onEndEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
* (optional) - Runs when the aura effect ends (aura no longer on unit)
*
* private method onEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
* (optional) - Runs when aura effect starts (aura just went on to unit)
*
* private method onPeriodicEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
* (optional) - Runs every period of the aura. First run is right after onEffect
*
* private static method absFilter takes UnitIndex source, UnitIndex entering returns boolean
* (optional) - Runs when the unit initially enters in the range of the aura. If this returns false, the
* - unit is ignored as if it doesn't exist and it will never be able to get the aura
*
* private method filter takes UnitIndex source, UnitIndex affected, integer level returns boolean
* (optional) - Runs whenever the aura cycles (every TIMEOUT seconds). This helps determine if the
* - aura is active or not for the unit.
*
* (it is either both of these at once or neither of them)
* private static method removeBuff takes unit whichUnit, integer level returns nothing
* (optional) - Runs when the buff icon should be removed from the unit
*
* private static method addBuff takes unit whichUnit, integer level returns nothing
* (optional) - Runs when the buff icon should be added to the unit
*
************************************************************************************/
globals
private integer array affectedLevel
private boolean array active
private UnitIndex array source
private UnitIndex array affected
endglobals
private function InitModule takes code index, code deindex, code death, code revive, boolean animatedBestow returns nothing
call RegisterUnitIndexEvent(Condition(index), UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(deindex), UnitIndexer.DEINDEX)
call UnitEvent.START_REINCARNATE.register(Condition(death))
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, Condition(death))
call UnitEvent.REINCARNATE.register(Condition(revive))
call UnitEvent.RESURRECT.register(Condition(revive))
if (animatedBestow) then
call UnitEvent.ANIMATE.register(Condition(revive))
endif
endfunction
module AuraStruct
private static integer array buffCount
private static integer array auraCount
private static integer array auraInstance
private static integer array activeAura
private static integer array onEnterT
private static integer array onEnterT2
private static integer array auraLevel
private static integer array level
private static thistype array next
private static thistype array prev
private static Table array affecting
private static real array range
private static integer array sourceAffected
static method getActiveAuraCount takes UnitIndex affectedId returns integer
return buffCount[affectedId]
endmethod
static method getAuraCount takes UnitIndex affectedId returns integer
return auraCount[affectedId]
endmethod
static method getUnitsUnderAura takes UnitIndex sourceId returns integer
return auraInstance[sourceId]
endmethod
static method getUnitsUnderActiveAura takes UnitIndex sourceId returns integer
return activeAura[sourceId]
endmethod
private method isActive takes integer af, integer so returns boolean
static if not thistype.STACKS then
static if thistype.filter.exists then
return filter(so, af, level[so]) and (level[so] > auraLevel[af] or sourceAffected[af] == this)
else
return level[so] > auraLevel[af] or sourceAffected[af] == this
endif
else
static if thistype.filter.exists then
return filter(so, af, level[so])
else
return true
endif
endif
endmethod
private static method removeCheck takes UnitIndex sourceId returns nothing
set next[prev[sourceId]]=next[sourceId]
set prev[next[sourceId]]=prev[sourceId]
call sourceId.unlock()
endmethod
private static integer array ho
private static boolean array wa
private static integer array ht
private static integer array htc
implement CTM
local boolean wasActive
local UnitIndex so
local UnitIndex af
local integer lev
local integer array l
local integer hc = 0
implement CTMExpire
set so = source[this]
set af = affected[this]
set lev = level[so]
if (/*
*/0 == lev or /*
*/not IsUnitInRange(GetUnitById(so), GetUnitById(af), range[so]) /*
*/) then
set auraCount[af] = auraCount[af] - 1
set auraInstance[so] = auraInstance[so] - 1
if (active[this]) then
set buffCount[af] = buffCount[af] - 1
set activeAura[so] = activeAura[so] - 1
static if thistype.addBuff.exists then
if (0 == buffCount[af]) then
call removeBuff(GetUnitById(af), auraLevel[af])
endif
endif
if (sourceAffected[af] == this) then
set auraLevel[af] = 0
set sourceAffected[af] = 0
endif
static if thistype.onEndEffect.exists then
call onEndEffect(so, af, affectedLevel[this])
endif
endif
call destroy()
if (0 != affecting[so]) then
call affecting[so].remove(af)
endif
call so.unlock()
call af.unlock()
elseif (affectedLevel[this] == level[so]) then
set wasActive = active[this]
set active[this] = isActive(af, so)
if (active[this]) then
if (lev > auraLevel[af]) then
if (lev > l[af]) then
set l[af] = lev
if (0 == ht[af]) then
set ht[af] = hc
set htc[hc] = af
set ho[hc] = this
set wa[hc] = wasActive
set hc = hc + 1
else
set hc = ht[af]
set ho[hc] = this
set wa[hc] = wasActive
endif
endif
static if thistype.STACKS then
static if thistype.onPeriodicEffect.exists then
call onPeriodicEffect(so, af, lev)
endif
endif
else
static if thistype.onPeriodicEffect.exists then
call onPeriodicEffect(so, af, lev)
endif
endif
elseif (wasActive) then
set buffCount[af] = buffCount[af] - 1
set activeAura[so] = activeAura[so] - 1
static if thistype.addBuff.exists then
if (0 == buffCount[af]) then
call removeBuff(GetUnitById(af), auraLevel[af])
endif
endif
if (sourceAffected[af] == this) then
set auraLevel[af] = 0
set sourceAffected[af] = 0
endif
static if thistype.onEndEffect.exists then
call onEndEffect(so, af, affectedLevel[this])
endif
endif
endif
implement CTMNull
loop
exitwhen 0 == hc
set hc = hc - 1
set ht[htc[hc]] = 0
set this = ho[hc]
set so = source[this]
set af = affected[this]
set lev = level[so]
static if not thistype.STACKS then
set wasActive = wa[hc]
if (not wasActive) then
set buffCount[af] = buffCount[af] + 1
set activeAura[so] = activeAura[so] + 1
endif
endif
static if thistype.addBuff.exists then
if (0 < buffCount[af]) then
call removeBuff(GetUnitById(af), auraLevel[af])
endif
endif
static if not thistype.STACKS then
static if thistype.onEndEffect.exists then
if (wasActive) then
call onEndEffect(so, af, affectedLevel[this])
else
if (0 != sourceAffected[af]) then
set active[sourceAffected[af]] = false
call thistype(sourceAffected[af]).onEndEffect(source[sourceAffected[af]], af, auraLevel[af])
endif
endif
endif
set sourceAffected[af] = this
set auraLevel[af] = lev
endif
static if thistype.addBuff.exists then
call addBuff(GetUnitById(af), lev)
endif
static if not thistype.STACKS then
static if thistype.onEffect.exists then
call onEffect(so, af, lev)
endif
endif
endloop
implement CTMEnd
private static method doEnter takes UnitIndex sourceId, UnitIndex entering returns nothing
local thistype time = affecting[sourceId][entering]
local boolean canEnter
local boolean wasActive
local integer buffs
local integer lev
static if thistype.absFilter.exists then
set canEnter = absFilter(sourceId, entering)
else
set canEnter = true
endif
if (0 != entering and GetUnitUserData(GetUnitById(entering)) == entering and canEnter and (0 == time or affectedLevel[time] != level[sourceId])) then
if (0 == time) then
call entering.lock()
call sourceId.lock()
set time = create(TIMEOUT)
set source[time] = sourceId
set affected[time] = entering
set affectedLevel[time] = 0
set affecting[sourceId][entering] = time
set auraCount[entering] = auraCount[entering] + 1
set auraInstance[sourceId] = auraInstance[sourceId] + 1
set wasActive = false
else
set wasActive = active[time]
endif
set lev = level[sourceId]
set active[time] = time.isActive(entering, sourceId)
if (active[time]) then
if (auraLevel[entering] < lev) then
static if thistype.addBuff.exists then
if (0 < buffCount[entering]) then
call removeBuff(GetUnitById(entering), auraLevel[entering])
endif
endif
static if thistype.addBuff.exists then
call addBuff(GetUnitById(entering), lev)
endif
endif
if (not wasActive) then
set buffCount[entering] = buffCount[entering] + 1
set activeAura[sourceId] = activeAura[sourceId] + 1
endif
static if thistype.onEndEffect.exists then
if (wasActive) then
call time.onEndEffect(sourceId, entering, affectedLevel[time])
else
static if not thistype.STACKS then
if (0 != sourceAffected[entering]) then
set buffCount[entering] = buffCount[entering] - 1
set activeAura[source[sourceAffected[entering]]] = activeAura[source[sourceAffected[entering]]] - 1
set active[sourceAffected[entering]] = false
call thistype(sourceAffected[entering]).onEndEffect(source[sourceAffected[entering]], entering, auraLevel[entering])
endif
endif
endif
endif
set auraLevel[entering] = lev
set sourceAffected[entering] = time
static if thistype.onEffect.exists then
call time.onEffect(sourceId, entering, lev)
endif
elseif (wasActive) then
if (sourceAffected[entering] == time) then
set auraLevel[entering] = 0
set sourceAffected[entering] = 0
endif
set buffCount[entering] = buffCount[entering] - 1
set activeAura[sourceId] = activeAura[sourceId] - 1
static if thistype.addBuff.exists then
if (0 == buffCount[entering]) then
call removeBuff(GetUnitById(entering), auraLevel[entering])
endif
endif
static if thistype.onEndEffect.exists then
call time.onEndEffect(sourceId, entering, affectedLevel[time])
endif
endif
set affectedLevel[time] = lev
endif
endmethod
private static method onEnter takes nothing returns boolean
call doEnter(GetEventSourceUnitId(), GetUnitUserData(GetTriggerUnit()))
return false
endmethod
private static method registerRange takes integer sourceId, unit sourceUnit, real range returns nothing
set onEnterT[sourceId] = RegisterUnitInRangeEvent(function thistype.onEnter, sourceUnit, range)
endmethod
private static method checkLevel takes nothing returns boolean
local thistype sourceId = next[0]
local integer newLevel
loop
exitwhen 0 == sourceId
set newLevel = getLevel(sourceId)
if (newLevel != level[sourceId]) then
if (0 != range[sourceId]) then
call UnregisterUnitInRangeEvent(onEnterT[sourceId])
endif
set level[sourceId] = newLevel
if (0 == newLevel) then
set range[sourceId] = 0
else
set range[sourceId] = getRange(sourceId, newLevel)
endif
static if thistype.onLevel.exists then
call onLevel(sourceId, level[sourceId])
endif
if (0 != newLevel) then
call doEnter(sourceId, sourceId)
endif
if (0 != range[sourceId]) then
call registerRange(sourceId, GetUnitById(sourceId), range[sourceId])
endif
endif
set sourceId = next[sourceId]
endloop
return false
endmethod
private static method addCheck takes UnitIndex sourceId returns nothing
call sourceId.lock()
set prev[sourceId]=prev[0]
set next[sourceId]=0
set next[prev[0]]=sourceId
set prev[0]=sourceId
endmethod
static method createAura takes UnitIndex sourceId returns boolean
local integer newLevel = getLevel(sourceId)
local unit sourceUnit = GetUnitById(sourceId)
if (not (IsUnitDead(sourceId) or IsUnitReincarnating(sourceId)) and 0 != GetUnitTypeId(sourceUnit) and GetUnitUserData(sourceUnit) == sourceId) then
call addCheck(sourceId)
if (0 == newLevel) then
set range[sourceId] = 0
else
set range[sourceId] = getRange(sourceId, newLevel)
endif
if (newLevel != level[sourceId]) then
static if thistype.onLevel.exists then
call onLevel(sourceId, newLevel)
endif
if (0 != newLevel) then
call doEnter(sourceId, sourceId)
endif
if (0 != range[sourceId]) then
call registerRange(sourceId, sourceUnit, range[sourceId])
endif
endif
set level[sourceId] = newLevel
endif
set sourceUnit = null
return false
endmethod
private static method onIndex takes nothing returns boolean
set affecting[GetIndexedUnitId()] = Table.create()
set level[GetIndexedUnitId()] = 0
set range[GetIndexedUnitId()] = 0
set onEnterT[GetIndexedUnitId()] = 0
set sourceAffected[GetIndexedUnitId()] = 0
return false
endmethod
private static method onDeath takes nothing returns boolean
local integer id = GetUnitUserData(GetTriggerUnit())
if (0 != level[id]) then
if (0 != range[id]) then
call UnregisterUnitInRangeEvent(onEnterT[id])
endif
call removeCheck(id)
set level[id] = 0
endif
return false
endmethod
private static method onRevive takes nothing returns boolean
set level[GetEventUnitId()] = getLevel(GetEventUnitId())
if (0 != level[GetEventUnitId()]) then
set range[GetEventUnitId()] = getRange(GetEventUnitId(), level[GetEventUnitId()])
call addCheck(GetEventUnitId())
call doEnter(GetEventUnitId(), GetEventUnitId())
if (0 != range[GetEventUnitId()]) then
call registerRange(GetEventUnitId(), GetEventUnit(), getRange(GetEventUnitId(), level[GetEventUnitId()]))
endif
endif
return false
endmethod
private static method onDeindex takes nothing returns boolean
if (0 != level[GetIndexedUnitId()]) then
set level[GetIndexedUnitId()] = 0
call removeCheck(GetIndexedUnitId())
call affecting[GetIndexedUnitId()].destroy()
set affecting[GetIndexedUnitId()] = 0
endif
return false
endmethod
private static method onInit takes nothing returns nothing
call Timer[TIMEOUT].list.register(Condition(function thistype.checkLevel))
static if thistype.ANIMATED_BESTOW_AURA then
call InitModule(function thistype.onIndex, function thistype.onDeindex, function thistype.onDeath, function thistype.onRevive, true)
else
call InitModule(function thistype.onIndex, function thistype.onDeindex, function thistype.onDeath, function thistype.onRevive, false)
endif
endmethod
endmodule
endlibrary
Demo
JASS:
struct tester extends array
//required
private static constant real TIMEOUT = .3
private static constant boolean ANIMATED_BESTOW_AURA = true
private static constant boolean STACKS = false
private static method getLevel takes UnitIndex sourceId returns integer
endmethod
private static method getRange takes UnitIndex source, integer level returns real
endmethod
//optional
private static method removeBuff takes unit whichUnit, integer level returns nothing
call UnitRemoveAbility(whichUnit, BUFF_ID) //for level?
endmethod
private static method addBuff takes unit whichUnit, integer level returns nothing
call DummyCaster[ABILITY_BUFF_ORDER].castTarget(...)
endmethod
private static method onLevel takes UnitIndex source, integer level returns nothing
endmethod
private method onEndEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
endmethod
private method onEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
endmethod
private method onPeriodicEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
endmethod
private static method absFilter takes UnitIndex source, UnitIndex entering returns boolean
endmethod
private method filter takes UnitIndex source, UnitIndex affected, integer level returns boolean
endmethod
//required
implement AuraStruct
//code
private static method onInit takes nothing returns nothing
//event registration
endmethod
endstruct