| This is a compilation of libraries intended to replace the damage detection and prevention engine ADamage. Originally, this was intended to be merely the next version of ADamage, but some conceptual changes such as moving from damage prevention to more general damage modification made it difficult to maintain backwards compatibility, so I decided to make it a new resource instead which could provide a more general, robust and modular damage detection and modification framework than ADamage. Also, I know some people will like that fact that I gave it a new name.
The resource is split into two libraries: - DamageEvent, a damage detection engine.
If you are already using another damage detection system in your map, DamageEvent will interface with it instead of creating its own damage detection triggers to avoid wasting performance. Currently supported damage detection systems are IDDS and LLDD.
 DamageEvent: library DamageEvent initializer Init requires optional DamageModifiers, optional LightLeaklessDamageDetect, optional IntuitiveDamageSystem, optional xedamage
globals
private constant real DAMAGE_THRESHOLD = 0.0
private constant boolean REFRESH_TRIGGER = true
private constant real TRIGGER_REFRESH_PERIOD = 300.0
endglobals
private function CanTakeDamage takes unit u returns boolean
return true
endfunction
public function interface Response takes unit damagedUnit, unit damageSource, real damage returns nothing
globals
private Response array responses
private integer responsesCount = 0
endglobals
function RegisterDamageResponse takes Response r returns nothing
set responses[responsesCount]=r
set responsesCount=responsesCount+1
endfunction
private function Damage takes nothing returns nothing
local unit damaged=GetTriggerUnit()
local unit damager=GetEventDamageSource()
local real damage=GetEventDamage()
local integer i = 0
loop
exitwhen not (damage>DAMAGE_THRESHOLD)
static if LIBRARY_xedamage then
exitwhen xedamage.isDummyDamage
endif
static if LIBRARY_DamageModifiers then
set damage=RunDamageModifiers()
endif
loop
exitwhen i>=responsesCount
call responses[i].execute(damaged, damager, damage)
set i=i+1
endloop
exitwhen true
endloop
set damaged=null
set damager=null
endfunction
private function DamageC takes nothing returns boolean
call Damage()
return false
endfunction
globals
private group g
private boolexpr b
private boolean clear
private trigger currentTrg
private triggeraction currentTrgA
private trigger oldTrg = null
private triggeraction oldTrgA = null
endglobals
private function TriggerRefreshEnum takes nothing returns nothing
if clear then
call GroupClear(g)
set clear = false
endif
call GroupAddUnit(g, GetEnumUnit())
call TriggerRegisterUnitEvent( currentTrg, GetEnumUnit(), EVENT_UNIT_DAMAGED )
endfunction
private function TriggerRefresh takes nothing returns nothing
if oldTrg!=null then
call TriggerRemoveAction(oldTrg, oldTrgA)
call DestroyTrigger(oldTrg)
endif
call DisableTrigger(currentTrg)
set oldTrg=currentTrg
set oldTrgA=currentTrgA
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set clear = true
call ForGroup(g, function TriggerRefreshEnum)
if clear then
call GroupClear(g)
endif
endfunction
private function DamageRegister takes nothing returns boolean
local unit u = GetFilterUnit()
if CanTakeDamage(u) then
call TriggerRegisterUnitEvent( currentTrg, u, EVENT_UNIT_DAMAGED )
call GroupAddUnit(g, u)
endif
set u = null
return false
endfunction
private function Init takes nothing returns nothing
local rect rec
local region reg
local trigger t
static if LIBRARY_IntuitiveDamageSystem then
set t=CreateTrigger()
call TriggerAddAction(t, function Damage)
call TriggerRegisterDamageEvent(t, 0)
else
static if LIBRARY_LightLeaklessDamageDetect then
call AddOnDamageFunc(Condition(function DamageC))
else
set rec = GetWorldBounds()
set reg = CreateRegion()
set t = CreateTrigger()
call RegionAddRect(reg, rec)
call TriggerRegisterEnterRegion(t, reg, Condition(function DamageRegister))
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set g = CreateGroup()
call GroupEnumUnitsInRect(g, rec, Condition(function DamageRegister))
if REFRESH_TRIGGER then
call TimerStart(CreateTimer(), TRIGGER_REFRESH_PERIOD, true, function TriggerRefresh)
endif
call RemoveRect(rec)
set rec = null
set b = null
endif
endif
endfunction
endlibrary
- DamageModifiers, an optional addon that allows you to modify damage.
Two versions of DamageModifiers are provided, one that uses Table and one that uses AutoIndex.
DamageModifiers can use either xepreload or AbilityPreload to preload the ability it uses.
 DamageModifiers (Table version): library DamageModifiers initializer Init requires Table, optional AbilityPreload, optional xepreload
