library ShieldSystem requires optional TimerUtils
/*********************************************************************************************************************
* This is a shield system that can easily create a shield for a certain unit to absorb damage.It is
* lightweight and does not require any other resource (TimerUtils is only optional but recommanded for
* better efficiency). It can be used both for GUI and Jass users.
*
* Version: 1.0.1
*
* API:
* function ShieldSetUp takes unit u, real amount, real duration, string sfx, string attach returns nothing
* "u" stands for the unit to whom you want to give the shield.
* "amount" is the damage that the shield is able to absorb.
* "duration" is the duration of the shield.
* "sfx" is the string for the effect of the shield.
* "attach" is the attachment point of the special effect on the unit.
*
* The next API has nothing to do with the creation of a protective shield, I just made it for the convenience of
* writing this system, some people may find it useful. It is used for adding and removing a certain ability in a
* period of time, especially useful for adding buff to a certain unit for a period of time.
*
* function AbilityTimedSetUp takes unit u, integer ab, real duration returns nothing
*
* Implementation:
* 1. Copy the "ShieldSystem" trigger to your map;
* 2. Copy the "Life Saver Ability" to your map;
* 3. Make sure to match the ability ID of the "Life Saver Ability" with the constant integer LIFE_SAVER_AB;
* 4. You can copy the "TimerUtils" trigger to your map if you want better efficiency.
* Done!
*
*///*****************************************************************************************************************
globals
private hashtable ht = InitHashtable()
private constant integer LIFE_SAVER_AB = 'A000' //The ability to prevent unit from dying when the damage is more than its HP.
endglobals
//AbilityTimed is used for adding and removing a certain ability in a period of time.
struct AbilityTimed
private integer abil
private unit u
private static method Remove takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this
static if LIBRARY_TimerUtils then
set this = GetTimerData(t)
call UnitRemoveAbility(this.u, this.abil)
call ReleaseTimer(t)
else
set this = LoadInteger(ht, GetHandleId(t), 0)
call FlushChildHashtable(ht, GetHandleId(t))
call UnitRemoveAbility(this.u, this.abil)
call PauseTimer(t)
call DestroyTimer(t)
endif
set this.u = null
call this.destroy()
set t = null
endmethod
static method Create takes unit u, integer ab, real duration returns thistype
local thistype this = thistype.allocate()
local timer t
static if LIBRARY_TimerUtils then
set t = NewTimer()
call SetTimerData(t, this)
else
set t = CreateTimer()
call SaveInteger(ht, GetHandleId(t), 0, this)
endif
call UnitAddAbility(u, ab)
set this.u = u
set this.abil = ab
call TimerStart(t, duration, false, function thistype.Remove)
set t = null
return this
endmethod
endstruct
function AbilityTimedSetUp takes unit u, integer ab, real duration returns nothing
call AbilityTimed.Create(u, ab, duration)
endfunction
//The core of ShieldSystem
struct ShieldSystem
private real shieldAmount
private unit shieldUnit
private trigger shieldTrig
private real shieldDuration
private string shieldSfx
private effect shieldEff
private timer shieldTimer
private real newLife
private boolean b = false
//Removing the shield when the duration ends
private static method Remove takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this
static if LIBRARY_TimerUtils then
set this = GetTimerData(t)
call ReleaseTimer(t)
else
set this = LoadInteger(ht, GetHandleId(t), 0)
call FlushChildHashtable(ht, GetHandleId(t))
call PauseTimer(t)
call DestroyTimer(t)
endif
if this.shieldTrig != null then
call FlushChildHashtable(ht, GetHandleId(this.shieldTrig))
call DestroyTrigger(this.shieldTrig)
endif
set this.shieldTrig = null
set this.shieldUnit = null
if this.shieldEff != null then
call DestroyEffect(this.shieldEff)
endif
set this.shieldEff = null
set this.shieldTimer = null
call this.destroy()
set t = null
endmethod
private static method AdjustHP takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this
static if LIBRARY_TimerUtils then
set this = GetTimerData(t)
call ReleaseTimer(t)
else
set this = LoadInteger(ht, GetHandleId(t), 0)
call FlushChildHashtable(ht, GetHandleId(t))
call PauseTimer(t)
call DestroyTimer(t)
endif
call SetWidgetLife(this.shieldUnit, this.newLife)
if this.b then
set this.shieldUnit = null
call this.destroy()
endif
set t = null
endmethod
private static method DamageHandler takes nothing returns nothing
local trigger trig = GetTriggeringTrigger()
local thistype this = LoadInteger(ht, GetHandleId(trig), 0)
local real dmg = GetEventDamage()
local real currentLife = GetWidgetLife(this.shieldUnit)
local real life
local timer t
if dmg > currentLife then
/*If the damage is able to kill the target, then
adding Life Saver Ability to prevent the target from dying*/
call AbilityTimedSetUp(this.shieldUnit, LIFE_SAVER_AB, 0.00)
//Abosrbing the incoming damage.
set this.shieldAmount = this.shieldAmount - dmg
//If the incoming damage is more than the shield can handle, then...
if this.shieldAmount <= 0 then
//If the reduced damage is still more than the target's current life, then kill the target.
if RAbsBJ(this.shieldAmount) >= currentLife then
//Stop the duration timer
static if LIBRARY_TimerUtils then
call ReleaseTimer(this.shieldTimer)
else
call PauseTimer(this.shieldTimer)
call DestroyTimer(this.shieldTimer)
endif
call KillUnit(this.shieldUnit)
//Release memory
call DestroyEffect(this.shieldEff)
call FlushChildHashtable(ht, GetHandleId(trig))
call DestroyTrigger(trig)
set this.shieldUnit = null
set this.shieldTrig = null
set this.shieldEff = null
set this.shieldTimer = null
call this.destroy()
else
/*If the reduced damage is less than the target's current life,
then set the target's life to the correct amount by using a 0 second timer. */
//Stop the duration timer
static if LIBRARY_TimerUtils then
call ReleaseTimer(this.shieldTimer)
else
call PauseTimer(this.shieldTimer)
call DestroyTimer(this.shieldTimer)
endif
set life = currentLife - RAbsBJ(this.shieldAmount)
static if LIBRARY_TimerUtils then
set t = NewTimerEx(this)
set this.newLife = life
else
set t = CreateTimer()
call SaveInteger(ht, GetHandleId(t), 0, this)
set this.newLife = life
endif
//Set this to true in order to to null "this.shieldUnit" in the timer callback
set this.b = true
call TimerStart(t, 0.00, false, function thistype.AdjustHP)
//Release memory
call DestroyEffect(this.shieldEff)
call FlushChildHashtable(ht, GetHandleId(trig))
call DestroyTrigger(trig)
set this.shieldTrig = null
set this.shieldEff = null
set this.shieldTimer = null
set t = null
endif
else
//If the shield is strong enough to withstand the damage.
set life = GetWidgetLife(this.shieldUnit) + dmg
call SetWidgetLife(this.shieldUnit, life)
endif
else
//If the damage is less than the target's current life.
set this.shieldAmount = this.shieldAmount - dmg
//If there is still HP left of the shield, then...
if this.shieldAmount > 0 then
set life = currentLife + dmg
static if LIBRARY_TimerUtils then
set t = NewTimerEx(this)
set this.newLife = life
else
set t = CreateTimer()
call SaveInteger(ht, GetHandleId(t), 0, this)
set this.newLife = life
endif
call TimerStart(t, 0.00, false, function thistype.AdjustHP)
else
//Stop the duration timer
static if LIBRARY_TimerUtils then
call ReleaseTimer(this.shieldTimer)
else
call PauseTimer(this.shieldTimer)
call DestroyTimer(this.shieldTimer)
endif
set life = currentLife - RAbsBJ(this.shieldAmount)
static if LIBRARY_TimerUtils then
set t = NewTimerEx(this)
set this.newLife = life
else
set t = CreateTimer()
call SaveInteger(ht, GetHandleId(t), 0, this)
set this.newLife = life
endif
//Set this to true in order to to null "this.shieldUnit" in the timer callback
set this.b = true
call TimerStart(t, 0.00, false, function thistype.AdjustHP)
//Release memory
call DestroyEffect(this.shieldEff)
call FlushChildHashtable(ht, GetHandleId(trig))
call DestroyTrigger(trig)
set this.shieldTrig = null
set this.shieldEff = null
set this.shieldTimer = null
endif
endif
set trig = null
endmethod
static method Create takes unit u, real amout, real duration, string sfx, string attach returns thistype
local thistype this = thistype.allocate()
local timer t
local trigger trig = CreateTrigger()
call SaveInteger(ht, GetHandleId(trig), 0 , this)
static if LIBRARY_TimerUtils then
set this.shieldTimer = NewTimerEx(this)
call SetTimerData(this.shieldTimer, this)
else
set this.shieldTimer = CreateTimer()
call SaveInteger(ht, GetHandleId(this.shieldTimer), 0, this)
endif
//Registering damage event
call TriggerRegisterUnitEvent(trig, u, EVENT_UNIT_DAMAGED)
call TriggerAddCondition(trig, Condition(function thistype.DamageHandler))
//Storing data
set this.shieldUnit = u
set this.shieldAmount = amout
set this.shieldTrig = trig
set this.shieldDuration = duration
set this.shieldSfx = sfx
set this.shieldEff = AddSpecialEffectTarget(this.shieldSfx, this.shieldUnit, attach)
call TimerStart(this.shieldTimer, duration, false, function thistype.Remove)
set t = null
set trig = null
return this
endmethod
endstruct
function ShieldSetUp takes unit u, real amount, real duration, string sfx, string attach returns nothing
call ShieldSystem.Create(u, amount, duration, sfx, attach)
endfunction
endlibrary