• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Over Time Effect System by xD.Schurke

Level 16
Joined
Feb 22, 2006
Messages
960
I saw some damage over time system, so i wanted to create my own. The user has much more power over the effect. Actually there are 4 diffrent OverTime effects.

Damage
DamageAoe
State
StateAoe

so look into it ;)

The system requires TimerUtils by Vexorian so also give credits to him

TimerUtils:
JASS:
library_once TimerUtils
//*********************************************************************
//* TimerUtils (Blue flavor)
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//*             limit, which means that if more than 408000 handle ids
//*             are used in your map, TimerUtils might fail, this
//*             value is quite big and it is much bigger than the 
//*             timer limit in Red flavor.
//*
//********************************************************************

//================================================================
    globals
        private constant integer MAX_HANDLE_ID_COUNT = 408000
        // values lower than 8191: very fast, but very unsafe.
        // values bigger than 8191: not that fast, the bigger the number is the slower the function gets
        // Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it
        // as the rather inflated value of 408000
    endglobals

    //=================================================================================================
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction

    //==================================================================================================
    globals
        private integer array data[MAX_HANDLE_ID_COUNT]
        private constant integer MIN_HANDLE_ID=0x100000
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        debug if(H2I(t)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then
        debug     call BJDebugMsg("SetTimerData: Handle id too big, increase the max handle id count or use gamecache instead")
        debug endif
        set data[H2I(t)-MIN_HANDLE_ID]=value
    endfunction

    function GetTimerData takes timer t returns integer
        debug if(H2I(t)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then
        debug     call BJDebugMsg("GetTimerData: Handle id too big, increase the max handle id count or use gamecache instead")
        debug endif
        return data[H2I(t)-MIN_HANDLE_ID]
    endfunction

    //==========================================================================================
    globals
        private timer array tT
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (tN==0) then
            set tT[0]=CreateTimer()
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],0)
     return tT[tN]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==8191) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif    
    endfunction

endlibrary

Main System:
JASS:
//********************************************************************
//* OverTimeEffectSystem [OTES] v1.1
//*
//* By xD.Schurke
//*
//*
//* changelog:
//* 
//*  v1.1
//*     - Fixed a bug with AOE Effects
//*     - Added Test Ability for AOE Damage
//********************************************************************
library OTES requires TimerUtils
globals
    private constant real MIN_TIMER_TIME = 0.035 //lower then 0.035 will lead to a lower performance
    
    //===========DO NOT CHANGE ANYTHING BELOW THIS LINE=================
    private group tmpG = CreateGroup()
endglobals

