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

Modify Mana Over Time

Level 17
Joined
Jun 17, 2007
Messages
1,433
A simple library that modifies a unit's unitstate periodically. Here it is.

JASS:
library ModifyStatePeriodic requires TimerUtils
struct MSP
    static method create takes unit whichunit, real amount, real interval, real duration, unitstate state returns thistype
        local thistype da = thistype.allocate()
        local timer t = CreateTimer()
        set da.target = whichunit
        set da.amount = amount
        set da.max = duration/interval
        set da.state = state
        call SetTimerData(t, da)
        call TimerStart(t, interval, true, function thistype.Callback)
        return da
    endmethod
    private static method Callback takes nothing returns nothing
        local thistype d = GetTimerData(GetExpiredTimer())
        call SetUnitState(d.target, d.state, GetUnitState(d.target, d.state) + d.amount)
        set d.tick = d.tick + 1
        if d.tick >= d.max then
            call d.destroy()
            call ReleaseTimer(GetExpiredTimer())
        endif
    endmethod
    real tick        = 0
    real max         = 0
    unit target      = null
    real amount      = 0
    unitstate state  = null
 endstruct
function ModifyStatePeriodic takes unit whichunit, real amount, real interval, real duration, unitstate state returns nothing
    call MSP.create(whichunit, amount, interval, duration, state)
endfunction
endlibrary
 
Last edited:
Ouch...

Interval should be constant, like 0.05 or something.
You should use a struct loop with one timer, not TimerUtils.
You should take a duration parameter, and calculate the number of executions based on the duration and period yourself.
Your amount parameter should be the total amount to add/subtract over the whole duration.

There is no point whatsoever in making the user choose their own timer interval. If I used a DPS or anything similar, I'd just want it to smoothly reduce/increase the health or whatever, and I can't think of any time I'd want it only to do it every 1 second or whatever, and you can make it more efficient with a struct loop (linked lists or a stack).
 
Level 17
Joined
Jun 17, 2007
Messages
1,433
Interval should be constant, like 0.05 or something.
You should use a struct loop with one timer, not TimerUtils.
I'd rather not execute code periodically when I don't have to.

You should take a duration parameter, and calculate the number of executions based on the duration and period yourself.
Your amount parameter should be the total amount to add/subtract over the whole duration.
Why? A user can easily do that themselves if they want to.

If I used a DPS or anything similar
Which this isn't, so go and find a DoT system.

I can't think of any time I'd want it only to do it every 1 second or whatever
Then you obviously haven't looked at any standard Warcraft 3 spells. Shadowstrike, Rejuvenation, etc...

You can make it more efficient with a struct loop (linked lists or a stack).
That's purely dependant on the interval.
 
I'd rather not execute code periodically when I don't have to.
You can pause the timer when nothing is running.
Why? A user can easily do that themselves if they want to.
Almost everyone will want it that way, so why not make it easier and make it that way?
Which this isn't, so go and find a DoT system.
So you're saying a "mana over time" isn't similar to a "damage over time"?
Then you obviously haven't looked at any standard Warcraft 3 spells. Shadowstrike, Rejuvenation, etc...
Yeah. They're crap.
That's purely dependant on the interval.
Which is why I said you should have a fixed interval...
 
Level 8
Joined
Aug 6, 2008
Messages
451
Using one timer per interval is really badass when doing some stuff that requires smooth transition. ( Moving thingies, fading stuff, changing size )

It usually works for stuff like this too, but if you want to do some more advanced stuff, you realize why one timer per instance pwns.

For example, if you had a buff system, which had over time health and mana manipulation features, it would be pretty easy to make some slow time spell that also affects the period of these change. Just get buff struct from units in area, and reset buff structs timers period.

But this system doesnt really support stuff like that anyways, so this all I said was kinda meaningless in this case.

edit. You can also make one periodic timer per interval, instead of one periodic timer for all intervals. I think Jesus4Lyfs nice and smooth KeyTimers2 -timer system uses this method.
 
Level 11
Joined
Apr 13, 2006
Messages
353
SetUnitStateTimed would be better.

Yup.

edit. You can also make one periodic timer per interval, instead of one periodic timer for all intervals. I think Jesus4Lyfs nice and smooth KeyTimers2 -timer system uses this method.

That isn't needed at all. His implementation is fine. I also heard that KT2 got shot down at Wc3C, which tells you something about the resource. Mainly, that TimerUtils is superior.
 
You shouldn't use a timer instance for everything, just use one system timer.
As it, for example, is easier to read and to control.
You only need to pause the struct timer to stop all instances, while a timer for
every instance would be quite hard to control.

Also, you can also change the timers time with a real variable which is the remainingTime. You can decrease it again to change the remaining time.
(For post http://www.hiveworkshop.com/forums/submissions-414/modify-mana-over-time-142239/#post1289991)
 
Level 11
Joined
Feb 22, 2006
Messages
752
A single global timer only makes sense when there is a common period for all instances and code needs to be executed for all instances every time the timer times out, which is why you see it in knockbacks, jumps, etc.

Implementing it that way for something like this makes way less sense, because different instances here can very well have different periods (one instance might mod mana every 2 seconds while another every 1 second).

That being said, I do think it would be better if you let the user specify duration instead of number of iterations. Most people think in terms of "lose x mana every y seconds for z seconds". Not "lose x mana every y seconds z times".
 
aznricepuff, you don't get it, at all.

The thing is: The interval and duration doesn't matter at all.
It simply runs every 0.03 seconds, for example. Then it decreases
every others remainingTime by 0.03 seconds. If remainingTime <= 0
then it fires off the destroying effect and decreasing the stack.

Also, your version is bug friendly with the GetHandleID(), which isn't
fixed at all and still causes bugs.

And, to be honest, you shouldn't cast an handleID-Event for things
you don't need to.
 
Level 11
Joined
Feb 22, 2006
Messages
752
Yea, it runs every 0.03 seconds and most of those timeouts it's doing nothing but either decrementing or incrementing some counter. If you use one timer per instance, it only needs to execute code when it actually matters: i.e. when it actually needs to modify mana/destroy itself.

And how does GetHandleId() cause bugs? As far as I know it's not a state-changing function. So actually the only way it can bug is if it doesn't always return the right id, and I don't think blizz is that incompetent.
 
Why not trying yourself with this library?

JASS:
library handletest initializer init

    globals
        integer handleID
        unit target
        timer test = CreateTimer()
    endglobals
    
    private function test takes nothing returns nothing
        if GetHandleId(target) != handleID then
            call BJDebugMsg("Handle ID is new")
        endif
    endfunction
    
    private function init takes nothing returns nothing
        set target = CreateUnit(Player(0), 'hpea', 0., 0., 270.)
        set handleID = GetHandleId(target)
        call TimerStart(test, 0.03, true, function test)
    endfunction

endlibrary
 
Top