library ABuffAura requires ABuff //***************************************************************** //* ABUFF AURA SYSTEM 1.5 //* //* written by: Anitarf //* requires: -ABuff //* //* This library allows you to create efficient triggered auras //* using the ABuff system. To use it, you must first define a //* STANDARD aBuffType, then you can add the aura to a unit (or //* update it if the unit already has it) using this function: //* //* function UnitSetABuffAura takes unit u, aBuffType appliedBuff, integer level, ABuffAuraTargets targets, real range returns nothing //* //* The unit argument passed tot he function will be the source of //* the aura, appliedBuff is the buff type used by the aura, level //* and range should be self-explanatory and the targets argument //* is a function that follows the ABuffAuraTargets function //* interface and determines whether a unit should be affected by //* the aura. Note that the buff type used by an aura must be in //* the STANDARD category. //* //* To remove an aura from a unit, use this function: //* //* function UnitRemoveABuffAura takes unit u, aBuffType removedBuff returns boolean //* //* The returned boolean will be true if the aura was removed and //* false if the unit did not have an aura granting that buff. If //* you remove the aura's aBuff from a unit, that buff will not //* be reapplied until the unit exits and re-enters the range of //* the aura, so be careful not to remove aura aBuffs with //* triggered dispel abilities unless that is the effect you want //* to achieve. //* //* When a unit that is already under the effect of the aura from //* one source comes in range of another source, the aBuff on the //* unit will be refreshed to the new source if that source has a //* higher level, otherwise it will not be refreshed. //***************************************************************** globals private constant real AURA_REFRESH_PERIOD = 0.25 private constant real MAXIMUM_COLLISION_SIZE = 55. endglobals // This function interface is included in the calibration section // for user reference only and should not be changed in any way. function interface ABuffAuraTargets takes unit affected, unit auraGiver returns boolean // ================================================================ // AURA UNIT private struct aBuffAuraUnit aBuffAuraUnit next = 0 aBuffAuraUnit prev = 0 unit u = null integer level = 0 real range = 0 group affected ABuffAuraTargets targets = 0 boolean torefresh=false boolean todestroy=false static method create takes unit u, integer level, ABuffAuraTargets targets, real range returns aBuffAuraUnit local aBuffAuraUnit this = aBuffAuraUnit.allocate() set .u = u set .level=level set .range=range set .targets=targets if .affected == null then set .affected = CreateGroup() endif return this endmethod method onDestroy takes nothing returns nothing call GroupClear(.affected) endmethod endstruct // ================================================================ // AURA TYPE private struct aBuffAura aBuffType appliedBuff = 0 aBuffAuraUnit first = 0 private static aBuffAura array table private static aBuffAura array list private static integer count = 0 private integer listIndex = 0 private static method create takes aBuffType abt returns aBuffAura local aBuffAura this=aBuffAura.allocate() set aBuffAura.list[aBuffAura.count] = this set aBuffAura.table[abt] = this set .listIndex = aBuffAura.count set .appliedBuff = abt set aBuffAura.count=aBuffAura.count+1 return this endmethod static method get takes aBuffType abt returns aBuffAura if aBuffAura.table[abt] == 0 then return aBuffAura.create(abt) endif return aBuffAura.table[abt] endmethod method onDestroy takes nothing returns nothing //this is only called if the aura has no units linked so no need to cleanup .first set aBuffAura.count=aBuffAura.count-1 set aBuffAura.table[.appliedBuff] = 0 set aBuffAura.list[.listIndex]=aBuffAura.list[aBuffAura.count] set aBuffAura.list[.listIndex].listIndex = .listIndex endmethod // ================================================================ method getAuraUnit takes unit u returns aBuffAuraUnit local aBuffAuraUnit abau = .first loop exitwhen abau==0 if abau.u==u then return abau endif set abau = abau.next endloop return 0 endmethod private method insertAuraUnit takes aBuffAuraUnit insert returns nothing local aBuffAuraUnit abau = .first if insert.level>=abau.level then set .first=insert set abau.prev=insert set insert.next=abau else loop exitwhen insert.level>=abau.next.level or abau.next==0 set abau=abau.next endloop if abau.next!=0 then set abau.next.prev=insert set insert.next=abau.next endif set insert.prev=abau set abau.next=insert endif endmethod private method removeAuraUnit takes aBuffAuraUnit remove returns nothing if .first==remove then set .first = remove.next else set remove.prev.next = remove.next endif if remove.next != 0 then set remove.next.prev = remove.prev endif endmethod // ================================================================ private static aBuffAuraUnit current private static boolean updating = false private static group inrange = CreateGroup() private static group toremove = CreateGroup() private static group processed = CreateGroup() private static boolexpr enum private static method groupenum takes nothing returns boolean // get eligible targets by picking units in range that: // - are not dead, // - match the target criteria and // - are not already affected by a higher level of the aura. local unit u=GetFilterUnit() local boolean b=(not IsUnitType(u, UNIT_TYPE_DEAD)) and (aBuffAura.current.targets.evaluate(u, current.u)) and (not IsUnitInGroup(u, aBuffAura.processed)) set u=null return b endmethod private static method updateAffectedUnits takes nothing returns nothing local unit u=GetEnumUnit() if IsUnitInGroup(u, aBuffAura.inrange) then call GroupAddUnit(aBuffAura.processed, u) call GroupRemoveUnit(aBuffAura.inrange, u) else if not IsUnitInGroup(u, aBuffAura.processed) then call GroupAddUnit(aBuffAura.toremove, u) endif call GroupRemoveUnit(aBuffAura.current.affected, u) endif set u=null endmethod method update takes nothing returns nothing local aBuffAuraUnit abau=.first local unit u set aBuffAura.updating=true call GroupClear(aBuffAura.inrange) call GroupClear(aBuffAura.processed) call GroupClear(aBuffAura.toremove) loop exitwhen abau==0 set aBuffAura.current=abau if abau.torefresh or abau.todestroy then call ForGroup(abau.affected, function aBuffAura.updateAffectedUnits) set abau.torefresh=false endif if abau.todestroy then call .removeAuraUnit(abau) call abau.destroy() else call GroupEnumUnitsInRange(aBuffAura.inrange, GetUnitX(abau.u), GetUnitY(abau.u), abau.range, aBuffAura.enum) call ForGroup(abau.affected, function aBuffAura.updateAffectedUnits) loop set u=FirstOfGroup(aBuffAura.inrange) exitwhen u==null call ABuffApply(.appliedBuff, u, abau.u, 0.0, abau.level, 0) call GroupAddUnit(abau.affected, u) call GroupAddUnit(aBuffAura.processed, u) call GroupRemoveUnit(aBuffAura.toremove, u) call GroupRemoveUnit(aBuffAura.inrange, u) endloop endif set abau=abau.next endloop loop set u=FirstOfGroup(aBuffAura.toremove) exitwhen u==null call ABuffRemove(GetABuffFromUnitByType(u, .appliedBuff)) call GroupRemoveUnit(aBuffAura.toremove, u) endloop set aBuffAura.updating=false endmethod static method updateAll takes nothing returns nothing local integer i=0 loop exitwhen i>=aBuffAura.count call aBuffAura.list[i].update() if aBuffAura.list[i].first==0 then call aBuffAura.list[i].destroy() else set i=i+1 endif endloop endmethod private static method onInit takes nothing returns nothing call TimerStart(CreateTimer(), AURA_REFRESH_PERIOD, true, function aBuffAura.updateAll) set aBuffAura.enum = Condition(function aBuffAura.groupenum) endmethod // ================================================================ method setUnit takes unit u, integer level, ABuffAuraTargets targets, real range returns nothing local aBuffAuraUnit abau=.getAuraUnit(u) if abau==0 then call .insertAuraUnit(aBuffAuraUnit.create(u, level, targets, range)) else if abau.level != level then call .removeAuraUnit(abau) set abau.level=level call .insertAuraUnit(abau) endif if abau.targets != targets then set abau.targets=targets set abau.torefresh=true endif set abau.range=range set abau.todestroy=false endif if not aBuffAura.updating then call .update() endif endmethod method removeUnit takes unit u returns boolean local aBuffAuraUnit abau=.getAuraUnit(u) if abau!=0 then set abau.todestroy=true if not aBuffAura.updating then call .update() endif return true endif return false endmethod endstruct // ================================================================ // MAIN USER FUNCTIONS function UnitSetABuffAura takes unit u, aBuffType appliedBuff, integer level, ABuffAuraTargets targets, real range returns nothing if appliedBuff.category==ABuff_STANDARD then call aBuffAura.get(appliedBuff).setUnit(u, level, targets, range) debug else debug call BJDebugMsg("UnitSetABuffAura error: the function only accepts buff types in the STANDARD category.") endif endfunction function UnitRemoveABuffAura takes unit u, aBuffType removedBuff returns boolean return aBuffAura.get(removedBuff).removeUnit(u) endfunction endlibrary