HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

DOT - Damage Over Time

02-28-2008, 09:45 AM#1
cr4xZz
v 1.1

This was a submitted system in the resource forum but because of the lot of limitations
it has it was suggested to be moved here, so I can improve it until it is finally ready for another try.
This is a DOT system that allows you to damage a unti over time if it has a specific buff.
As for now, you can read how it works in the implementation instruction.
I need your ideas so this can be improved, stretchened, or whatever you want it,
so users can use it freely and with ease. Reasonable suggestions will be considered
and if are useful enough I'll try to add them if they fit in my jass abilities (I'm not a very skilled jasser).

Requires:
- JASS NewGen Editor
- ABCT v2.0 (or higher)
- PUI v4.2 (or higher)


Implementation Instructions:
Collapse JASS:
//====================================================================== ========
// Damage Over Time System [v 1.1] by cr4xzZz
//====================================================================== ========

' PURPOSE:

- Creating damage over time spells the easy way
- Save time on damage over time spells

' HOW IT WORKS:

- When you call the start function ( DOT_Start() ) 
a periodic timer will run every 0.1 seconds for 
5 seconds, checking each period if the target
has the assigned buff you chose. If in that time
the target is applied the buff then DOT starts
with all the values you used. If the target does 
not recieve that buff on time (5 secs) then the
function is cancelled. These values can be
changed to satisfy your needs (look for a 
comment in the globals in the DOT trigger). 
- After the normal damaging starts the system
checks if the target has the assigned buff 
each time before it damages it. If the target
does not have that buff then the damaging
stops. The system stops if the buff is dispelled,
too (obviously). 
- The system attaches all values to array globals
thanks to PUI. That way they can easily be used
for Set and Get functions.

' HOW TO USE:

- call DOT_Start( DAMAGER, TARGET, PERIOD, DURATION, BUFF, DAMAGE, ATTACK TYPE, DAMAGE TYPE, MELEE?, RANGED?, EFFECT, ATTACHMENT POINT)
* DAMAGER - unit; The unit that is going to damage something
* TARGET - unit; The target unit that is going to be damaged
* PERIOD - real; Period per damaging 
* DURATION - real; Maximum duration of the damaging
* BUFF - integer; The buff that is required for the damaging to work
// raw code !
//! IF THE TARGET DOES NOT HAVE THAT
//! BUFF THEN THE DAMAGING STOPS
* DAMAGE - real; The damage dealt per each period
//! NOTE THAT THIS IS NOT THE MAX DAMAGE
* ATTACK TYPE - attacktype; The attack type of the damaging
* DAMAGE TYPE - damagetype; The damage type of the damaging
* MELEE? - boolean; If it is a melee type damage 
* RANGED? - boolean; If it i s a ranged type damage
// these two booleans must be true or false
* EFFECT - string; Special effect string
* ATTACHMENT POINT - string; The attachment point of the special effect

' OTHER FUNCTIONS:

//! Note that these functions work only on the TARGET
//! and NOT on the DAMAGER ! This means that the
//! unit argument should be a TARGET that has DOT 
//! on it.

- call DOT_SetDamage(unit TARGET, real AMOUNT)
- call DOT_SetDuration(unit TARGET, integer AMOUNT)
- call DOT_SetAttackType(unit TARGET, attacktype ATTACK TYPE)
- call DOT_SetDamageType(unit TARGET, damagetype DAMAGE TYPE)
// this must be true or false
- call DOT_SetMelee(unit TARGET, boolean MELEE?)
// this must be true or false
- call DOT_SetRange(unit TARGET, boolean RANGED?)
- call DOT_SetEffect(unit TARGET, string EFFECT)
- call DOT_SetAttachmentPoint(unit TARGET, string ATTACHMENT POINT)


- call DOT_GetDamage(unit TARGET) -> real AMOUNT
- call DOT_GetDuration(unit TARGET) -> integer AMOUNT
- call DOT_GetAttackType(unit TARGET) -> attacktype ATTACK TYPE
- call DOT_GetDamageType(unit TARGET) -> damagetype DAMAGE TYPE
- call DOT_GetMelee(unit TARGET) -> boolean MELEE?
- call DOT_GetRange(unit TARGET) -> boolean RANGED?
- call DOT_GetEffect(unit TARGET) -> string EFFECT
- call DOT_GetAttachmentPoint(unit TARGET) -> string ATTACHMENT POINT

- call DOT_Stop(unit TARGET) // stop all assigned DOT functions
- call DOT_AddDuration(unit TARGET, integer AMOUNT) // add more duration to the damaging

