HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Some help with timer variables

11-13-2006, 04:47 PM#1
moyack
Hi:

I've been browsing this page in order to find information about timers, specifically the function call TimerStart(time, 4, false, null).

In all the scripts where I use timers, I always use them in order to control the duration of one spell getting the remain time and storing in a real variable. But now I've found some inconvenients.

I'm doing a Jass spell based on Unholy Frenzy, where the unit get an increased attack speed but drains life. With this script I add chain effect and a special behaviour, which links the caster with the target unit, so this unit will look like a pet for the caster.

In order to understand better the spell, please check the hidden information:

Jass code


Quote:
Originally Posted by Loyalty and Sacrifice description
The NagaWarqueen gives her bless to her devoted warriors, increasing their attack speed but make them suffering damage over time. If one of this warriors is dead in battle under the effect of this spell, part of its life will be given to the Warqueen.

Level 1 - Increase attack speed in 40%, deals 5 damage per second. Returns 20% of life.
Level 2 - Increase attack speed in 50%, deals 4 damage per second. Returns 40% of life.
Level 3 - Increase attack speed in 60%, deals 3 damage per second. Returns 60% of life.

One screenshots of the spell working
Zoom (requires log in)

Collapse Loyalty & Sacrifice spell:
//***************************************************************************************************************
//*                                                                                                             *
//*                                        Loyalty and Sacrifice Spell.                                         *
//*                                                By Moyack.                                                   *
//*                                                  V.1.0.                                                     *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* The constant functions where you can modify the ability properties
//*
constant function Loyalty_and_Sacrifice_SpellID takes nothing returns integer
    return 'A005'
endfunction

constant function Loyalty_and_Sacrifice_BuffID takes nothing returns integer
    return 'B009'
endfunction

constant function Loyalty_and_Sacrifice_Range takes nothing returns real
    return 1000.0
endfunction

constant function Loyalty_and_Sacrifice_ChainEffect takes nothing returns string
    return "LEAS"
endfunction

constant function Loyalty_and_Sacrifice_TargetEffect takes nothing returns string
    return "Abilities\\Spells\\NightElf\\FaerieDragonInvis\\FaerieDragon_Invis.mdl"
endfunction

constant function Loyalty_and_Sacrifice_HealEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl"
endfunction

constant function Loyalty_and_Sacrifice_HealEffect_Dur takes nothing returns real
    return 1.
endfunction

constant function Loyalty_and_Sacrifice_DyingEffect takes nothing returns string
    return "Abilities\\Spells\\Orc\\EtherealForm\\SpiritWalkerChange.mdl"
endfunction

constant function Loyalty_and_Sacrifice_DyingEffect_Dur takes nothing returns real
    return 0.934
endfunction


constant function Loyalty_and_Sacrifice_Life_Gained takes integer level returns real
    return 0.2 + 0.2 * (level - 1)
endfunction

//***************************************************************************************************************
//*                                                                                                             *
//*                                 Loyalty and Sacrifice Casting Functions                                     *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* Loyalty and Sacrifice Control casting condition
//*
function Loyalty_and_Sacrifice_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Loyalty_and_Sacrifice_SpellID()
endfunction

//***************************************************************************************************************
//* Loyalty and Sacrifice Control casting Actions
//*
function Loyalty_and_Sacrifice_Actions takes nothing returns nothing
    local unit c = GetSpellAbilityUnit()
    local unit t = GetSpellTargetUnit()
    local real tl = GetUnitState(t, UNIT_STATE_LIFE) * Loyalty_and_Sacrifice_Life_Gained(GetUnitAbilityLevel(c, Loyalty_and_Sacrifice_SpellID()))
    local lightning l = AddLightningEx(Loyalty_and_Sacrifice_ChainEffect(), true, GetUnitX(c), GetUnitY(c), GetUnitFlyHeight(c)+200, GetUnitX(t), GetUnitY(t), GetUnitFlyHeight(t)+200)
    local effect te
    loop
        exitwhen GetUnitAbilityLevel(t, Loyalty_and_Sacrifice_BuffID()) > 0
        call TriggerSleepAction(0.1)
    endloop
    set te = AddSpecialEffectTarget(Loyalty_and_Sacrifice_TargetEffect(), t, "weapon")
    loop
        exitwhen GetUnitAbilityLevel(t, Loyalty_and_Sacrifice_BuffID()) < 1 or GetWidgetLife(t) <= 0.405 or GetWidgetLife(c) <= 0.405 or not IsUnitInRangeXY(t, GetUnitX(c), GetUnitY(c), Loyalty_and_Sacrifice_Range())
        call MoveLightningEx(l , false, GetUnitX(c), GetUnitY(c), GetUnitFlyHeight(c)+50, GetUnitX(t), GetUnitY(t), GetUnitFlyHeight(t)+100)
        call TriggerSleepAction( 0.1 )
    endloop
    call DestroyEffect(te)
    call DestroyLightning(l)
    if GetUnitAbilityLevel(t, Loyalty_and_Sacrifice_BuffID()) > 0 then
        call UnitRemoveAbility(t, Loyalty_and_Sacrifice_BuffID())
    endif
    if GetWidgetLife(t) <= 0.405 then
        set te = AddSpecialEffect(Loyalty_and_Sacrifice_DyingEffect(), GetUnitX(t), GetUnitY(t))
        call TriggerSleepAction(Loyalty_and_Sacrifice_DyingEffect_Dur())
        call DestroyEffect(te)
        if GetWidgetLife(c) > 0.405 then
            set te = AddSpecialEffectTarget(Loyalty_and_Sacrifice_HealEffect(), c, "chest")
            call TriggerSleepAction(Loyalty_and_Sacrifice_HealEffect_Dur())
            call DestroyEffect(te)
            call SetUnitState(c, UNIT_STATE_LIFE, GetWidgetLife(c) + tl)
        endif
    endif
    set c = null
    set t = null
    set l = null
    set te = null