globals
private constant boolean NEW_MODIFIER_ON_TOP = true
private constant integer SURVIVAL_ABILITY = 'DMsa'
endglobals
//! external ObjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
private interface DamageModiferTemplate
method onDamageDealt takes unit damagedUnit, real damage returns real defaults 0.0
method onDamageTaken takes unit damageSource, real damage returns real defaults 0.0
endinterface
private keyword Evaluate
struct DamageModifier extends DamageModiferTemplate
private unit targetunit
integer priority
private DamageModifier next=0
private static HandleTable first
private method listInsert takes nothing returns nothing
local DamageModifier dm=DamageModifier(DamageModifier.first[.targetunit])
if dm==0 or dm.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.priority==.priority) then
set .next=DamageModifier(DamageModifier.first[.targetunit])
set DamageModifier.first[.targetunit]=integer(this)
else
loop
exitwhen dm.next == 0 or dm.next.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.next.priority==.priority)
set dm=dm.next
endloop
set .next=dm.next
set dm.next=this
endif
endmethod
private method listRemove takes nothing returns nothing
local DamageModifier dm=DamageModifier(DamageModifier.first[.targetunit])
if dm==this then
set DamageModifier.first[.targetunit]=integer(.next)
else
loop
exitwhen dm.next==this
set dm=dm.next
endloop
set dm.next=.next
endif
endmethod
static method create takes unit u, integer priority returns DamageModifier
local DamageModifier this
if u==null then
return 0
endif
set this=DamageModifier.allocate()
set .targetunit=u
set .priority = priority
call .listInsert()
return this
endmethod
method onDestroy takes nothing returns nothing
call .listRemove()
if DamageModifier.first[.targetunit]==0 then
call DamageModifier.first.flush(.targetunit)
endif
endmethod
static method onInit takes nothing returns nothing
set DamageModifier.first=HandleTable.create()
endmethod
static method Evaluate takes unit damaged, unit damager, real damage returns real
local real modifier=0.0
local DamageModifier this=DamageModifier(DamageModifier.first[damager])
local DamageModifier that=DamageModifier(DamageModifier.first[damaged])
loop
exitwhen this==0 and that==0
if this!=0 and (that==0 or this.priority>=that.priority) then
set modifier=modifier+this.onDamageDealt(damaged, damage+modifier)
set this=this.next
elseif that !=0 then
set modifier=modifier+that.onDamageTaken(damager, damage+modifier)
set that=that.next
endif
endloop
return modifier
endmethod
endstruct
private struct Damage
unit u
real hp=0.0
real temp=0.0
private Damage next
private static Damage first=0
private static timer delay=CreateTimer()
private static method get takes unit u returns Damage
local Damage this=Damage.first
loop
exitwhen this==0 or .u==u
set this=.next
endloop
return this
endmethod
private static method create takes unit u returns Damage
local Damage this=Damage.allocate()
set .u = u
set .next = Damage.first
set Damage.first=this
return this
endmethod
method onDestroy takes nothing returns nothing
set Damage.first=.next
set .u = null
endmethod
private static method end takes nothing returns nothing
loop
exitwhen Damage.first==0
call UnitRemoveAbility(Damage.first.u, SURVIVAL_ABILITY)
call SetWidgetLife(Damage.first.u, Damage.first.hp)
call Damage.first.destroy()
endloop
endmethod
static method RunModifiers takes nothing returns real
local unit damaged = GetTriggerUnit()
local unit damager = GetEventDamageSource()
local real damage = GetEventDamage()
local real modifier = 0.0
local real life = GetWidgetLife(damaged)
local real maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
local real stackeddamage
local Damage d
if damaged==null then
set damager=null
set damaged=null
return 0.0
endif
set modifier=DamageModifier.Evaluate(damaged, damager, damage)
set d=Damage.get(damaged)
if d==0 then
set d=Damage.create(damaged)
set d.hp=life
call TimerStart(Damage.delay, 0.0, false, function Damage.end)
endif
set stackeddamage=((life-d.temp)-d.hp)+damage
set damage=damage+modifier
set d.hp=d.hp-damage
if d.hp>0.405 and maxlife-stackeddamage<=0.405 then
call UnitAddAbility(damaged, SURVIVAL_ABILITY)
set maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
endif
set modifier=modifier+d.temp
set d.temp=0.0
if life-modifier>0.405 and life-modifier<=maxlife then
call SetWidgetLife(damaged, life-modifier)
elseif life-modifier>maxlife then
set d.temp=modifier-(life-maxlife)
call SetWidgetLife(damaged, maxlife)
else
set d.temp=modifier-(life-0.406)
call SetWidgetLife(damaged, 0.406)
endif
set damaged=null
set damager=null
return damage
endmethod
static method getLife takes unit u returns real
local Damage d=Damage.get(u)
if d==0 then
return GetWidgetLife(u)
endif
if d.hp<=0.405 then
return 0.0
endif
return d.hp
endmethod
static method setLife takes unit u, real newLife returns nothing
local Damage d=Damage.get(u)
local real modifier
local real life
local real maxlife
local real stackeddamage
if d==0 then
call SetWidgetLife(u, newLife)
else
set modifier=newLife-d.hp
if modifier>0 and d.hp<=0.405 then
set life=GetWidgetLife(u)
set maxlife = GetUnitState(u,UNIT_STATE_MAX_LIFE)
set stackeddamage=(life-d.temp)-d.hp
if d.hp>0.405 and maxlife-stackeddamage<=0.405 then
call UnitAddAbility(u, SURVIVAL_ABILITY)
set maxlife = GetUnitState(u,UNIT_STATE_MAX_LIFE)
endif
set d.hp=newLife
if maxlife-life>modifier then
call SetWidgetLife(u, life+modifier)
else
call SetWidgetLife(u, maxlife)
set d.temp=d.temp-modifier+maxlife-life
endif
else
set d.hp=newLife
set d.temp=d.temp-modifier
endif
endif
endmethod
endstruct
private function Init takes nothing returns nothing
static if LIBRARY_xepreload then
call XE_PreloadAbility(SURVIVAL_ABILITY)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(SURVIVAL_ABILITY)
endif
endif
endfunction
function RunDamageModifiers takes nothing returns real
return Damage.RunModifiers()
endfunction
function GetUnitLife takes unit u returns real
return Damage.getLife(u)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
call Damage.setLife(u, newLife)
endfunction
endlibrary
 DamageModifiers (AutoIndex version): library DamageModifiers initializer Init requires AutoIndex, optional AbilityPreload, optional xepreload
