• 🏆 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!

DamageUnitOverTime

Level 5
Joined
Jan 18, 2007
Messages
59
This system allows u not only to damage units over time but also to create special effects and to make the damage non stackable! These are the features of the system

So Pros:
1) System uses structs and not cache
2) System allows u to create speciall effects
3) System allows u to make it non-stackable

Cons:
1) System uses JassNewgenPack
2) System requires CSData+CSSafety

JASS:
library DamageTargetOverTime uses CSSafety
private struct data
    unit c
    unit u
    timer t
    real dmg
    boolean attack
    boolean ranged
    damagetype DamageType
    attacktype AttackType
    weapontype WeaponType
    real runned
    real duration
    real howoften
    integer Buff
    string Effect
    string where
    string stack
    effect e
    
    static method create takes unit c, unit u, real dmg, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real duration, real howoften, string stack,integer Buff, string Effect, string where returns data
        local data dat = data.allocate()
        local data d = GetCSData(u)
        if stack!=null and stack==d.stack then
           call data.destroy(d)
        endif
        set dat.t = NewTimer()
        set dat.u = u
        set dat.c = c
        set dat.dmg = dmg
        set dat.attack=attack
        set dat.ranged=ranged
        set dat.AttackType=AttackType
        set dat.DamageType=DamageType
        set dat.WeaponType=WeaponType
        set dat.duration = duration
        set dat.howoften=howoften
        set dat.Buff=Buff
        set dat.Effect=Effect
        set dat.where=where
        set dat.stack=stack
        set dat.runned=0
        if (Effect!=null or Effect!="") and (where!=null or where!="") then
           set dat.e=AddSpecialEffectTarget(Effect,u,where)
        endif
        call SetCSData(dat.t,dat)
        call SetCSData(u,dat)
        return dat
    endmethod
    
    method onDestroy takes nothing returns nothing
    call DestroyEffect(.e)
    call ReleaseTimer(.t)
    endmethod
endstruct

private function Child takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local data dat = GetCSData(t)
    set dat.runned=dat.runned+dat.howoften
    
    if dat.runned>=dat.duration or GetUnitState(dat.u,UNIT_STATE_LIFE)<=.405 or (dat.Buff!=0 and GetUnitAbilityLevel(dat.u,dat.Buff)==0) then
        call data.destroy(dat)
    else
        call DestroyEffect(dat.e)
        set dat.e=AddSpecialEffectTarget(dat.Effect,dat.u,dat.where)
        call UnitDamageTarget(dat.c, dat.u, dat.dmg, dat.attack, dat.ranged, dat.AttackType, dat.DamageType, dat.WeaponType)
    endif
    
    set t = null
endfunction

function SetAttackType takes unit target, attacktype AttackType returns nothing
    local data dat = GetCSData(target)
    set dat.AttackType=AttackType
endfunction

function SetDamageType takes unit target, damagetype DamageType returns nothing
    local data dat = GetCSData(target)
    set dat.DamageType=DamageType
endfunction

function SetDamage takes unit target, real amount returns nothing
    local data dat = GetCSData(target)
    set dat.dmg=amount
endfunction

function SetDuration takes unit target, real duration returns nothing
    local data dat = GetCSData(target)
    set dat.duration=duration
endfunction

function SetEffect takes unit target, string Effect, string where returns nothing
    local data dat = GetCSData(target)
    set dat.Effect = Effect
    set dat.where = where
endfunction

function DamageOverBuffEx takes unit whichUnit, unit target, real amount, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real howoften,integer Buff,string stack, string Effect, string where returns nothing
    call TimerStart(data.create(whichUnit, target,amount,attack,ranged, AttackType, DamageType,WeaponType, 9999999, howoften,stack, Buff,Effect, where).t, howoften, true, function Child)
endfunction

