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

DestroyEffectTimed system

Level 18
Joined
Oct 18, 2007
Messages
930
This system of mine was inspired by Justify's own system. Credits to him for the ida.

->[RAINBOW]REQUIRES JNGP[/RAINBOW]http://www.wc3campaigns.net/showthread.php?t=90999http://www.wc3campaigns.net/showthread.php?t=90999<-

[RAINBOW]All of this is made from scratch[/RAINBOW]

I did this to make an efficient code for removing effects over a period of time.

Script:
JASS:
library EffectOverTime

    globals
        private constant real Tick = 0.1

        private timer Tim = CreateTimer()

        private integer array Er
        private integer Total = 0
    endglobals

    private struct Effect
        effect StoredEffect
        real Counter    = 0
        real CounterMax = 0
        
        static method ExecuteEffect takes nothing returns nothing
            local Effect dat
            local integer i = 0
            
            loop
                exitwhen i >= Total
                set dat = Er[i]

                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Er[i] = Er[Total]

                    set i = i - 1

                    call dat.destroy()
                else
                    set dat.Counter = dat.Counter + Tick
                endif
                
                set i = i + 1
            endloop
            
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod
                
        
        static method create takes effect StoringEffect, real Time returns Effect
            local Effect dat = Effect.allocate()
            
            set dat.StoredEffect = StoringEffect

            set dat.CounterMax = Time - Tick
            
            if Total == 0 then
                call TimerStart(Tim, Tick, true, function Effect.ExecuteEffect)
            endif
            
            set Er[Total] = dat
            set Total = Total + 1
            
            return dat
        endmethod
        
        method onDestroy takes nothing returns nothing
            call DestroyEffect(.StoredEffect)
            set .StoredEffect = null
        endmethod
    endstruct

    function DestroyEffectTimed takes effect StoringEffect, real Time returns nothing
        call Effect.create(StoringEffect, Time)
    endfunction
    
endlibrary

Ok if you find any bugs with it or ways of improvement, please reply.
Fixes
  • Update: Fixed the things that Deaod found ( another thing to ^^ )

Tip: If you want to have a more accurate timer just set the global variable "Tick" to a lower value. Lower that 0.05 is not recommended

How To Use
-
[RAINBOW]Example use of Triggering:[/RAINBOW]
  • Example
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Storm Bolt
    • Actions
      • -------- Just a variable ( not needed for the call ) --------
      • Set Unit = (Triggering unit)
      • -------- - --------
      • -------- A Time variable to set the time it takes to remove the effect ( you dont need this variable, you could set the value yourself. But a variable is recommended! --------
      • Set Time = 3.00
      • -------- - --------
      • -------- Creating and setting the effect --------
      • Special Effect - Create a special effect attached to the origin of Unit using Abilities\Spells\NightElf\EntanglingRoots\EntanglingRootsTarget.mdl
      • Set Effect = (Last created special effect)
      • -------- - --------
      • -------- Doing the call. This requires an special effect variable, and a real variable ( can be just a number ) --------
      • Custom script: call DestroyEffectTimed( udg_Effect , udg_Time )
      • -------- - --------
      • -------- Nulling the effects --------
      • Set Unit = No unit
      • Custom script: set udg_Effect = null
      • -------- - --------
[RAINBOW]Example use of Jass:[/RAINBOW]
JASS:
function Trig_Example_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'AHtb'
endfunction

function Trig_Example_Actions takes nothing returns nothing
    // Just a variable ( not needed for the call )
    local unit Unit = GetTriggerUnit()
    // -
    // A Time variable to set the time it takes to remove the effect ( you dont need this variable, you could set the value yourself. But a variable is recommended!
    local real Time = 3.00
    // -
    // Creating and setting the effect
    local effect Effect = AddSpecialEffectTarget( "Abilities\\Spells\\NightElf\\EntanglingRoots\\EntanglingRootsTarget.mdl" , Unit , "origin" )
    // -
    // Doing the call. This requires an special effect variable, and a real variable ( can be just a number )
    call DestroyEffectTimed( Effect , Time )
    // -
    // Nulling the effects
    set Unit = null
    set Effect = null
    // -
endfunction

//===========================================================================
function InitTrig_Example takes nothing returns nothing
    set gg_trg_Example = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Example, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Example, Condition( function Trig_Example_Conditions ) )
    call TriggerAddAction( gg_trg_Example, function Trig_Example_Actions )
endfunction
Added test map:
 

Attachments

  • DestroyEffectTimed by Dynasti.w3x
    16.5 KB · Views: 151
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
JASS:
if dat.Counter == dat.CounterMax then
should be:
JASS:
if dat.Counter >= dat.CounterMax then


JASS:
set Er[i] = Er[Total - 1]
set Total = Total - 1

just switch those two lines

