//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
GC | gamecache | No |
//TESH.scrollpos=114
//TESH.alwaysfold=0
library CTL /* v1.1.0.0
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* CTL or Constant Timer Loop provides a loop for constant merged timers of timeout .03125
*
* Similar to T32 but pauses timer when no structs have instances and removes structs
* from timer trigger when those structs have no instances.
*
* This can also create new timers after destroying a previous timer and generates less
* code in the module. It also generates no triggers so long as the module is implemented
* at the top of the struct.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* static method create takes nothing returns thistype
* - CTL
* - Creates new timer
*
* method destroy takes nothing returns nothing
* - CTL
* - Destroys created timer
*
* Module
*
* module CTL
* - Declare locals in here
* - Run ini code
* module CTLExpire
* - Run timer code
* -
* - thistype this refers to current expiring timer\
* module CTLNull
* - Null locals here
* module CTLEnd
*
* Example of Constant Timer Loop 32
* struct MyTimer extends array
* integer myValue
* implement CTL
* local string s="My value is "
* implement CTLExpire
* call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(myValue))
* call destroy()
* implement CTLNull
* set s=null //pointless, but shows how to use null block
* implement CTLEnd
* endstruct
*
* set MyTimer.create().myValue=16 //will display "My value is 16" in 5 seconds
*
* module CT32
* - A constant running timer. Useful when the timer is pretty much never ever
* - going to stop. Also allows control over loop (just provides an expiring timer).
* - Code goes in between two methods
* module CT32End
*
* Example of Constant Timer 32
*
* //Displays
* // 1
* // 2
* // 3
* struct MyTimers extends array
* integer myValue
* thistype next
* implement CTL2
* local thistype this=thistype(0).next
* loop
* exitwhen 0==this
* call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(myValue))
* set this=next
* endloop
* implement CTL2End
* private static method onInit takes nothing returns nothing
* set thistype(0).next=1
* set thistype(1).next=2
* set thistype(2).next=3
* set thistype(1).myValue=1
* set thistype(2).myValue=2
* set thistype(3).myValue=3
* endmethod
* endstruct
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
globals
private integer ic=0 //instance count
private integer tc=0 //timer count
private integer array rf //root first
private integer array n //next
private integer array p //previous
private integer array th //timer head
private integer array ns //next stack
private trigger t=CreateTrigger()
private timer m=CreateTimer()
private triggercondition array ct
private conditionfunc array rc
endglobals
private function E takes nothing returns nothing
local integer i=ns[0]
set ns[0]=0
loop
exitwhen 0==i
if (0==p[i]) then
if (0==n[i]) then
call TriggerRemoveCondition(t,ct[th[i]])
set ct[th[i]]=null
set tc=tc-1
set rf[th[i]]=0
else
set rf[th[i]]=n[i]
set p[n[i]]=0
endif
else
set p[n[i]]=p[i]
set n[p[i]]=n[i]
endif
set n[i]=n[0]
set n[0]=i
set i=ns[i]
endloop
if (0==tc) then
call PauseTimer(m)
else
call TriggerEvaluate(t)
endif
endfunction
private function CT takes integer r returns integer
local integer i
local integer f
if (0==n[0]) then
set i=ic+1
set ic=i
else
set i=n[0]
set n[0]=n[i]
endif
set th[i]=r
set ns[i]=-1
set f=rf[r]
if (0==f) then
set n[i]=0
set p[i]=0
set rf[r]=i
set ct[r]=TriggerAddCondition(t,rc[r])
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
else
set n[i]=f
set p[i]=0
set p[f]=i
set rf[r]=i
endif
return i
endfunction
private function DT takes integer t returns nothing
debug if (0>ns[t]) then
set ns[t]=ns[0]
set ns[0]=t
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"TIMER LOOP ERROR: ATTEMPT TO DESTROY NULL TIMER")
debug endif
endfunction
private keyword r
private keyword e
module CTL
static integer r
static method create takes nothing returns thistype
return CT(r)
endmethod
method destroy takes nothing returns nothing
call DT(this)
endmethod
static method e takes nothing returns boolean
local thistype this=rf[r]
endmodule
module CTLExpire
loop
exitwhen 0==this
endmodule
module CTLNull
set this=n[this]
endloop
endmodule
module CTLEnd
return false
endmethod
private static method onInit takes nothing returns nothing
set r=ic+1
set ic=r
set rc[r]=Condition(function thistype.e)
endmethod
endmodule
module CT32
static method e takes nothing returns boolean
endmodule
module CT32End
return false
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(t,Condition(function thistype.e))
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// ********************************************************************************* //
// This spells requires the CTL library //
// ********************************************************************************* //
// ********************************************************************************* //
// HOW TO IMPLEMENT //
// ********************************************************************************* //
// //
// 1. Copy the Maledict Ability (object editor) //
// 2. Copy the Maledict Buff (object editor) //
// 3. Copy this trigger //
// 4. Enjoy :), but remember, this spell requires CTL //
// //
// ********************************************************************************* //
library Maledict requires CTL
globals
// ***************************************************************************** //
// CONFIGURABLES //
// ***************************************************************************** //
// Rawcode of the curse and the curse buff
private constant integer MALEDICT_RAWCODE = 'A001'
private constant integer MALEDICT_BUFF_RAWCODE = 'B000'
// @SPECIAL_EFFECT = Path of the special effect
// @EFFECT_ATTACHMENT = Position of the special effect
private constant string SPECIAL_EFFECT = "Abilities\\Spells\\Undead\\DeathPact\\DeathPactCaster.mdl"
private constant string EFFECT_ATTACHMENT = "overhead"
// @ONLY_HERO = If it is true, MALEDICT will only affect
// HERO type units
private constant boolean ONLY_HERO = false
// @DAMAGE_TYPE_EVERY_SECOND = Here you can choose what kind of damage
// the curse will do every second
private constant damagetype DAMAGE_TYPE_EVERY_SECOND = DAMAGE_TYPE_UNIVERSAL
// @DAMAGE_TYPE = Type of damage that the spell will do
// in the extra damage
// @ATTACK_TYPE = Type of attack that the spell will do
// in the extra damage and normal damage
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// Area of effect of the curse (do not touch the +50)
private constant real AoE = 165. + 50.
// @EXTRA_DAMAGE_PERIOD = Seconds that needs to pass to make the extra damage
private constant real EXTRA_DAMAGE_PERIOD = 4.
// @PERIODIC_DAMAGE = Seconds that needs to pass to make the periodic damage
private constant real PERIODIC_DAMAGE = 1.
// @PRELOAD = To preload the special effect
private constant boolean PRELOAD = true
// ***************************************************************************** //
// END CONFIGURABLES //
// ***************************************************************************** //
// @G = "Units" affected by the curse - DO NOT TOUCH THIS!
private constant group G = bj_lastCreatedGroup
endglobals
// ********************************************************************************* //
// CONFIGURABLE //
// ********************************************************************************* //
// Returns the extra damage
// ((HP OF THE TARGET WHEN ADQUIERE THE CURSE - ACTUAL LIFE OF TARGET) / 100) * (10 * LEVEL OF THE CURSE)
private function getExtraDamage takes unit target, real hp, integer level returns real
local real damage
set damage = ((hp - GetWidgetLife(target)) / 100) * (10. * level)
if damage < 0 then
set damage = 0
endif
return damage
endfunction
// Returns the periodic damage
private function getPeriodicDamage takes integer level returns real
return 5. * level
endfunction
// Returns the unit that can be affected by the curse
private function affectedUnit takes unit target, player p returns boolean
static if ONLY_HERO then
return IsUnitType(target, UNIT_TYPE_HERO) and IsUnitEnemy(target, p) and not(IsUnitType(target, UNIT_TYPE_DEAD)) and not(IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE))
else
return IsUnitEnemy(target, p) and not(IsUnitType(target, UNIT_TYPE_DEAD)) and not(IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE))
endif
endfunction
// ********************************************************************************* //
private keyword Init
private struct Spell extends array
private static integer instanceCount = 0
private static thistype recycle = 0
private thistype recycleNext
private unit caster // Caster
private unit target // Curse target
private real hp // HP of target
private integer level // Spell level
private real period // @period will serve to know if PERIODIC_DAMAGE seconds has passed
private real period_extraDamage // Same as period, but for the extra damage
implement CTL
implement CTLExpire
// If the target has the curse and is not dead...
if 0 < GetUnitAbilityLevel(this.target, MALEDICT_BUFF_RAWCODE) and not(IsUnitType(this.target, UNIT_TYPE_DEAD)) then
// Let's see if PERIODIC_DAMAGE seconds has passed
if PERIODIC_DAMAGE < this.period then
// If was, reset period to 0
set this.period = 0.
// Making the periodic damage (only if the unit
// is not inmmune to magic)
if not(IsUnitType(this.target, UNIT_TYPE_MAGIC_IMMUNE)) then
call UnitDamageTarget(this.caster, this.target, getPeriodicDamage(this.level), false, false, ATTACK_TYPE, DAMAGE_TYPE_EVERY_SECOND, null)
endif
else
// If was not, add the expired seconds to period (seconds
// of the CTL timer)
set this.period = this.period + 0.031250000
endif
// Let's see if the EXTRA_DAMAGE_PERIOD seconds has passed
if EXTRA_DAMAGE_PERIOD < this.period_extraDamage then
// If was, reset period_extraDamage to 0
set this.period_extraDamage = 0.
// Making the damage and special effect only if the
// damage is greater than 0
if getExtraDamage(this.target, this.hp, this.level) > 0 then
// Only make the damage if the target is not inmmune to magic
if not(IsUnitType(this.target, UNIT_TYPE_MAGIC_IMMUNE)) then
call DestroyEffect(AddSpecialEffectTarget(SPECIAL_EFFECT, this.target, EFFECT_ATTACHMENT))
call UnitDamageTarget(this.caster, this.target, getExtraDamage(this.target, this.hp, this.level), false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
endif
else
// If was not (the seconds has not passed)...
set this.period_extraDamage = this.period_extraDamage + 0.031250000
endif
else
// If the unit is dead or not has
// the buff...
set this.caster = null
set this.target = null
call this.destroy()
set recycleNext = recycle
set recycle = this
endif
implement CTLNull
implement CTLEnd
private static method run takes nothing returns boolean
local unit j
local thistype this
// If the ability being cast is the Rupture and the target can be affected
// (units that can be affected can be changed in the affectedUnit function)
if GetSpellAbilityId() == MALEDICT_RAWCODE then
call GroupEnumUnitsInRange(G, GetSpellTargetX(), GetSpellTargetY(), AoE, null)
loop
set j = FirstOfGroup(G)
exitwhen j == null
call GroupRemoveUnit(G, j)
if affectedUnit(j, GetTriggerPlayer()) then
set this = thistype.create()
if (recycle == 0) then
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = recycle
set recycle = recycle.recycleNext
endif
set this.caster = GetTriggerUnit()
set this.level = GetUnitAbilityLevel(this.caster, MALEDICT_RAWCODE)
set this.target = j
set this.hp = GetWidgetLife(j)
endif
endloop
endif
return false
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.run))
// Preload the special effect...
static if PRELOAD then
call Preload(SPECIAL_EFFECT)
endif
endmethod
endmodule
endlibrary