private struct OverTimeEffect
    unit caster
    unit tar
    timer t
    real interval = 0.
    real duration = 0.
    real curdur = 0.
    real value = 0.
    string sfx = ""
    string attach = ""
    integer style = 0
    boolean pereodic = false
    damagetype dt = null
    unitstate us = null
    real x = 0.
    real y = 0.
    real radius = 0.
    group g = CreateGroup()
    boolexpr filter = null
    boolean pick = true
        static method oteffect takes nothing returns nothing
            local OverTimeEffect Dat = GetTimerData(GetExpiredTimer())
            local real us = 0
            
            if Dat.pereodic then
                set Dat.curdur = Dat.curdur + Dat.interval
                if Dat.style == 0 then
                    call UnitDamageTarget(Dat.caster,Dat.tar,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)                        
                else
                    set us = GetUnitState(Dat.tar,Dat.us)
                    call SetUnitState(Dat.tar,Dat.us,us+Dat.value)
                endif
                call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,Dat.tar,Dat.attach))
                if Dat.curdur >= Dat.duration or GetUnitState(Dat.tar,UNIT_STATE_LIFE) <= 0 then
                    call OverTimeEffect.destroy(Dat)
                endif
            else
                if Dat.style == 0 then
                    call UnitDamageTarget(Dat.caster,Dat.tar,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)                        
                else
                    set us = GetUnitState(Dat.tar,Dat.us)
                    call SetUnitState(Dat.tar,Dat.us,us+Dat.value)
                endif
                call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,Dat.tar,Dat.attach))    
                call OverTimeEffect.destroy(Dat)
            endif            
        endmethod
        
        static method oteffectaoe takes nothing returns nothing
            local OverTimeEffect Dat = GetTimerData(GetExpiredTimer())
            local real us = 0
            local unit u
            if Dat.pereodic then
                set Dat.curdur = Dat.curdur + Dat.interval                
                if Dat.pick !=true then                    
                    if Dat.style == 0 then                     
                        loop
                            set u = FirstOfGroup(Dat.g)
                            exitwhen u == null
                            call UnitDamageTarget(Dat.caster,u,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)
                            call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))
                            call GroupAddUnit(tmpG,u)
                            call GroupRemoveUnit(Dat.g,u)
                            set u = null
                        endloop
                        loop
                            set u = FirstOfGroup(tmpG)
                            exitwhen u == null
                            call GroupAddUnit(Dat.g,u)
                            call GroupRemoveUnit(tmpG,u)
                            set u = null
                        endloop
                    else
                        loop
                            set u = FirstOfGroup(Dat.g)
                            exitwhen u == null
                            set us = GetUnitState(u,Dat.us)
                            call SetUnitState(u,Dat.us,us+Dat.value)
                            call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))
                            call GroupAddUnit(tmpG,u)
                            call GroupRemoveUnit(Dat.g,u)
                            set u = null
                        endloop
                        loop
                            set u = FirstOfGroup(tmpG)
                            exitwhen u == null
                            call GroupAddUnit(Dat.g,u)
                            call GroupRemoveUnit(tmpG,u)
                            set u = null
                        endloop                        
                    endif
                else
                    call GroupEnumUnitsInRange(tmpG,Dat.x,Dat.y,Dat.radius,Dat.filter)
                    loop
                        set u = FirstOfGroup(tmpG)
                        exitwhen u == null
                        if Dat.style == 0 then                     
                            call UnitDamageTarget(Dat.caster,u,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)
                            call GroupRemoveUnit(tmpG,u)
                        else
                            set us = GetUnitState(u,Dat.us)
                            call SetUnitState(u,Dat.us,us+Dat.value)
                            call GroupRemoveUnit(tmpG,u)
                        endif
                        call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))
                        set u = null
                    endloop
                endif
                if Dat.curdur >= Dat.duration then
                    call OverTimeEffect.destroy(Dat)
                endif                
            else
                if Dat.pick !=true then
                    set tmpG = Dat.g
                    if Dat.style == 0 then                     
                        loop
                            set u = FirstOfGroup(tmpG)
                            exitwhen u == null
                            call UnitDamageTarget(Dat.caster,u,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)
                            call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))   
                            call GroupRemoveUnit(tmpG,u)
                            set u = null
                        endloop
                    else
                        loop
                            set u = FirstOfGroup(tmpG)
                            exitwhen u == null
                            set us = GetUnitState(u,Dat.us)
                            call SetUnitState(u,Dat.us,us+Dat.value)
                            call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))                            
                            call GroupRemoveUnit(tmpG,u)
                            set u = null
                        endloop 
                    endif
                else
                    call GroupEnumUnitsInRange(tmpG,Dat.x,Dat.y,Dat.radius,Dat.filter)
                    loop
                        set u = FirstOfGroup(tmpG)
                        exitwhen u == null
                        if Dat.style == 0 then                     
                            call UnitDamageTarget(Dat.caster,u,Dat.value,false,false,ATTACK_TYPE_NORMAL,Dat.dt,null)
                            call GroupRemoveUnit(tmpG,u)
                        else
                            set us = GetUnitState(u,Dat.us)
                            call SetUnitState(u,Dat.us,us+Dat.value)
                            call GroupRemoveUnit(tmpG,u)
                        endif
                        call DestroyEffect(AddSpecialEffectTarget(Dat.sfx,u,Dat.attach))
                        set u = null
                    endloop
                endif
                call OverTimeEffect.destroy(Dat)
            endif
        endmethod
        
        static method createDamage takes unit caster, unit target,real duration,boolean pereodic,real interval,real value,boolean ConsiderArmor,string sfx,string attach returns OverTimeEffect
            local OverTimeEffect Dat = OverTimeEffect.allocate()
            local real time = 0
            if duration < MIN_TIMER_TIME or caster == null or target == null then
                if pereodic and interval < MIN_TIMER_TIME then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5.,"The duration or interval can't be less then 0.03 seconds!")
                    call OverTimeEffect.destroy(Dat)
                    return 0
                endif
            endif
            if ConsiderArmor then
                set Dat.dt = DAMAGE_TYPE_NORMAL
            else
                set Dat.dt = DAMAGE_TYPE_UNIVERSAL
            endif            
            set Dat.style = 0            
            set Dat.caster = caster
            set Dat.tar = target
            set Dat.sfx = sfx
            set Dat.attach = attach
            set Dat.value = value
            set Dat.t = NewTimer()
            set Dat.duration = duration
            set Dat.interval = interval             
            if pereodic then
                set Dat.pereodic = pereodic
                set time = Dat.interval
            else
                set time = Dat.duration
            endif
            call SetTimerData(Dat.t,Dat)
            call TimerStart(Dat.t,time,pereodic,function OverTimeEffect.oteffect)
            return Dat
        endmethod
        
        static method createDamageAoe takes unit caster, real x,real y,real radius,boolexpr filter,boolean b, real duration,boolean pereodic,real interval,real value,boolean ConsiderArmor,string sfxpath,string attach returns OverTimeEffect
            local OverTimeEffect Dat = OverTimeEffect.allocate()
            local real time = 0
            if duration < MIN_TIMER_TIME or caster == null then
                if pereodic and interval < MIN_TIMER_TIME then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5.,"The duration or interval can't be less then 0.03 seconds!")
                    call OverTimeEffect.destroy(Dat)
                    return 0
                endif
            endif
            if ConsiderArmor then
                set Dat.dt = DAMAGE_TYPE_NORMAL
            else
                set Dat.dt = DAMAGE_TYPE_UNIVERSAL
            endif            
            if b != true then
                set Dat.pick = b
                call GroupEnumUnitsInRange(Dat.g,x,y,radius,filter)        
            endif
            set Dat.style = 0
            set Dat.caster = caster
            set Dat.x = x
            set Dat.y = y
            set Dat.radius = radius
            set Dat.filter = filter
            set Dat.t = NewTimer()
            set Dat.duration = duration
            set Dat.interval = interval
            set Dat.value = value
            set Dat.sfx = sfxpath
            set Dat.attach = attach
            if pereodic then
                set Dat.pereodic = pereodic
                set time = Dat.interval
            else
                set time = Dat.duration
            endif
            call SetTimerData(Dat.t,Dat)
            call TimerStart(Dat.t,time,pereodic,function OverTimeEffect.oteffectaoe)            
            return Dat
        endmethod
        
        static method createState takes unit target,real duration,boolean pereodic,real interval,unitstate us,real value,string sfx,string attach returns OverTimeEffect
            local OverTimeEffect Dat = OverTimeEffect.allocate()
            local real time = 0
            if duration < MIN_TIMER_TIME or target == null or us == null then
                if pereodic and interval < MIN_TIMER_TIME then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5.,"The duration or interval can't be less then 0.03 seconds!")
                    call OverTimeEffect.destroy(Dat)
                    return 0
                endif
            endif
            set Dat.style = 1            
            set Dat.us = us
            set Dat.tar = target
            set Dat.sfx = sfx
            set Dat.attach = attach
            set Dat.value = value
            set Dat.t = NewTimer()
            set Dat.duration = duration
            set Dat.interval = interval             
            if pereodic then
                set Dat.pereodic = pereodic
                set time = Dat.interval
            else
                set Dat.duration = duration
                set time = Dat.duration
            endif
            call SetTimerData(Dat.t,Dat)
            call TimerStart(Dat.t,time,pereodic,function OverTimeEffect.oteffect)            
            return Dat
        endmethod

        static method createStateAoe takes real x,real y,real radius,boolexpr filter,boolean b, real duration,boolean pereodic,real interval,real value,unitstate us,string sfxpath,string attach returns OverTimeEffect
            local OverTimeEffect Dat = OverTimeEffect.allocate()
            local real time = 0
            if duration < MIN_TIMER_TIME or us == null then
                if pereodic and interval < MIN_TIMER_TIME then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5.,"The duration or interval can't be less then 0.03 seconds!")
                    call OverTimeEffect.destroy(Dat)
                    return 0
                endif
            endif         
            if b != true then
                set Dat.pick = b
                call GroupEnumUnitsInRange(Dat.g,x,y,radius,filter)        
            endif
            set Dat.style = 1
            set Dat.x = x
            set Dat.y = y
            set Dat.radius = radius
            set Dat.filter = filter
            set Dat.t = NewTimer()
            set Dat.duration = duration
            set Dat.interval = interval
            set Dat.value = value
            set Dat.sfx = sfxpath
            set Dat.attach = attach
            set Dat.us = us
            if pereodic then
                set Dat.pereodic = pereodic
                set time = Dat.interval
            else
                set time = Dat.duration
            endif
            call SetTimerData(Dat.t,Dat)
            call TimerStart(Dat.t,time,pereodic,function OverTimeEffect.oteffectaoe)            
            return Dat
        endmethod        
        
        method onDestroy takes nothing returns nothing
            call ReleaseTimer(.t)
            set .caster = null
            set .tar = null
            set .t = null
            set .dt = null
            set .us = null
            call DestroyGroup(.g)
            set .g = null
            set .filter = null
        endmethod