endfunction

//===========================================================================
function InitTrig_Loyalty_and_Sacrifice takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Loyalty_and_Sacrifice_Conditions ) )
    call TriggerAddAction( t, function Loyalty_and_Sacrifice_Actions )
    call Preload(Loyalty_and_Sacrifice_TargetEffect())
    call Preload(Loyalty_and_Sacrifice_HealEffect())
    call Preload(Loyalty_and_Sacrifice_DyingEffect())
    set t = null
endfunction



Well, this code is nice and meets the JESP standard, but the problem is the usage of TriggerSleepAction() function, the minimum value which I can use is 0.1 seconds, in theory it's enough, but when the target unit moves, the chain effect "jumps" and looks ugly.

Resuming I heve this questions:
  • I know the Timer must get periodic, so it can repeat the code. But if I want this scripts makes 100 iterations, how can I define or control this??
  • In order to make it leak free, I have to destroy the timer and then set it to null, but in the examples that I've seen, There is not any DestroyTimer() at the end of the code, so how you can nullify this variable??

Thanks for your help :)
Attached Images
File type: jpgWC3ScrnShot_100306_230914_01.jpg (282.0 KB)
11-13-2006, 05:12 PM#2
Rising_Dusk
An example of a timer in action --

Collapse JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction

function I2Timer takes integer i returns timer
    return i
    return null
endfunction

function TimerCallback takes nothing returns nothing
    local integer i = H2I(GetExpiredTimer())
    //Do stuff
    call DestroyTimer(I2Timer(i))
endfunction

function TimerBeginner takes nothing returns nothing
    local integer i = H2I(CreateTimer())
    call TimerStart(I2Timer(i), .033, true, function TimerCallback)
endfunction

Now that gets the job done.
Creates a timer, does stuff once, destroys the timer.
No leaks, hooray.

However, there really isn't a point to a periodic timer if you don't run it multiple times.
Being that I'm at school and don't know exact native arguments offhand, if I make a mistake in the following code let me apologize in advance.

Collapse JASS:
function TimerCallback takes nothing returns nothing
    local integer i = H2I(GetExpiredTimer())
    local real cur = TimerGetElapsed(I2Timer(i)) //Check me on this native! //ninja
    local real dur = 2.0 

    //***********************************************************************
    //*   Math is your friend!                                              *
    //*   		dur/iterations = interval 		  Therefore...  *
    //*   		interval*iterations = maxduration                       *
    //*                                                                     *
    //*       Use these formulae to get what you need time wise.            *
    //***********************************************************************

    //Do stuff based on variable cur or whatever
    if cur >= dur then
        call PauseTimer(I2Timer(i)) //A call I like to use to be safe
        call DestroyTimer(I2Timer(i))
    endif
endfunction

function TimerBeginner takes nothing returns nothing
    local integer i = H2I(CreateTimer())
    call TimerStart(I2Timer(i), .033, true, function TimerCallback)
endfunction

Note I skipped the I2Timer() and H2I() declarations in the just above example.
Now assuming everything is right, that timer I just made will run until the elapsed time on it is greater than your maximum duration.

Hopefully SOMETHING of what I said here will make sense.
So rawr. :D

Notably, many people would argue I2Timer() is unnecessary.
In my examples, they would be correct.
However, once you get more complicated and start attaching handles or some such to the timer, you're going to want to avoid setting timers to null.
Because of that, using the H2I()/I2Timer() is one way to keep it clean.
11-13-2006, 05:35 PM#3
blu_da_noob
I was under the impression TimerGetElapsed returned the time elapsed on each repetition of a repeating timer. That's what I always assumed anyway. Does it 'stockpile' the time for a repeating one?
11-13-2006, 05:56 PM#4
Rising_Dusk
It's worth looking into for sure.
I was under that it would return the stockpiled time it's taken.

However, if it does not then I'll rewrite the above bits to use a stored integer.
I'll have to test it when I get home from work.
Notably I use the stored integer method for AotZ, I was just doing it this way because I couldn't remember the exact functions I use.
11-13-2006, 06:00 PM#5
blu_da_noob
I'll test it now.

Nope, doesn't keep a running count. Just gives the time elapsed on that 'iteration' of the timer.
11-13-2006, 06:35 PM#6
Rising_Dusk
This sort of stuff needs some serious documentation.
I'll update the post above --

And heck, while I'm at it submit a tutorial.
This stuff can be confusing. :/
11-13-2006, 10:45 PM#7
moyack
Wow!!! Thanks for the help Rising and Blu :)

I'll do some test, and I'll tell you how they behave. But seriously I think there should be a tutorial about timers, it's something confusing sometimes.
11-14-2006, 12:56 AM#8
Rising_Dusk
Well, I wrote a timer tutorial just tonight to help out with this sort of thing.
Check it out, tell me if it helps at all. :P