• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

(DOT) Damage Over Interval Time. v3

Easy to use recourse that can handle hundreds of damage over time instances without any fps drops
The advantage of this system over other damage over time systems is that it can
deal damage as well as heal per interval of time (2,3,5... seconds)
Just use the function calls to register a unit that you want to be damaged the duration, interval, and damage amount and you are good to go.
Requires JassNewGenPack
JASS:
library DamageOverIntervalTime initializer Init /* v 3.00 by Gorillabul
************************************************************************************************************************************************************************************
* A simple system which   is able to deal damage over intervals of time, compared to other damage over time systems which deal
* damage every 0.01  second, dealing damage over an interval is idea for things like custom critical strike. Can also handle heals
* over time.  You can "stack" multiple instances on one unit however they will not be merged ... yet.
********************************************************************************************************************************************************************************
*/
globals
    private  unit array doitDamager                          //The Unit dealing the damage.
    private  unit array doitTarget                               // The target Unit
    private real array doitDamage                           //Amount of damage PER INTERVAL
    private  integer array doitCounting
    private integer array doitRecycler
    private integer array doitPrev
    private integer array doitNext
    private integer array  doitIterations                    //Number of times to add 1 to doitCurrentIteration ( interval/0.03125 )
    private integer array doitCurrentIteration          //Counting variable
    private integer array totalCurrentDoitIterations //Total iterations for an instance, if u specify a duration of 15 second and interval of 3 this is 5 (duration/interval)
    private integer  array doitTci                            //Used to generate a consecutive Damage over time instance.
    private timer doitTimer                                   //Timer to loop through the instances and pause if unused.
    private attacktype  array doitDamageTypeIndex // the attacktype that deals damage for each instance(hero fortified siege...) 
    private integer array doitInstanceIndex         //the attacktype index is set by the user when using ApplyDotEffect and is set to integer aid 
    /*
     set doitDamageTypeIndex[0] = ATTACK_TYPE_CHAOS 
    set doitDamageTypeIndex[1] = ATTACK_TYPE_MAGIC
    set doitDamageTypeIndex[2] = ATTACK_TYPE_NORMAL 
    set doitDamageTypeIndex[3] = ATTACK_TYPE_PIERCE
    set doitDamageTypeIndex[5] = ATTACK_TYPE_SIEGE 
    */ 
endglobals
/**********************************************************************************************************************************************************************************
*HOW TO USE-
*
*function  ApplyDotEffect takes unit damager , unit target , real damg , real duration , real interval, integer attacktypeindex returns nothing
*
*       -this function will register a unit to be damaged or healed over time, the names are self explanatory duration is how long
*         you want your target to be damaged for and interval is the timeout between dealing damage.
*         For example a unit takes damage every 3 seconds for 15 seconds would define an interval of 3 and a duration of 15.
*         The aid integer corresponds to doitInstanceIndex and determines the attacktype used per instance, look at how doitDamageTypeIndex
*         is set. 
*
*****************************************************************************************************************************************************************************
*
*
***********************************************************************************************************************************************************************
*HOW TO IMPORT
*
*Copy this trigger into your map, then  call ApplyDotEffect(caster, target, amount , duration , interval ) set amount to a negative value
* for healing over time.
***********************************************************************************************************************************************************************/
private function DoitLoop takes nothing  returns nothing
        local integer l = doitNext[0]
        
        if l == 0 then
            call PauseTimer(doitTimer)
        endif
        loop
                exitwhen l == 0
                set doitCurrentIteration[l]  =doitCurrentIteration[l] + 1
                if doitCurrentIteration[l] == doitIterations[l] then
                    set  doitCurrentIteration[l] = 0
                    set doitTci[l] = doitTci[l] + 1
                    if not IsUnitType(doitTarget[l], UNIT_TYPE_DEAD)   then
                        if doitDamage[l] > 0 then
                            call UnitDamageTarget( doitDamager[l] ,  doitTarget[l],  doitDamage[l] , true, false, doitDamageTypeIndex[doitInstanceIndex[l]], DAMAGE_TYPE_UNIVERSAL, null)
                        else
                            call SetWidgetLife(doitTarget[l], GetWidgetLife(doitTarget[l]) + (doitDamage[l] * -1 ))
                        endif
                    else
                        set doitNext[doitPrev[l]] = doitNext[l]
                        set doitPrev[doitNext[l]] = doitPrev[l]
                        set doitRecycler[l] = doitRecycler[0]
                        set doitRecycler[0] = l
                        set totalCurrentDoitIterations[l] = 0
                        set doitIterations[l] = 0
                        set doitTci[l] = 0
                    endif
                endif
                if doitTci[l] == totalCurrentDoitIterations[l] and IsUnitType(doitTarget[l], UNIT_TYPE_DEAD) ==false   then
                    set doitNext[doitPrev[l]] = doitNext[l]
                    set doitPrev[doitNext[l]] = doitPrev[l]
                    set doitRecycler[l] = doitRecycler[0]
                    set doitRecycler[0] = l
                    set totalCurrentDoitIterations[l] = 0
                    set doitIterations[l] = 0
                    set doitTci[l] = 0
                endif
                set l = doitNext[l]
        endloop