' PROS:
- Easy to use 
- A lot customizeable
- Can be fully controlled
' CONS:
- None? (cannot think of any...)

' HOW TO IMPLEMENT: 
- Copy the ABCT and PUI systems if you
do not have them
- Copy the DOT trigger in your map

' CREDITS TO:
- Cohadar for ABCT v2.0 and PUI v4.2

' CHANGELOG:
- v1.0: First release
- v1.1: No changes at all. Just uses the newest version of ABCT (v 2.0)

Code:
Collapse JASS:
//==============================================================================
//        Damage Over Time System [v 1.1] by cr4xzZz
//==============================================================================


library DOT uses ABCT, PUI

globals
    private real array DPP
    private integer array DURATION
    private attacktype array ATTACK
    private damagetype array DAMAGE
    private string array EFFECT
    private string array POINT
    private boolean array MELEE
    private boolean array RANGE
// - Can be configured -
    private constant real CHECK_PERIOD = 0.1 //period for buff checkup
    private constant real MAX_CHECK_TIME = 5. //max wait time for buff checkup
endglobals
// - DO NOT change anything below this line !!! -

private struct SysStruct
    unit damager
    unit target
    real period
    integer buffz
    integer temp
    timer clock = CreateTimer()
    
    static method create takes unit whichUnit, unit whichTarg, integer whichBuff, real whichPeriod returns SysStruct
        local SysStruct data = SysStruct.allocate()
        set data.damager = whichUnit
        set data.target = whichTarg
        set data.buffz = whichBuff
        set data.period = whichPeriod
        set data.temp = R2I(MAX_CHECK_TIME / CHECK_PERIOD)
        return data
    endmethod

    method onDestroy takes nothing returns nothing
        set DURATION[GetUnitIndex(.target)] = 0
        call PauseTimer(.clock)
        call DestroyTimer(.clock)
    endmethod
endstruct

// - - - - - SET FUNCTIONS - - - - -
public function SetDamage takes unit whichUnit, real whichDmg returns nothing
    set DPP[GetUnitIndex(whichUnit)] = whichDmg
endfunction

public function SetDuration takes unit whichUnit, integer whichInt returns nothing
    if whichInt > 0 then
        set DURATION[GetUnitIndex(whichUnit)] = whichInt
    else    
        //debug call BJDebugMsg("|cffffcc00[DOT] -|r Cannot use <= 0 in SetDuration(). Use Stop() instead")
    endif
endfunction

public function SetAttackType takes unit whichUnit, attacktype whichAttackType returns nothing
    set ATTACK[GetUnitIndex(whichUnit)] = whichAttackType 
endfunction

public function SetDamageType takes unit whichUnit, damagetype whichDamageType returns nothing
    set DAMAGE[GetUnitIndex(whichUnit)] = whichDamageType
endfunction

public function SetMelee takes unit whichUnit, boolean whichBool returns nothing
    set MELEE[GetUnitIndex(whichUnit)] = whichBool
endfunction

public function SetRanged takes unit whichUnit, boolean whichBool returns nothing
    set RANGE[GetUnitIndex(whichUnit)] = whichBool
endfunction

public function SetEffect takes unit whichUnit, string whichFx returns nothing
    set EFFECT[GetUnitIndex(whichUnit)] = whichFx
endfunction

public function SetAttachmentPoint takes unit whichUnit, string whichPoint returns nothing
    set POINT[GetUnitIndex(whichUnit)] = whichPoint
endfunction

// = = = = = = = = = = = = = = = = = = = = = = = = = =
// - - - - - GET FUNCTIONS - - - - -
public function GetDamage takes unit whichUnit returns real
    return DPP[GetUnitIndex(whichUnit)] 
endfunction

public function GetDuration takes unit whichUnit returns integer
    return DURATION[GetUnitIndex(whichUnit)]
endfunction

public function GetAttackType takes unit whichUnit returns attacktype
    return ATTACK[GetUnitIndex(whichUnit)]
endfunction

public function GetDamageType takes unit whichUnit returns damagetype
    return DAMAGE[GetUnitIndex(whichUnit)]
endfunction

public function GetMelee takes unit whichUnit returns boolean
    return MELEE[GetUnitIndex(whichUnit)]
endfunction

public function GetRange takes unit whichUnit returns boolean
    return RANGE[GetUnitIndex(whichUnit)]
endfunction

public function GetEffect takes unit whichUnit returns string
    return EFFECT[GetUnitIndex(whichUnit)]
endfunction

public function GetAttachmentPoint takes unit whichUnit returns string
    return POINT[GetUnitIndex(whichUnit)]
endfunction
// = = = = = = = = = = = = = = = = = = = = = = = = = =