JASS:
set Total = Total - 1
set Er[i] = Er[Total]

same again in your create method


JASS:
call DestroyEffect(dat.StoredEffect)
is not needed in your ExecuteEffect method
 
Level 18
Joined
Oct 18, 2007
Messages
930
JASS:
if dat.Counter == dat.CounterMax then
should be:
JASS:
if dat.Counter >= dat.CounterMax then


JASS:
set Er[i] = Er[Total - 1]
set Total = Total - 1

just switch those two lines

JASS:
set Total = Total - 1
set Er[i] = Er[Total]

same again in your create method


JASS:
call DestroyEffect(dat.StoredEffect)
is not needed in your ExecuteEffect method

k fixing ^^ thx +rep

Edit: you told me
JASS:
                if dat.Counter == dat.CounterMax then
should have been
JASS:
                if dat.Counter >= dat.CounterMax then
but if i keep it the way i want it gets accurate :p tested it. If i do what you say on that point it will tick 0.1 secconds over. but anyways. Thx ^^
 
Level 13
Joined
Mar 16, 2008
Messages
941
I tested it now, and I'm right.
I changed your method to this:
JASS:
static method ExecuteEffect takes nothing returns nothing
            local Effect dat
            local integer i = 0
            
            call BJDebugMsg("Total:"+I2S(Total))
            loop
                exitwhen i >= Total
                set dat = Er[i]

                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Er[i] = Er[Total]

                    call dat.destroy()
                else
                    set dat.Counter = dat.Counter + Tick
                endif
                
                set i = i + 1
            endloop
            
            call BJDebugMsg(I2S(i))
            
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod
BJDebugMsg("Total:"+I2S(Total)) shows how often the loop should run to increase all durations, while BJDebugMsg(I2S(i)) shows how often the loop does run.

I added 2 effects (screen as attachment)
I would just insert the "i=i+1" into the else condition :)
Omg, editing destroys attachments? Annoying :S

EDIT: Timer test!
1. Attachment is the integer test, the second the timertest...
JASS:
static method create takes effect StoringEffect, real Time returns Effect
            local Effect dat = Effect.allocate()
            
            set dat.StoredEffect = StoringEffect
            set dat.t = CreateTimer()
            set dat.CounterMax = Time - Tick
            
            call TimerStart(dat.t, 10, false, null)
            
            if Total == 0 then
                call TimerStart(Tim, Tick, true, function Effect.ExecuteEffect)
            endif
            
            set Er[Total] = dat
            set Total = Total + 1
            
            return dat
        endmethod
        
        method onDestroy takes nothing returns nothing
            call PauseTimer(.t)
            call BJDebugMsg(R2S(TimerGetElapsed(.t)))
            call DestroyEffect(.StoredEffect)
            set .StoredEffect = null
        endmethod
Timer test was random hitting the ESC button^^ Sometimes 3.1, sometimes 3.2 and in very rare cases 3.025 (3.0)
 

Attachments

  • screen.jpg
    screen.jpg
    179.2 KB · Views: 189
  • test2.jpg
    test2.jpg
    191.5 KB · Views: 161
Level 23
Joined
Nov 29, 2006
Messages
2,482
Dynasty, he is right, although it will not be especially visible for the human eye if you don't have that many structs attached.

I made some testing with your piece of code adding a global tick counter.
JASS:
        static method ExecuteEffect takes nothing returns nothing
            local Effect dat
            local integer i = 0
            set tickstatus = tickstatus + Tick
            loop
                exitwhen i >= Total
                set dat = Er[i]

                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Er[i] = Er[Total]

                    call dat.destroy()
                    
                    call BJDebugMsg("CurrentTick: "+R2S(tickstatus))
                else
                    set dat.Counter = dat.Counter + Tick
                endif
                
                set i = i + 1
            endloop
            
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod

Now, if I create 10 effects at once, giving them all the same duration shows the following:
2q0r1nc.jpg

And with 20 effects:
358duvp.jpg


I tested out Justify's system just to see if there was any difference with the usage of a boolean:
wi4ho9.jpg


His result is actually the most accurate. Though, /@Justify, your code could be done more efficient.

Normally a tick which is 'jumping' over one struct is not a problem. Timers with shorter tick increasing does not suffer that much of this. So you can speculate whether or not it is an issue to bother about. I am just showing the facts =)

Edit: Funny that you made some tests as well...
 
Level 14
Joined
Nov 18, 2007
Messages
816
Justify should be right and you can fix this by adding this:
JASS:
set i=i-1
to your if-then-else for destroying.
Like this:
JASS:
                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Er[i] = Er[Total]

                    call dat.destroy()
                    //set i=i-1 -- added
                else
                    set dat.Counter = dat.Counter + Tick
                endif
 
Level 18
Joined
Oct 18, 2007
Messages
930
Yeah, that is actually a clever solution.