endstruct

function OverTimeDamage takes unit caster, unit target,real duration,boolean pereodic,real interval,real value,boolean ConsiderArmor,string sfx,string attach returns nothing
    call OverTimeEffect.createDamage(caster,target,duration,pereodic,interval,value,ConsiderArmor,sfx,attach)
endfunction

function OverTimeState takes unit target,real duration,boolean pereodic,real interval,unitstate us,real value,string sfx,string attach returns nothing
    call OverTimeEffect.createState(target,duration,pereodic,interval,us,value,sfx,attach)
endfunction

function OverTimeDamageAoe takes unit caster, real x,real y,real radius,boolexpr filter,boolean b, real duration,boolean pereodic,real interval,real value,boolean ConsiderArmor,string sfxpath,string attach returns nothing
    call OverTimeEffect.createDamageAoe(caster,x,y,radius,filter,b,duration,pereodic,interval,value,ConsiderArmor,sfxpath,attach)
endfunction

function OverTimeStateAoe takes real x,real y,real radius,boolexpr filter,boolean b, real duration,boolean pereodic,real interval,real value,unitstate us,string sfxpath,string attach returns nothing
    call OverTimeEffect.createStateAoe(x,y,radius,filter,b,duration,pereodic,interval,value,us,sfxpath,attach)