function DamageOverBuff takes unit whichUnit, unit target, real amount, attacktype AttackType, damagetype DamageType, integer Buff, real howoften returns nothing
    call TimerStart(data.create(whichUnit, target,amount,true,false, AttackType, DamageType, null, 99999999, howoften,null,Buff,"","").t, howoften, true, function Child)
endfunction

function DamageOverTimeEx takes unit whichUnit, unit target, real amount, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real duration, real howoften,string stack, string Effect, string where returns nothing
    call TimerStart(data.create(whichUnit, target,amount,attack,ranged, AttackType, DamageType,WeaponType, duration, howoften,stack,0, Effect, where).t, howoften, true, function Child)
endfunction

function DamageOverTime takes unit whichUnit, unit target, real amount, attacktype AttackType, damagetype DamageType, real duration, real howoften returns nothing
    call TimerStart(data.create(whichUnit, target,amount,true,false, AttackType, DamageType, null, duration, howoften,null,0,"","").t, howoften, true, function Child)
endfunction

endlibrary
Download DamageUnitOverTime
 
Last edited:
Level 11
Joined
Feb 18, 2004
Messages
394
#1: Indent your code like a sane person:
JASS:
library DamageTargetOverTime uses CSSafety
private struct data
    unit c
    unit u
    timer t
    real dmg
    boolean attack
    boolean ranged
    damagetype DamageType
    attacktype AttackType
    weapontype WeaponType
    real runned
    real duration
    real howoften
    integer Buff
    string Effect
    string where
    string stack
    effect e
    
    static method create takes unit c, unit u, real dmg, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real duration, real howoften, string stack,integer Buff, string Effect, string where returns data
        local data dat = data.allocate()
        local data d = GetCSData(u)
        if stack!=null and stack==d.stack then
           call data.destroy(d)
        endif
        set dat.t = NewTimer()
        set dat.u = u
        set dat.c = c
        set dat.dmg = dmg
        set dat.attack=attack
        set dat.ranged=ranged
        set dat.AttackType=AttackType
        set dat.DamageType=DamageType
        set dat.WeaponType=WeaponType
        set dat.duration = duration
        set dat.howoften=howoften
        set dat.Buff=Buff
        set dat.Effect=Effect
        set dat.where=where
        set dat.stack=stack
        set dat.runned=0
        if (Effect!=null or Effect!="") and (where!=null or where!="") then
           set dat.e=AddSpecialEffectTarget(Effect,u,where)
        endif
        call SetCSData(dat.t,dat)
        call SetCSData(u,dat)
        return dat
    endmethod
    
    method onDestroy takes nothing returns nothing
    call DestroyEffect(.e)
    call ReleaseTimer(.t)
    endmethod
endstruct

private function Child takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local data dat = GetCSData(t)
    set dat.runned=dat.runned+dat.howoften
    
    if dat.runned>=dat.duration or GetUnitState(dat.u,UNIT_STATE_LIFE)<=.405 or (dat.Buff!=0 and GetUnitAbilityLevel(dat.u,dat.Buff)==0) then
        call data.destroy(dat)
    else
        call DestroyEffect(dat.e)
        set dat.e=AddSpecialEffectTarget(dat.Effect,dat.u,dat.where)
        call UnitDamageTarget(dat.c, dat.u, dat.dmg, dat.attack, dat.ranged, dat.AttackType, dat.DamageType, dat.WeaponType)
    endif
    
    set t = null
endfunction

function SetAttackType takes unit target, attacktype AttackType returns nothing
    local data dat = GetCSData(target)
    set dat.AttackType=AttackType
endfunction

function SetDamageType takes unit target, damagetype DamageType returns nothing
    local data dat = GetCSData(target)
    set dat.DamageType=DamageType
endfunction

function SetDamage takes unit target, real amount returns nothing
    local data dat = GetCSData(target)
    set dat.dmg=amount
endfunction

function SetDuration takes unit target, real duration returns nothing
    local data dat = GetCSData(target)
    set dat.duration=duration
endfunction

function SetEffect takes unit target, string Effect, string where returns nothing
    local data dat = GetCSData(target)
    set dat.Effect = Effect
    set dat.where = where