So at the destroying im going to just set i = i - 1? if that is then good for me ^^

Edit:
JASS:
        static method ExecuteEffect takes nothing returns nothing
            local Effect dat
            local integer i = 0
            
            loop
                exitwhen i >= Total
                set dat = Er[i]

                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Er[i] = Er[Total]

                    call dat.destroy()
                    
                    set i = i - 1
                else
                    set dat.Counter = dat.Counter + Tick
                endif
                
                set i = i + 1
            endloop
            
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod
ok?
 
Level 18
Joined
Oct 18, 2007
Messages
930
Doesnt exist, mark my words:p

Examples! :

    • call DestroyEffect(<handle>)
    • call DestroyBoolExpr(<handle>)
    • call DestroyFogModifier(<handle>)
    • call DestroyForce(<handle>)
    • call DestroyGroup(<handle>)
    • call DestroyImage(<handle>)
    • call DestroyItemPool(<handle>)
    • call DestroyLeaderboard(<handle)
    • call DestroyLightning(<handle>)
    • call DestroyMultiboard(<handle>)
    • call DestroyTextTag(<handle>)
    • call DestroyTimer(<handle>)
    • call DestroyTimerDialog(<handle>)
    • call DestroyTrigger(<handle>) // " could be useful "
    • call DestroyUbersplat(<handle>)
    • call DestroyUnitPool(<handle)

There!!
 
Level 23
Joined
Nov 29, 2006
Messages
2,482
well, I didnt mean it that way... Besides, just because an effect is a handle doesnt mean that a handle is an effect. So, If we ought to do a DestroyHandleTimed those natives will not allowe handle types. Sure with if and elseifs it is possible, but I figured it would be a very slow system.
 
Level 18
Joined
Oct 18, 2007
Messages
930
well, I Didnt Mean It That Way... Besides, Just Because An Effect Is A Handle Doesnt Mean That A Handle Is An Effect. So, If We Ought To Do A Destroyhandletimed Those Natives Will Not Allowe Handle Types. Sure With If And Elseifs It Is Possible, But I Figured It Would Be A Very Slow System.

Or!!! I Could Use Textmacros!! Mwhoahaha!!!

Caps Ftw!
 
Level 23
Joined
Nov 29, 2006
Messages
2,482
Yes, textmacros would work too even though, but creates a big amount of functions. Also, there are some exceptions such as Remove%handle% and Release%handle%. I guess thats not a problem though.
 
Level 18
Joined
Oct 18, 2007
Messages
930
Yes, textmacros would work too even though, but creates a big amount of functions. Also, there are some exceptions such as Remove%handle% and Release%handle%. I guess thats not a problem though.

you are probably right, but it could be used for the "texter's" own good. Like if he wants a Effect of sum, then just make that Textmacro xD

Edit: Here, example
JASS:
//! textmacro DestroyHandleTimed takes HANDLE, NAME
library $NAME$OverTime

    globals
        private constant real Tick = 0.1

        private timer Tim = CreateTimer()

        private integer array Ar
        private integer Total = 0
    endglobals

    private struct $NAME$
        $HANDLE$ Stored$NAME$
        real Counter    = 0
        real CounterMax = 0
        
        static method Execute$NAME$ takes nothing returns nothing
            local $NAME$ dat
            local integer i = 0
            
            loop
                exitwhen i >= Total
                set dat = Ar[i]

                if dat.Counter >= dat.CounterMax then

                    set Total = Total - 1
                    set Ar[i] = Ar[Total]

                    call dat.destroy()
                    
                    set i = i - 1
                else
                    set dat.Counter = dat.Counter + Tick
                endif
                
                set i = i + 1
            endloop
            
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod
                
        
        static method create takes $HANDLE$ Storing$NAME$, real Time returns $NAME$
            local $NAME$ dat = $NAME$.allocate()
            
            set dat.Stored$NAME$ = Storing$NAME$

            set dat.CounterMax = Time - Tick
            
            if Total == 0 then
                call TimerStart(Tim, Tick, true, function $NAME$.Execute$NAME$)
            endif
            
            set Ar[Total] = dat
            set Total = Total + 1
            
            return dat
        endmethod
        
        method onDestroy takes nothing returns nothing
            call Destroy$NAME$(.Stored$NAME$)
            set .Stored$NAME$ = null
        endmethod
    endstruct

    function Destroy$NAME$Timed takes $HANDLE$ Storing$NAME$, real Time returns nothing
        call $NAME$.create(Storing$NAME$, Time)
    endfunction
    
endlibrary
//! endtextmacro

//! runtextmacro DestroyHandleTimed("effect","Effect")

Ps: added a map
 

Attachments

  • Example Handle Destroy.w3x
    16.8 KB · Views: 55
Last edited:
Top