endfunction
endlibrary

so first the 4 diffrent calls:

DamageOverTime Single Target:
call OverTimeDamage(<unit caster>,<unit target>,<real duration>,<boolean pereodic>,<real interval>,<real damage>,<boolean considerArmor>,<string effectpath>,<string effect attach>)

example:
call OverTimeDamage(udg_u, udg_t,3.,false,0.,200,true,"Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl","origin")

DamageOverTime AOE:
call OverTimeDamageAoe(<unit caster>,<real x>,<real y>,<real radius>,<boolexpr filter>,<boolean refresh or not>,< real duration>,<boolean pereodic>,<real interval>,<real value>,<boolean ConsiderArmor>,<string sfxpath>,<string attach>)

example:
call OverTimeDamageAoe(udg_t,GetLocationX(GetSpellTargetLoc()),GetLocationY(GetSpellTargetLoc()),400,Condition(function filter),false,10,true,2,100,true,"Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl","origin")

StateOverTime Single Target:
call OverTimeState(<unit caster>,<unit target>,<real duration>,<boolean pereodic>,<real interval>,<unitstate>,<real value>,<string effectpath>,<string effect attach>)

example:
call OverTimeState(udg_u, udg_t,5.,true,1.,UNIT_STATE_LIFE,100,"Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl","origin")

StateOverTime AOE:
call OverTimeStateAoe(<real x>,<real y>,<real radius>,<boolexpr filter>,<boolean refresh or not>, <real duration>,<boolean pereodic>,<real interval>,<real value>,<unitstate>,<string sfxpath>,<string attach>)

example:
call OverTimeStateAoe(GetLocationX(GetSpellTargetLoc()),GetLocationY(GetSpellTargetLoc()),400,Condition(function filter),true, 10,true,1,25,UNIT_STATE_MANA,"Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl","chest")


So pls give feedback and credits if you use this system ;)
 

Attachments

  • OverTimeEffectSystem.w3x
    29.6 KB · Views: 58
Last edited:
Top