public function Stop takes unit whichUnit returns nothing
    set DURATION[GetUnitIndex(whichUnit)] = 0
    //debug call BJDebugMsg("|cffffcc00[DOT] -|r DOT stops")
endfunction

public function AddDuration takes unit whichUnit, integer whichInt returns nothing
    set DURATION[GetUnitIndex(whichUnit)] = DURATION[GetUnitIndex(whichUnit)] + whichInt
endfunction

private function Damage takes nothing returns boolean
    local SysStruct data = ABCT_GetData()
    local integer pui = GetUnitIndex(data.target)
    if GetWidgetLife(data.target) >= 0.405 and GetUnitAbilityLevel(data.target, data.buffz) > 0 and DURATION[pui] > 0 then
        call UnitDamageTarget(data.damager, data.target, DPP[pui], MELEE[pui], RANGE[pui], ATTACK[pui], DAMAGE[pui], null)
        call DestroyEffect(AddSpecialEffectTarget(EFFECT[pui], data.target, POINT[pui]))
    else
        call data.destroy()
        //debug call BJDebugMsg("|cffffcc00[DOT] -|r DOT finishes")
        return true
    endif
    set DURATION[pui] = DURATION[pui] - 1
    return false
endfunction

private function Check takes nothing returns boolean
    local SysStruct data = ABCT_GetData()
    if GetUnitAbilityLevel(data.target, data.buffz) > 0 then
        call ABCT_Start(function Damage, data, data.period)
        //debug call BJDebugMsg("|cffffcc00[DOT] -|r DOT starts")
        return true
    endif
    set data.temp = data.temp - 1
    if data.temp <= 0 then 
        call data.destroy()
        //debug call BJDebugMsg("|cffffcc00[DOT] -|r DOT cancelled")
        return true
    endif
    return false
endfunction

public function Start takes unit whichUnit, unit whichTarg, real whichPeriod, real whichDur, integer whichBuff, real whichDmg, attacktype whichAttackType, damagetype whichDamageType, boolean isMelee, boolean isRange, string whichFx, string whichPoint returns nothing
    local SysStruct data = SysStruct.create(whichUnit, whichTarg, whichBuff, whichPeriod)
    local integer pui = GetUnitIndex(data.target)
    set DPP[pui] = whichDmg
    set DURATION[pui] = DURATION[pui] + R2I((1. / whichPeriod) * whichDur)
    set ATTACK[pui] = whichAttackType
    set DAMAGE[pui] = whichDamageType
    set MELEE[pui] = isMelee
    set RANGE[pui] = isRange
    set EFFECT[pui] = whichFx
    set POINT[pui] = whichPoint
    call ABCT_Start(function Check, data, CHECK_PERIOD)
endfunction

endlibrary

Test map includes Acid Bomb and Frost Nova ability tests (for the buffs).

Changelog:
v1.0 - First release
v1.1 - Absolutely no changes. Just uses the newest version of ABCT - v2.0
Attached Images
File type: jpgDOT.jpg (4.9 KB)
Attached Files
File type: w3x[System] DOT v1..w3x (88.1 KB)
File type: w3x[System] DOT v1.1.w3x (86.9 KB)
02-28-2008, 09:54 AM#2
Pyrogasm
It's so stretched because you have an extra long line: "public function Start takes...".
02-28-2008, 10:07 AM#3
cr4xZz
> It's so stretched because you have an extra long line: "public function Start takes...".
Oh, right... ^^ Comments anybody?
02-29-2008, 02:57 PM#4
Rising_Dusk
You've got some weird things going on here.
Collapse JASS:
...boolean isMelee, boolean isRange...
Would it not be so that if your isMelee boolean is true, your isRange boolean would automatically be false?

Also, what exactly is the point of your melee and ranged booleans that you store? They do nothing in the UnitDamageTarget() call as far as I am concerned, and they just add unnecessary arguments to your sample's function call.

Quote:
It requires a buff on a unit
to work and has a mechanism ot start that waits for 5 seconds for the unit to have the buff applied.
Checking time is every 0.1 seconds and if in 5 seconds the unit does not get the buff then the DOT is cancelled.
This limits the value of your system significantly. Why should you require a buff at all? What if I want to damage a bunch of units periodically without applying a buff to them? It also doesn't help that you in no way mitigate the pain of having to apply your own buff at all in your code.

I also don't see the value of requiring the buff in the first place. I mean, if I know my DOT is supposed to last 12 seconds and I want to deal damage once every 0.5 seconds, I don't need anything else. If I want a buff, I can make one and add it for 12 seconds, but I don't need one. Really, because of that huge limitation, you've made this absolutely un-approvable. Also, your DOT_AddDuration thing is impractical because it doesn't even increase the duration of the buff you require! What good is that since your system ends when the buff ends anyways!