globals
private constant boolean NEW_MODIFIER_ON_TOP = true
private constant integer SURVIVAL_ABILITY = 'DMsa'
endglobals
//! external ObjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
private interface DamageModiferTemplate
method onDamageDealt takes unit damagedUnit, real damage returns real defaults 0.0
method onDamageTaken takes unit damageSource, real damage returns real defaults 0.0
endinterface
private keyword Evaluate
struct DamageModifier extends DamageModiferTemplate
private unit targetunit
private integer priority
private DamageModifier next=0
private static DamageModifier array first
private method listInsert takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.targetunit)]
if dm==0 or dm.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.priority==.priority) then
set .next=DamageModifier.first[GetUnitId(.targetunit)]
set DamageModifier.first[GetUnitId(.targetunit)]=this
else
loop
exitwhen dm.next == 0 or dm.next.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.next.priority==.priority)
set dm=dm.next
endloop
set .next=dm.next
set dm.next=this
endif
endmethod
private method listRemove takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.targetunit)]
if dm==this then
set DamageModifier.first[GetUnitId(.targetunit)]=.next
else
loop
exitwhen dm.next==this
set dm=dm.next
endloop
set dm.next=.next
endif
endmethod
static method create takes unit u, integer priority returns DamageModifier
local DamageModifier this
if u==null or not(IsUnitIndexed(u)) then
return 0
endif
set this=DamageModifier.allocate()
set .targetunit=u
set .priority = priority
call .listInsert()
return this
endmethod
method onDestroy takes nothing returns nothing
call .listRemove()
endmethod
static method Evaluate takes unit damaged, unit damager, real damage returns real
local real modifier=0.0
local DamageModifier this=0
local DamageModifier that=DamageModifier(DamageModifier.first[GetUnitId(damaged)])
if IsUnitIndexed(damager) then
set this=DamageModifier(DamageModifier.first[GetUnitId(damager)])
endif
loop
exitwhen this==0 and that==0
if this!=0 and (that==0 or this.priority>=that.priority) then
set modifier=modifier+this.onDamageDealt(damaged, damage+modifier)
set this=this.next
elseif that !=0 then
set modifier=modifier+that.onDamageTaken(damager, damage+modifier)
set that=that.next
endif
endloop
return modifier
endmethod
endstruct
private struct Damage
unit u
real hp=0.0
real temp=0.0
private Damage next
private static Damage first=0
private static timer delay=CreateTimer()
private static Damage array unitlist
private static method get takes unit u returns Damage
return Damage.unitlist[GetUnitId(u)]
endmethod
private static method create takes unit u returns Damage
local Damage this=Damage.allocate()
set .u = u
set .next = Damage.first
set Damage.first=this
set Damage.unitlist[GetUnitId(.u)]=this
return this
endmethod
method onDestroy takes nothing returns nothing
set Damage.first=.next
set Damage.unitlist[GetUnitId(.u)]=0
set .u = null
endmethod
private static method end takes nothing returns nothing
loop
exitwhen Damage.first==0
call UnitRemoveAbility(Damage.first.u, SURVIVAL_ABILITY)
call SetWidgetLife(Damage.first.u, Damage.first.hp)
call Damage.first.destroy()
endloop
endmethod
static method RunModifiers takes nothing returns real
local unit damaged = GetTriggerUnit()
local unit damager = GetEventDamageSource()
local real damage = GetEventDamage()
local real modifier = 0.0
local real life = GetWidgetLife(damaged)
local real maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
local real stackeddamage
local Damage d
if damaged==null or not(IsUnitIndexed(damaged)) then
set damager=null
set damaged=null
return 0.0
endif
set modifier=DamageModifier.Evaluate(damaged, damager, damage)
set d=Damage.get(damaged)
if d==0 then
set d=Damage.create(damaged)
set d.hp=life
call TimerStart(Damage.delay, 0.0, false, function Damage.end)
endif
set stackeddamage=((life-d.temp)-d.hp)+damage
set damage=damage+modifier
set d.hp=d.hp-damage
if d.hp>0.405 and maxlife-stackeddamage<=0.405 then
call UnitAddAbility(damaged, SURVIVAL_ABILITY)
set maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
endif
set modifier=modifier+d.temp
set d.temp=0.0
if life-modifier>0.405 and life-modifier<=maxlife then
call SetWidgetLife(damaged, life-modifier)
elseif life-modifier>maxlife then
set d.temp=modifier-(life-maxlife)
call SetWidgetLife(damaged, maxlife)
else
set d.temp=modifier-(life-0.406)
call SetWidgetLife(damaged, 0.406)
endif
set damaged=null
set damager=null
return damage
endmethod
static method getLife takes unit u returns real
local Damage d=0
if IsUnitIndexed(u) then
set d=Damage.get(u)
endif
if d==0 then
return GetWidgetLife(u)
endif
if d.hp<=0.405 then
return 0.0
endif
return d.hp
endmethod
static method setLife takes unit u, real newLife returns nothing
local Damage d=0
local real modifier
local real life
local real maxlife
local real stackeddamage
if IsUnitIndexed(u) then
set d=Damage.get(u)
endif
if d==0 then
call SetWidgetLife(u, newLife)
else
set modifier=newLife-d.hp
if modifier>0 and d.hp<=0.405 then
set life=GetWidgetLife(u)
set maxlife = GetUnitState(u,UNIT_STATE_MAX_LIFE)
set stackeddamage=(life-d.temp)-d.hp
if d.hp>0.405 and maxlife-stackeddamage<=0.405 then
call UnitAddAbility(u, SURVIVAL_ABILITY)
set maxlife = GetUnitState(u,UNIT_STATE_MAX_LIFE)
endif
set d.hp=newLife
if maxlife-life>modifier then
call SetWidgetLife(u, life+modifier)
else
call SetWidgetLife(u, maxlife)
set d.temp=d.temp-modifier+maxlife-life
endif
else
set d.hp=newLife
set d.temp=d.temp-modifier
endif
endif
endmethod
endstruct
private function Init takes nothing returns nothing
static if LIBRARY_xepreload then
call XE_PreloadAbility(SURVIVAL_ABILITY)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(SURVIVAL_ABILITY)
endif
endif
endfunction
function RunDamageModifiers takes nothing returns real
return Damage.RunModifiers()
endfunction
function GetUnitLife takes unit u returns real
return Damage.getLife(u)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
call Damage.setLife(u, newLife)
endfunction
endlibrary
|