endfunction

function  ApplyDotEffect takes unit damager , unit target , real damg , real duration , real interval, integer attacktypeindex returns nothing
 local integer instance =  R2I(duration / interval )
local integer id = doitRecycler[0]

call TimerStart(doitTimer, 0.031250000, true , function DoitLoop )
    if  id == 0 then
        set doitCounting[0] = doitCounting[0]  + 1
        set id = doitCounting[0]
    else
        set doitRecycler[0]  = doitRecycler[id]
    endif
    set doitNext[id] = 0
    set doitPrev[id] = doitPrev[0]
    set doitNext[doitPrev[0]] = id
    set doitPrev[0] = id
    set  doitDamager[ id] = damager
    set  doitTarget[ id] = target
    set doitIterations[id] = R2I(interval / 0.03125)
    set totalCurrentDoitIterations[id] = R2I(instance)
    set  doitDamage[id] = damg /instance
    set doitInstanceIndex[id] = attacktypeindex 

endfunction




private function Init  takes nothing returns nothing
    local integer l = 0
    local trigger t = CreateTrigger(  )
    set doitRecycler[0]  = 0
    
    loop
            exitwhen l > 15
                set doitCounting   [l] = 0
                set l = l + 1
    endloop
    set doitDamageTypeIndex[0] = ATTACK_TYPE_CHAOS 
    set doitDamageTypeIndex[1] = ATTACK_TYPE_MAGIC
    set doitDamageTypeIndex[2] = ATTACK_TYPE_NORMAL 
    set doitDamageTypeIndex[3] = ATTACK_TYPE_PIERCE
    set doitDamageTypeIndex[5] = ATTACK_TYPE_SIEGE 
    set doitCounting[0] = 0
    set doitTimer = CreateTimer()
endfunction

endlibrary


Keywords:
damage over time, damage over, dot, damage per second, dots, noob, damage over interval, DAMAGE OVER TIME, DAMAGE PER SECOND, DPS, dps
Contents

Damage Over Interval Time (Map)

Reviews
Damage Over Interval Time. v3 | Reviewed by Maker | 28th May 2013 APPROVED The system works and it is MUI [tr] Fix the indentation Your doitDamageTypeIndex should be named doitAttackTypeIndex You could make the...

Moderator

M

Moderator


Damage Over Interval Time. v3 | Reviewed by Maker | 28th May 2013
APPROVED


126248-albums6177-picture66521.png


  • The system works and it is MUI
126248-albums6177-picture66523.png


  • Fix the indentation
  • Your doitDamageTypeIndex should be named doitAttackTypeIndex
  • You could make the attack and damage types configurable
  • Dead units could still get the credit from kills. Maybe check if the unit exists
    instead if it is dead
[tr]
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I think saying to JH how big array should be has no effect whatsoever, maybe it initializes them but I doubt that
why arent those globals private?
I cant see more then several globals because on pyone you cant scroll over the jass tag so thats all from me right now
 
JASS:
    unit array doit_Damager [8000]
    unit array doit_Target [8000]
    real array doit_Damage[8000]
    real array doit_Duration[8000]  
    integer array doit_Instance [8000]                                          //id of the current instance per player
    real array doit_Interval [8000]
    integer array doit_Counting   [8000]                                     //per player instance of number of running dots
    integer array doit_InstancePerInstance [8000]                    //HOV MANY INSTANCES EACH DOT HOLDS
    integer array doit_FreeIdValue [8000]                                 //  [instance id (doit_FreeidCount)]
    integer array doit_FreeIdCount [10]

You don't need to size these arrays.

The globals and functions should be private with the exception of the API.
(Everything in the library should be private except for the function that the user has to call)

FunctionNamesShouldBeWrittenLikeThis.

doit_One is not an appropriate name for a function.
Use something similar to Blizzard's natives like ApplyDamageOverTime

Don't use an InitTrig initialization function, use a library initializer:

JASS:
library Lib initializer Init

    private function Init takes nothing returns nothing
        // This code runs on map init.
    endfunction
endlibrary

This (if GetWidgetLife(doit_Target[instanceID]) <= 0.000 then) is not a valid death check.
Do this instead: if IsUnitType(u, UNIT_TYPE_DEAD) then

Why are you limiting the number of instances?
The system should rely on dynamic indexing to manage instances. You would also use some sort of list/stack.

This is how you would generate a unique id for array storage:
JASS:
local integer id = recycler[0] // recycler is an integer array.
if id == 0 then
    set instanceCount = instanceCount + 1 // instanceCount is an integer
    set id = instanceCount
else
    set recycler[0] = recycler[id]
endif
// id will be unique as of now.

After that, you'd want to insert it into your list.
You would manage a list with 2 arrays (next and prev, both integer arrays)

JASS:
// This is how you insert an instance into the list.
// After you generate id from above,
set next[id] = 0
set prev[id] = prev[0]
set next[prev[0]] = id
set prev[0] = id

// This is how you would remove the instance from the list when you're done with it
set next[prev[id]] = next[id]
set prev[next[id]] = prev[id]

After removing an instance from the list, you're going to want to deallocate that instance to your recycler.
This is how you'd do it:
JASS:
set recycler[id] = recycler[0]
set recycler[0] = id

Now as for the loop, this is how you would loop over all instances in a list:

JASS:
local integer instance = next[0] // next[0] is the first instance in the list

loop
    exitwhen instance == 0 // exit when the list is empty

    // use instance as an index for your arrays here

    set instance = next[instance] // go to the next instance on the list
endloop

This is what you would have to do basically:
- Generate unique id
- Add id to the list
- Set the data using id as an array index

When the instance is done,
- Remove id from the list
- Deallocate id
- Null data while using id as array index

One other thing I noticed is your reliance on one timer per instance.
Instead of having one timer per instance, you can have one timer for the system entirely.
It would work like this:

You would keep an integer called count to keep track of the number of instances running.
When a new instance is created, increase count by 1. If count is then 1, start the global timer to iterate over instances in the list.
When an instance is destroyed, decrease count by 1. If count is then 0, pause the global timer to stop iterating over the instances in the list.

This way, your system would be pretty efficient for large numbers of DamageOverTime instances. (Hundreds)
 
Last edited:
Level 29
Joined
Mar 10, 2009
Messages
5,016
- Lacks documentation
- Make your DoitLoop private
- Do not use TriggerRegisterTimerEvent, use timers so that if no more is using the system, pause the timer
- Use Timer systems such as TimerUtils or TimerTools
- Fix your indention

also, something like this and alike...
if IsUnitType(doitTarget[l], UNIT_TYPE_DEAD) ==false then
should be ==>
if not IsUnitType(doitTarget[l], UNIT_TYPE_DEAD) then
 
Level 33
Joined
Apr 24, 2012
Messages
5,113
Indention brother :D

0.03125 must be 0.31250000 because reals are not accurate enough.

Just like what mckill2009, putting == false makes it slower. just use not.But if you want a faster,use else(me and bowser found it was faster than "not")
You can also use GetWidgetLife(myWidget) > 0.405

You should use TriggerAddCondition over action because actions are slower due to that they create new thread.

You should also add support for the users to use their own Attack types and Damage Types. Simply using a damagetype and attacktype array will support their needs.

edit
Indention. Never use The_Witcher's Aligner because it uses keywords to indent.
This is how The_Witcher's indention works:
JASS:
//Example we have a word function here
                             Its indent like this
IsUnitType(doitTarget[l], UNIT_TYPE_DEAD) ==false
Also this. Just like what mckill said.

Also didn't read my suggestion,your system is not damage and attack type friendly.

You can also add "return" after the PauseTimer line,but that's not necessary.

You can also use Struct over functions.
 
Last edited:
Top