Also, your implementation instructions are terribly hard to read. Do us all a favor and make the entire thing in comments so it's at least the same color.

You also should implement safety for if I use a blank string for the effect. What if I don't want an effect to play when I damage them per interval? You should at least add a check in your Periodic function to ensure that you're not creating effects without given paths. (It's good coding practice to be safe) The same applies for your POINT vars.

EDIT:
One more thing I find a critical flaw in your system. There is no way to know what type of damage or what attack type is being used for a given damage. Your system resets a unit's damagetype/attacktype every time you call the DOT_Start function, so I lose all information about the last damage call I made. Also, your DOT_AddDuration function AGAIN only applies to the last created damage periodic! That's TERRIBLE! What if I want to add some duration to one of my DOT spells but not to them all? I can't do that with how you've set up your system.

This can hardly even be considered a system with how many limitations and shortcomings it has. I'd rather use ABuff for everything and just use a generic damage buff, it'd not only be easier but also more malleable. This needs A LOT of work to be in a state that I can allow it to be approved.
02-29-2008, 03:38 PM#5
cohadar
I agree with Rising_Dusk in all except in the buff statement.
IMHO every Dot spell should be buff based, because if you have buff it can be dispelled or purged.
02-29-2008, 04:34 PM#6
Captain Griffen
In my opinion dispell and purge abilities should be purged from every map for being lame, so that isn't an issue in my opinion, and is in fact GOOD because buffs can be iffy with conflicts, recasts, etc. Full and proper MUI isn't possible with Blizzard's lame buffs.
02-29-2008, 05:10 PM#7
cohadar
Quote:
Originally Posted by Captain Griffen
In my opinion dispell and purge abilities should be purged from every map for being lame, so that isn't an issue in my opinion, and is in fact GOOD because buffs can be iffy with conflicts, recasts, etc. Full and proper MUI isn't possible with Blizzard's lame buffs.

Absolutely not true.
Dispel adds tactical depth to some combats,
conflicts, recasts and similar can all be handled with a proper coding,
and full and proper MUI is 10 times easier with buffs than with attaching only.

I base my spells around blizzards native buffs all the time and it works great.
Just look at the "Rejuvenation Shield" from Pyramidal Defence, it is one of the best spells I made.
It works with recasts (it stacks based on caster level) and it can be dispelled.

So your claims are from the simple fact that you would rather be lazy than do a proper spell coding.
02-29-2008, 05:32 PM#8
moyack
And in melee games, dispelling is a must have for any race in order to keep the balance.

Honestly I've been a little bit busy, so I haven't seen the code in depth, I'll give a review this weekend if my body can resist the real life.
02-29-2008, 09:34 PM#9
Rising_Dusk
Quote:
I'll give a review this weekend if my body can resist the real life.
Reality before resources man, no worries, that's what I'm here for. :p
Quote:
IMHO every Dot spell should be buff based, because if you have buff it can be dispelled or purged.
Even though I agree that buffs add a level of immersion to skills (It's so much cooler seeing that little buff icon on my command card than to just have some random effect on me), I still think that for this to be considered for approval it needs to work without buffs AND with buffs, not one without the other!
03-02-2008, 11:06 AM#10
cr4xZz
Oh well...

This was supposed to be a simple dot system because I do not have much experience in jass. Now that you mentioned 100 bugs that I didn't found this "system" really seems pointless. :\
I give up.
03-02-2008, 02:27 PM#11
Rising_Dusk
You shouldn't give up, you should just aim to make things as globally interactive as possible. I can move this to triggers and scripts for you to seek out ways to improve your work and make it more useful, if you'd like.
03-02-2008, 03:17 PM#12
cr4xZz
> I can move this to triggers and scripts for you to seek out ways to improve your work and make it more useful, if you'd like.
Well, ok. Sounds fine I guess ^^
03-02-2008, 03:31 PM#13
Rising_Dusk
As you wish. Moved.
03-03-2008, 06:55 PM#14
cr4xZz
Since I have no idea where to start, I'd like some tips. First of all, I'd like to know what would be the best way to make Set functions work for each Start function? As noted a few posts above, the Set functions have the flaw to set information on the last started DOT function. This means that if a unit has 3 DOT calls on it, the Set functions will work only on the last called one!
03-04-2008, 03:09 AM#15
Pyrogasm
Why have any "set" functions at all? Wouldn't it make more sense to just have people declare the damage/attacktype upon starting the function?