endfunction

function DamageOverBuffEx takes unit whichUnit, unit target, real amount, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real howoften,integer Buff,string stack, string Effect, string where returns nothing
    call TimerStart(data.create(whichUnit, target,amount,attack,ranged, AttackType, DamageType,WeaponType, 9999999, howoften,stack, Buff,Effect, where).t, howoften, true, function Child)
endfunction

function DamageOverBuff takes unit whichUnit, unit target, real amount, attacktype AttackType, damagetype DamageType, integer Buff, real howoften returns nothing
    call TimerStart(data.create(whichUnit, target,amount,true,false, AttackType, DamageType, null, 99999999, howoften,null,Buff,"","").t, howoften, true, function Child)
endfunction

function DamageOverTimeEx takes unit whichUnit, unit target, real amount, boolean attack, boolean ranged, attacktype AttackType, damagetype DamageType, weapontype WeaponType, real duration, real howoften,string stack, string Effect, string where returns nothing
    call TimerStart(data.create(whichUnit, target,amount,attack,ranged, AttackType, DamageType,WeaponType, duration, howoften,stack,0, Effect, where).t, howoften, true, function Child)
endfunction

function DamageOverTime takes unit whichUnit, unit target, real amount, attacktype AttackType, damagetype DamageType, real duration, real howoften returns nothing
    call TimerStart(data.create(whichUnit, target,amount,true,false, AttackType, DamageType, null, duration, howoften,null,0,"","").t, howoften, true, function Child)
endfunction

endlibrary

#2: By the shear number of parameters all those functions take, it is quite obvious you need to refactor.

#3: Your naming conventions are appalling:

JASS:
		unit c
	unit u
	timer t
	real dmg
	boolean attack
	boolean ranged
	damagetype DamageType
	attacktype AttackType
	weapontype WeaponType
	real runned
	real duration
	real howoften
	integer Buff
	string Effect
	string where
	string stack
	effect e

Absolutely NO consistency, and absolutely NO coherency!

#4: I now will show you the mental process it takes me to understand what the fuck you're doing with such poorly structured code:

unit c is the source unit

unit u is the target unit.

Those two alone are enough to fail you on decent coding...

timer t is a timer created with each instance, and used to do the actual time-based iteration.

real dmg slightly obviously the damage...

boolean attack boolean ranged damagetype DamageType attacktype AttackType weapontype WeaponType Obvious to anyone who's used UnitDamageTarget before...

real runned ... Usually I don't criticize code that gets underlined in my spell checker. But I have to make an exception here. That is not a word. The variable is used to count the time a timer has been going.

real duration How long the damage will last

real howoften How often the damage will occur

Your API already sucks right from how you use those 3 variables. Instead of actually working with the underlying mechanic, and allowing users to specify something useful, like a count of "ticks" and the duration of a "tick", you work against the underlying mechanic and force the coder to have to do math in order to calculate the number of times your system will run.

integer Buff if tracking a buff, this is set to the buff ID to check for. When creating a data object for a buff-based DoT, the duration is set to 9999999.... which is failure of logic on your part. Duration-based data should only be collected when tied to a buff, not compared against for an exit condition...

string Effect string where What effect and where to place it on the unit... The lack of flexibility sucks.

string stack I really can't even see the point of this variable... Really... a string?!

effect e the last created effect, with an effect being created at the start of the DoT...

The biggest problem with your code is that it's doing way too much from single function calls, with little to no flexibility. Your implementation is poor, your API overly complex for the tiny bit of work the system actually does, and your code is nigh unreadable without significant investment of time.

A much better system could be devised, taking advantage of vJASS' interfaces, either the object or function variety, to do actual period-based execution of arbitrary code tied to a unit with or without a buff, over a runtime-changeable duration. Such a system, especially using struct inheritance, could simplify the API immeasurably for the end user.

Overall, I give this 3/5 WTFs.
 
Top