- Joined
- Sep 26, 2009
- Messages
- 9,505
Detect when a unit is healed by using a periodic timer which rules out the unit's regeneration. The problems with this resource are:
- There's no way to accurately get the healer unless you trigger the heal using UnitHealUnit.
- If there is more than one heal within the interval the events will merge.
- If the unit has very high regeneration (over 100 per second at 0.10 interval and 10.00 HEAL_THRESHOLD), its regen could be mistaken for a heal. To circumvent this, you can decrease the constant INTERVAL or increase the constant HEAL_THRESHOLD.
JASS:
library HealEvent initializer Init requires DamageEngineGUI
private function UnitFilter takes unit u returns boolean
return GetUnitAbilityLevel(u, 'Aloc') == 0
endfunction
globals
//Minimum heal to pass as an actual event. Should be greater than a unit's
//life regeneration per INTERVAL
private constant boolean HEAL_THRESHOLD = 10.00
//Less decreases the chance of two overlapping heals, but decreases performance.
private constant real INTERVAL = 0.067
endglobals
globals
private constant string HEAL_EVENT = "healEventVariable"
real healEventVariable = 0.00
private timer interval = CreateTimer()
private integer count = 0
private boolean array inSys
private integer array indices
private integer array indexRef
private real array lastLife
private real array regen
private boolean array regenerating
private real heal = 0.00
private unit target = null
private unit source = null
private unit swap
endglobals
//Returns true if the unit is in the process of regenerating health
function IsUnitRegenerating takes unit u returns boolean
return regenerating[GetUnitUserData(u)]
endfunction
//This function is an approximation and can't tell the difference between healing
//salves, healing wards, fountain of health and natural regeneration. If the
//unit is at full health or dead, this will return 0.00 (even if they otherwise
//have regeneration).
function GetUnitRegeneration takes unit u returns real
return regen[GetUnitUserData(u)] / INTERVAL
endfunction
function GetHealEventAmount takes nothing returns real
return heal
endfunction
function GetHealEventTarget takes nothing returns unit
return target
endfunction
function GetHealEventSource takes nothing returns unit
return source
endfunction
function CreateHealEventTrigger takes code toRun returns trigger
return CreateRealEventTrigger(HEAL_EVENT, 1.00, toRun)
endfunction
private function UnitHealUnitEx takes unit src, unit tgt, real amount, boolean heals returns boolean
local integer id = GetUnitUserData(tgt)
local real life = GetWidgetLife(tgt)
local boolean b
if heals then
call SetWidgetLife(tgt, life + amount)
set amount = GetWidgetLife(tgt) - life
set b = amount != 0.00
else
set amount = life - regen[id]*TimerGetRemaining(interval)/INTERVAL - lastLife[id]
set b = amount >= HEAL_THRESHOLD
endif
if b then
//Don't let the automatic event run as we're running the event manually
set lastLife[id] = lastLife[id] + amount
//caching with a swap variable because handling recursion is ugly
set swap = src
set src = source
set source = swap
set swap = tgt
set tgt = target
set target = swap
set life = heal
set heal = amount
//finally, fire the event
set healEventVariable = 0.00
set healEventVariable = 1.00
set healEventVariable = 0.00
//set event globals back to where they were
set heal = life
set target = tgt
set source = src
//These parameters need to be nulled since I used "set" on them
set tgt = null
set src = null
endif
return b
endfunction
function CheckPendingHeal takes unit u returns boolean
return UnitHealUnitEx(null, u, 0, false)
endfunction
function UnitHealUnit takes unit s, unit t, real r returns boolean
call CheckPendingHeal(t)
return UnitHealUnitEx(s, t, r, true)
endfunction
function UpdateHealEvent takes unit u returns nothing
local integer id = GetUnitUserData(u)
set lastLife[id] = GetWidgetLife(u) - regen[id]*TimerGetRemaining(interval)/INTERVAL
endfunction
//Function runs 1.00/INTERVAL times per second
private function OnExpire takes nothing returns nothing
local integer i = count
local real life
local real diff
loop
exitwhen i == 0
set i = i - 1
set i = indices[i]
set target = udg_UDexUnits[i]
set life = GetWidgetLife(target)
set diff = life - lastLife[i]
set lastLife[i] = life
set life = regen[i]
set heal = diff - life
if heal >= HEAL_THRESHOLD then
set healEventVariable = 1.00
set healEventVariable = 0.00
elseif diff != 0.00 then
if not regenerating[i] then
set regenerating[i] = true
set regen[i] = diff
else
set regen[i] = (life + diff) * 0.50
endif
elseif regenerating[i] then
set regenerating[i] = false
endif
set i = indexRef[i]
endloop
endfunction
private function OnDamage takes nothing returns nothing
call CheckPendingHeal(udg_DamageEventSource)
endfunction
private function OnAfterDamage takes nothing returns nothing
call UpdateHealEvent(udg_DamageEventSource)
endfunction
private function OnCreate takes nothing returns nothing
if not inSys[udg_UDex] and UnitFilter(GetIndexedUnit()) then
set inSys[udg_UDex] = true
set indices[count] = udg_UDex
set indexRef[udg_UDex] = count
set lastLife[count] = GetWidgetLife(GetIndexedUnit())
set count = count + 1
endif
endfunction
private function OnRemove takes nothing returns nothing
local integer index
local integer pop
if inSys[udg_UDex] then
set inSys[udg_UDex] = false
set count = count - 1
set indices[indexRef[udg_UDex]] = indices[count]
set indexRef[indices[count]] = indexRef[udg_UDex]
endif
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), INTERVAL, true, function OnExpire)
call CreateRealEventTrigger("udg_DamageEvent", 1.00, function OnDamage)
call CreateRealEventTrigger("udg_AfterDamageEvent", 1.00, function OnAfterDamage)
//The unit can be added to the system when it's first created
call CreateRealEventTrigger("udg_UnitIndexEvent", 1.00, function OnCreate)
//when it's unloaded from a transport
call CreateRealEventTrigger("udg_CargoEvent", 2.00, function OnCreate)
//or when it's resurrected.
call CreateRealEventTrigger("udg_DeathEvent", 2.00, function OnCreate)
//A unit should be removed from the system if it was removed from Unit Indexer,
call CreateRealEventTrigger("udg_UnitIndexEvent", 2.00, function OnRemove)
//when it's loaded into a transport,
call CreateRealEventTrigger("udg_CargoEvent", 1.00, function OnRemove)
//when it begins reincarnating,
call CreateRealEventTrigger("udg_DeathEvent", 0.50, function OnRemove)
//when it dies,
call CreateRealEventTrigger("udg_DeathEvent", 1.00, function OnRemove)
//or is removed from the game. Whichever comes first.
call CreateRealEventTrigger("udg_DeathEvent", 3.00, function OnRemove)
endfunction
endlibrary
Last edited: