//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Retardation v1.1 //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Provides the unit with a restricting //
//~ protection, limiting the damage //
//~ he can suffer in a period of time. //
//~ //
//~ Level 1: 500 damage in 2 seconds. //
//~ Level 2: 450 damage in 3 seconds. //
//~ Level 3: 400 damage in 4 seconds. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ It's what the tooltip says. No matter what happens (okay... bugs occur) //
//~ it's impossible that the unit takes more then 500/450/400 damage //
//~ in 2/3/4 seconds. If there are very mighty neutrals, care that they can't //
//~ deal such an amount of damage, you'd be invulnurable. //
//~ //
//~ I though of it as a nuke-protection :) //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Utilities: //
//~ -TimerUtils //
//~ -InlinedHashtable //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
scope Retardation initializer InitRetard //No comment on this pls :D
globals
//The rawcode of "Retardation" [Spell]
private constant integer SPELLID = 'A000'
//The sfx is played whenever damage is absorbed (cap broken)
private constant string SFX = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl"
private constant string ATTACH = "chest"
//Please, don't touch these globals, they're needed for the spell
private trigger DAMAGED
private integer KEY
private unit TEMPUNIT
private real TEMPREAL
//The next two are combinations: if b_HEROCAP is true, only the hero damage will be counted
//for the cap. If false, creepdamage is included.
//If b_HEROABSORB is true, only the hero damage will be absorbed after reaching the cap.
//This means, that you could set b_HEROCAP to false but b_HEROABSORB to true.
//Now creeps would reach the cap to, but their damage isn't absorbed, making the spell
//against heros very strong if you're surrounded by creeps.
private constant boolean HEROCAP = false
private constant boolean HEROABSORB = false
endglobals
//The duration in which the damage must be done to reach the cap
private constant function GetDuration takes integer i_lvl returns real
return 2.+i_lvl
endfunction
//The cap. If more damage then this is done in the time from above, the damage is prevented
private constant function GetDamageCap takes integer i_lvl returns real
return 550.-i_lvl*50.
endfunction
//---------NO MORE TOUCHING PLS, I'M SHY! :P
//This struct is used to store all the damage that's done to the "caster"
//Each single piece of damage will be stored and reset after some time
private struct TempDamage
unit u_hero
real r_damage
static method Create takes unit u_hero, real r_damage returns TempDamage
local TempDamage s_dat = TempDamage.allocate()
set s_dat.u_hero = u_hero
set s_dat.r_damage = r_damage
return s_dat
endmethod
endstruct
//This function removes the damage out of the entire damage (from the cap)
//and releases the timer+struct instance
private function DecreaseDamage takes nothing returns nothing
local timer t_decrease = GetExpiredTimer()
local TempDamage s_dat = GetTimerData(t_decrease)
call SetHandleReal(s_dat.u_hero, KEY, GetHandleReal(s_dat.u_hero, KEY)-s_dat.r_damage)
call ReleaseTimer(t_decrease)
set s_dat.u_hero = null
call s_dat.destroy()
endfunction
//For the null timer
private function HealEx takes nothing returns nothing
call SetWidgetLife(TEMPUNIT, GetWidgetLife(TEMPUNIT)+TEMPREAL)
call ReleaseTimer(GetExpiredTimer())
endfunction
//Heal function, since normal "heal" with SetWidgetLife
//is done before the damage, causing damage at full life not to
//be healed -> null-timer.
private function Heal takes unit u_target, real r_amount returns nothing
local timer t_null
call DestroyEffect(AddSpecialEffectTarget(SFX, u_target, ATTACH))
if GetUnitState(u_target, UNIT_STATE_MAX_LIFE) - GetWidgetLife(u_target) < r_amount then
set t_null = NewTimer()
set TEMPUNIT = u_target
set TEMPREAL = r_amount
call TimerStart(t_null, 0., false, function HealEx)
else
call SetWidgetLife(u_target, GetWidgetLife(u_target)+r_amount)
endif
endfunction
//The function of the damage trigger, this registers every piece of damage
private function PreventDamage takes nothing returns boolean
local unit u_damaged
local timer t_decrease
local real r_damage
local real r_taken = GetEventDamage()
local integer i_level
local TempDamage s_dat
//Just to filter the millions of sources out that don't deal damage
//like AI-stuff and buffs
if r_taken <= 0 then
return false
endif
set u_damaged = GetTriggerUnit()
set t_decrease = NewTimer()
set r_damage = GetHandleReal(u_damaged, KEY)
set i_level = GetUnitAbilityLevel(u_damaged, SPELLID)
set s_dat = TempDamage.Create(u_damaged, r_taken)
static if HEROABSORB then
if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then
//If the cap is already reached, just heal
if r_damage >= GetDamageCap(i_level) then
call Heal(u_damaged, r_taken)
//If the cap will be reached with this damage,
//heal the cap-overflow
elseif r_damage+r_taken >= GetDamageCap(i_level) then
call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
endif
endif
else
//If the cap is already reached, just heal
if r_damage >= GetDamageCap(i_level) then
call Heal(u_damaged, r_taken)
//If the cap will be reached with this damage,
//heal the cap-overflow
elseif r_damage+r_taken >= GetDamageCap(i_level) then
call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
endif
endif
//Saving the damage and attaching it to the timer
static if HEROCAP then
if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then
call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
call SetTimerData(t_decrease, s_dat)
call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
endif
else
call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
call SetTimerData(t_decrease, s_dat)
call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
endif
set u_damaged = null
return false
endfunction
//Registers all units that get the skill
private function AddToDAMAGED takes nothing returns boolean
if GetLearnedSkill() == SPELLID and GetLearnedSkillLevel() == 1 then
call TriggerRegisterUnitEvent(DAMAGED, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
endif
return false
endfunction
private function InitRetard takes nothing returns nothing
local trigger t_init = CreateTrigger()
set DAMAGED = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t_init, Condition(function AddToDAMAGED))
call TriggerAddCondition(DAMAGED, Condition(function PreventDamage))
set KEY = StringHash("Retardation")
call RegisterStringHash("Retardation")
call Preload(SFX)
endfunction
endscope