Snippet vJASS Spell Template

SerraAvenger

Cuz I can
Reaction score
234
The current version was not tested yet, so take care.
This is a small scope snipper that I have found useful.
It is open to suggestions of any kind.
All you have to do is:
BEFORE FIRST USE
  • copy the superstruct library into a custom script trigger.
At any use after the first one:
  1. Copy the childstruct into a custom script section.
  2. Rename the scope to something unique (representing the spell, if possible)
  3. Specify the ABIL_ID (the raw-id of the ability that shall run the trigger) and the EVENT (the event that shall fire the trigger).
  4. Enter your spell code into the '.init' method. Also change the zero to the duration of the spell
  5. If the spell has a periodic component, you can use the global real variable 'PERIOD'. Every PERIOD seconds, all structs of type data will be run through with a call to the structs 'loopAction'. PERIOD should be smaller than 10% of the minimum struct life span, structs might live longer elsewise.
  6. After the struct's loopAction has been called, the code will automatically check wether the struct has reached the end of its life and needs to go the way of all mortal structs. If so, the structs '.clear' method will be called. This should be used to clear all data from the struct, like for example effects, dummy units or whatsoever.
What are the pros of this system?
  • It is really easy and fast to use
  • All the nasty background stuff is already done for you
  • It is quite flexible
  • Apart from JASSHelper, it is stand-alone
What are the cons of this system?
  • It requires JASSHelper
  • The Current Version is not Tested
  • It needs one trigger per spell instance :/

I externalised all the hard work into a superstruct. Not tested yet
JASS:
library SJ_SPELL_DATA

struct sj_spell_data
    unit caster
    real x
    real y
    integer ownerId
    integer level
    
    real timeRemaining
    integer id
    static data_instance array self
    static integer count = 0
    static constant timer TIMER = CreateTimer()    
    
    stub method loopAction takes nothing returns nothing
    endmethod
    
    stub method init takes nothing returns nothing
    endmethod
    
    stub method clear takes nothing returns nothing
    endmethod
    
    static method handleData takes nothing returns nothing
        local integer i = 0
        local data_instance curData
        loop
            exitwhen i >= .count
            set curdata_instance = .self[ i ]
            call curdata_instance.loopAction()
            
            set .timeRemaining = .timeRemaining - PERIOD
            if .timeRemaining <= 0 then
                call .destroy()
            endif
            
            set i = i + 1
        endloop
    endmethod
    
    static method create takes nothing returns sj_spell_data
        local sj_spell_data this = .allocate()
        call .init()
        
            set .caster = GetTriggerUnit()
            set .x = GetUnitX( .caster )
            set .y = GetUnitY( .caster )
            set .level   = GetUnitAbilityLevel( trigUnit, ABIL_ID )
            set .ownerId = GetPlayerId( GetOwningPlayer( .caster ) )
            
        set .timeRemaining = PERIOD
        set .self[ .count ] = this
        set .id = .count
        set .count = .count + 1
        
        if .count == 1 then
            call TimerStart( .TIMER, PERIOD, true, function data_instance.handledata_instance )
        endif
        return this
    endmethod
    
    method setRemaining takes real timeremaining returns nothing
        if timeremaining <= 0 then
            call .destroy()
        else
            set .timeRemaining = timeremaining
        endif
    endmethod
    
    method getRemaining takes nothing returns real
        return .timeRemaining
    endmethod
        
    private method onDestroy takes nothing returns nothing
        call .clear()
        set .count = .count - 1
        if .id != .count then
            set .self[ .id ] = .self[ .count ]
            set .self[ .id ].id = .id
        endif
        set .self[ .count ] = 0
        if .count == 0 then
            call PauseTimer( .TIMER )
        endif        
    endmethod    
endstruct
endlibrary

Here is the code of each spell instance

JASS:
scope Spell
globals
    private constant real PERIOD           = 0.04
    private constant integer ABIL_ID       = 'A000'
    private constant playerunitevent EVENT = EVENT_PLAYER_UNIT_SPELL_EFFECT
endglobals


private struct data_instance extends sj_spell_data
    //precreated variables...
      //    caster  --> unit    // the caster
      //    x       --> real    // the caster's x position
      //    y       --> real    // the caster's y position
      //    ownerId --> integer // the player id of the caster's owner
      //    level   --> integer // the level of the spell
      
    //add own variables here
    
    method loopAction takes nothing returns nothing
        // will be called every PERIOD
    endmethod
    
    method init takes nothing returns nothing
        // will be called on struct creation
        call .setRemaining( 0 ) // Replace 0 with the duration of the spell. 0 will instantly destroy the spell!
    endmethod
    
    method clear takes nothing returns nothing
        // please clear all handle variables here. Will be called when the .destroy() method is evoked.
    endmethod

// -----------------------------------
//  Please don't change the following
// -----------------------------------
    
    static constant trigger spellTrigger = CreateTrigger()
        
    static method actions takes nothing returns nothing
        if GetSpellAbilityId() == ABIL_ID then
            call .create()
        endif
    endmethod
    
    private static method onInit takes nothing returns nothing
        call TriggerRegisterAnyUnitEventBJ( .spellTrigger, EVENT )
        call TriggerAddAction( .spellTrigger, function .actions )
    endmethod
endstruct
    
endscope


Perhaps I will change it to use Jesus4Lyf's initial event system.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
It's a bit confusing for those who don't program in the same way as you.

I don't program for the struct very much, and I like the idea of an organized spell using an external attachment system for organizational purposes.

Here's my own personal Spell Template, if you're interested in seeing what I use. It's a bit old (a month or two) because I haven't made any spells in a while, but it's pretty easy for me to understand.

JASS:
scope Generic initializer Init

//==================================================||
//                                                  ||
//           Spell Created by Darthfett             ||
//                                                  ||
//             Easy to end spell, use:              ||
//                                                  ||
//    call Generic_Data[UNIT].release()             ||
//      - to finish a spell abruptly                ||
//                                                  ||
//    call Data.finish(Data[UNIT])                  ||
//      - to finish a spell with FX.                ||
//                                                  ||
//==================================================||

globals
    private integer AID_GENERIC = 'A000'
    private real TIMEOUT = 0.03125
endglobals

public struct Data
    //! runtextmacro PUI()
    timer t
    unit c
    integer lvl
    
    static method create takes nothing returns Data
        local Data d = Data.allocate()
        set d.t = CreateTimer()
        return d
    endmethod
        
    
    method onDestroy takes nothing returns nothing
//All the actual cleanup is done in .release/.destroy methods, 
//so that it is possible to stop a spell whenever.
//This makes the spell very flexible
        call SetCSData(this.t,0)
        call PauseTimer(this.t)
        call DestroyTimer(this.t)
        set this.t = null
        set this.c = null
    endmethod
    
    static method finish takes Data d returns nothing
        //Do stuff with the contents of Data, such as damaging something, or making an explosive finish.
        call d.release()
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_GENERIC
endfunction

private function Callback takes nothing returns nothing
    local Data d = Data(GetCSData(GetExpiredTimer()))
    if GetWidgetLife(d.c) < .405 == true then //Ending abnormally
        call d.release()
    endif
    
    //DO STUFF
    
    if true then //Ending normally
        call Data.finish(d)
    endif
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data[GetTriggerUnit()]
    if d == 0 then
//Above: Reusing old Data.  If you want to Remove any existing data, change this to "if d != 0 then", 
// and destroy the existing struct here.
        set d = Data.create()
        set Data[GetTriggerUnit()] = d
    endif
    
    set d.c = GetTriggerUnit()   
    //Set data
    
    call SetCSData(d.t,d)
    call TimerStart(d.t,TIMEOUT,true,function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
endfunction

endscope
 

Romek

Super Moderator
Reaction score
963
I agree with Darth on your template. I really dislike the coding style, and I'm pretty sure most other people would too.

Darths template is pretty complicated too for just a spell...
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Darths template is pretty complicated too for just a spell...

lolwut?

The only part that is different from a bare-minimum spell is the fact that I added a "finish" method, which just creates the effects at the end of the spell (such as a explosion at the end of a sliding charge spell or something).

It already has a few features in there just to make it easier for a coder to see how to correctly end a spell. :p
 

Azlier

Old World Ghost
Reaction score
461
I find the struct overly complicated. I don't understand why you need a separate thread for the initialization. Here's my spell template, and it serves me just fine.

JASS:
scope SpellTemplate initializer Init

globals
    private constant integer SPELL_ID = 'A000'
    //Any other needed globals.
endglobals

//If a struct is needed, add it here.

//Add any callbacks here (ForForce callback, filters, timer callbacks, etc.)

private function Actions takes nothing returns boolean
    //Variables, mostly unset, here.
    if GetSpellAbilityId() == SPELL_ID then
        //Set needed variables, spell code.
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 15
    loop
        exitwhen i < 0
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i = i - 1
    endloop
    call TriggerAddCondition(t, Condition(function Actions))
endfunction

endscope
 

SerraAvenger

Cuz I can
Reaction score
234
Thanks for your replies!

It's a bit confusing for those who don't program in the same way as you.

Hum... Could you elaborate further?
If you mean that most people don't externalize these .clear/.init/.loopAction methods but rather rely on programming them their own as needed...
The thing is the following:
a) It is easier to find the 'editeable code' if it is at the upper part of the script. In my script, all that you can edit is positioned in the first 39 lines of code - which, to me, seems quite fair.
b) I have coded a couple of spells and in the end, all of them looked like this. Hence I thought: 'Why to do it the hard way if I can just cnp the template and fill in the spaces as necessary?


Here's my own personal Spell Template, if you're interested in seeing what I use. It's a bit old (a month or two) because I haven't made any spells in a while, but it's pretty easy for me to understand.
Thanks a lot!
I'll voice my thoughts on your approach ; )
JASS:
call Generic_Data[UNIT].release()
call Data.finish(Data[UNIT])

I don't see the point in finishing a spell without an fx... Where can I specify the fx it finishes with? And what is the UNIT argument for :? ?

JASS:
    //! runtextmacro PUI()

I don't like to add requirements into my code. If my users like to use PUI, they can still add it themselves - I haven't needed it so far.

JASS:

Well - I as a user don't want to be confronted with the internal structure of your scope :/
On a side note, I have heard that timers don't like being destroyed - and that lot's of timers aren't good for a maps health, hence I'm using a single one for all my structs.


//All the actual cleanup is done in .release/.destroy methods,
//so that it is possible to stop a spell whenever.
//This makes the spell very flexible
the onDestroy method in my code calls the clear method, and also clears the struct from the static array. I don't need no .finish call, the .destroy one does it all ; )

For the rest of the code: It seems as if the principles would be the same, just with the change that your 'editeable code' is mixed altogether with 'nonediteable code'.

The destruction of the struct once the unit dies is a good idea, but I might want to use spells that don't end there - like an temporary aos effect, a spell that deals damage to a unit for some seconds and other stuff like that.

@Romek:
Please explain further - what is wrong with the coding style exactly?

Thanks a lot again for your replies!
Best wishes, davey

EDIT: @Azlier: What is the sense of doing a template if it doesn't do anything you use frequently :? I think, as long as a struct is needed I'll prefer mine. If not, yours is much better as it doesn't have the disadvantage of my system. The thought behind my template was: Just cnp, don't worry about how it works, just be happy that it does. If you need to code a static timer system behind it, use periodic callbacks or whatever, It missed the goals.
@the sperate thread: My Init thread is crashing frequently. Too many scopes :/ Well, 9000 lines of code pay out.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Hum... Could you elaborate further?
If you mean that most people don't externalize these .clear/.init/.loopAction methods but rather rely on programming them their own as needed...
The thing is the following:
a) It is easier to find the 'editeable code' if it is at the upper part of the script. In my script, all that you can edit is positioned in the first 39 lines of code - which, to me, seems quite fair.
b) I have coded a couple of spells and in the end, all of them looked like this. Hence I thought: 'Why to do it the hard way if I can just cnp the template and fill in the spaces as necessary?

I made my template specifically for me, because I knew that everyone has their own coding style. The only reason I posted mine here, was so you could get ideas. I'm not trying to impose mine on you, I just see a lot of systems that are strictly attach-to-timer, and mine is a bit different. Therefore, since I was making a lot of spells in a similar fashion (I often re-created this 'template' with some slight modifications varying between spells), I decided to make a template.

I wanted something to easily start with, but something that was also as flexible as I needed it to be. Therefore, there is no "this is your space, this is the system's space".

I'll voice my thoughts on your approach ; )
JASS:
call Generic_Data[UNIT].release()
call Data.finish(Data[UNIT])

I don't see the point in finishing a spell without an fx... Where can I specify the fx it finishes with? And what is the UNIT argument for :? ?

I guess you missed the finish method. It is specifically for all the effects and stuff.

JASS:
    static method finish takes Data d returns nothing
        //Do stuff with the contents of Data, such as damaging something, or making an explosive finish.
        call d.release()
    endmethod


In my Forest CTF map, I have a Charge spell that only 'ends' when the hero reaches the target. However, I also created a system that moves everyone back to starting positions when you capture the flag. If I charged an enemy who was trying to capture the flag, I would win the round, and be moved back to start. The problem is that I would continue charging, all the way across the map to the enemy.

The idea behind ending a spell without effects, is to safely clean it up without ending the spell. This way, it doesn't damage the unit or do any of those spiffy effects that should only happen if the spell succeeds.

I use PUI so that I can attach the struct to the unit (caster), as well as the timer, to make the spell flexible. This way, if you need to "end any charge spells that might be occuring", you simply pick the units, and call the release method.

I don't like to add requirements into my code. If my users like to use PUI, they can still add it themselves - I haven't needed it so far.

Why would a user of your script, which is supposed to be easy to use, no configuration, want to modify your script to add in unit attaching, or change the attachment system? Obviously, these are both user-preferences, which is why I only made the attachment system for myself.

JASS:

Well - I as a user don't want to be confronted with the internal structure of your scope :/
On a side note, I have heard that timers don't like being destroyed - and that lot's of timers aren't good for a maps health, hence I'm using a single one for all my structs.

There's a limit to how much flexibility AND simplicity you can give a user. Mine has more flexibility, while yours is more simplistic. I have heard about timer issues, but I haven't ever had a problem with it. I've been considering using Group/Timer recycling functions, as well as a system like TT, but I haven't decided yet.

1. the onDestroy method in my code calls the clear method, and also clears the struct from the static array. I don't need no .finish call, the .destroy one does it all ; )

2. For the rest of the code: It seems as if the principles would be the same, just with the change that your 'editeable code' is mixed altogether with 'nonediteable code'.

3. The destruction of the struct once the unit dies is a good idea, but I might want to use spells that don't end there - like an temporary aos effect, a spell that deals damage to a unit for some seconds and other stuff like that.

1. PUI has some stuff it needs to do before a struct is destroyed, and so the .release method is needed. If you don't want to use the .finish method, you don't have to. It's just a way for users to separate cleaning up leaks and the callback stuff from the ending area, to improve readability. Basically in my system, the .release method does it all as well.

2. For the most part, yes. There are a few differences, such as that you can get the data from the unit outside of the callback function. But then again, there really isn't much room for differences in a spell template. All you're really doing is providing space for different parts of the spell: Initialization, Callback, and Ending. The template is only responsible for attachment, get-attachment, and running the callback.

3. That's another reason that I merged the system with the 'user area'. The death was an example of how you could end the system. It's up to you to put whatever you want there.

JASS:
    if true then //Ending normally
        call Data.finish(d)
    endif


Was also an example, but without an actual condition (I didn't want to add in .ticks as an extra struct variable).

Originally, when I said
It's a bit confusing for those who don't program in the same way as you.
I meant that everyone has their own coding style. I think of a template as a personal 'jump-start' on a spell, not as a system that "does everything for you". What if you want to change how something works? With mine, it's rather simple, as I left it relatively open for change. You see attachment, you see Get-attachment, and you see the leak-cleaning, and that's pretty much all there is to the system. In yours, all you see is "Callback area" (How do you end the spell with this?), "Init area" and "clean leaks" area. The user has to read through tons of code to understand how the attachment works, just so they can modify something.

EDIT:

JASS:
        call SetCSData(this.t,0)
        call PauseTimer(this.t)
        call DestroyTimer(this.t)
        set this.t = null
        set this.c = null

Ahahahahahahahaahaha :D

What?
 

Romek

Super Moderator
Reaction score
963
> Please explain further - what is wrong with the coding style exactly?
Not only the style, the actual coding itself is quite terrible.

> What?
He's probably laughing at the fact that you null the CSData, and destroy the timer which is a member of the struct.
And null globals while you're at it.

You can easily recycle timers that are used in structs.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
> What?
He's probably laughing at the fact that you null the CSData, and destroy the timer which is a member of the struct.
And null globals while you're at it.

You can easily recycle timers that are used in structs.

>destroy the timer which is a member of the struct:

Anything wrong with that, other than the fact that it can be easily recycled?

>And you null globals while you're at it.

What happens if you have 20 spells go off at one time, but the most that go off later in the game is only 10? You've got 10 * the number of globals not nulled, correct? I thought the only reason globals didn't have to be nulled was because they are re-used. If you don't reuse those globals for the rest of the game, then they just sit in memory. Granted, it's not many leaks, but why not clear it to keep the game running faster?
 

SerraAvenger

Cuz I can
Reaction score
234
JASS:

How do you end the spell with this


intuitively (to me, as you say), you can stop my spell with .destroy(), which also calls the .clear method.

Why would a user of your script, which is supposed to be easy to use, no configuration, want to modify your script to add in unit attaching, or change the attachment system? Obviously, these are both user-preferences, which is why I only made the attachment system for myself.

because adding in PUI attachment is one line.
Removing all PUI calls or importing PUI just for one spell is much more work (yes I know, PUI is a great system and almost everybody uses it. But still, some might not.)

My system doesn't use no Attachment.

not as a system that "does everything for you".

My template is a backbone. You can always add anything. All that actually cannot be changed is the space allocation for the individual structs as well as the initialisation. And none of that needs to be changed by the user anyway.

In yours, all you see is "Callback area" "Init area" and "clean leaks" area.
That is all you can do with structs, allright? Destroy, create and loop through.

Not only the style, the actual coding itself is quite terrible.

As long as you don't tell me what needs to be improved, I can't fix it. Please give me examples so I see what you mean.


I use PUI so that I can attach the struct to the unit (caster), as well as the timer, to make the spell flexible. This way, if you need to "end any charge spells that might be occuring", you simply pick the units, and call the release method.

The user may use PUI to attach the struct to his units and do the same with hardly an effort.

JASS:
private struct data
    //add variables here
    //! runtextmacro PUI()


Attach the struct to the casting unit in the init method;
Now in the clear method, add something to free the struct from the unit and you're done. Three additional lines to get a system with PUI. If that is not flexible, I don't know what is.
 

Romek

Super Moderator
Reaction score
963
> Anything wrong with that, other than the fact that it can be easily recycled?
You answered your own question there. :)

JASS:
private function INIT_REALLY takes nothing returns nothing
    call TriggerRegisterAnyUnitEventBJ( SpellTrigger, EVENT )
    call TriggerAddAction( SpellTrigger, function Spell_Actions )
endfunction

private function INIT takes nothing returns nothing
    call ExecuteFunc( SCOPE_PRIVATE + "INIT_REALLY" )
endfunction

Why not just use the simple INIT function for the initialization, instead of then executing another function?

Heck, while you're at it, why not use the onInit method in the struct? Everything else is in a struct anyway.
And all the rest is quite messy. You have a lot of random method calls for minor tasks such as nulling.

I don't see why a spell template needs to be so complicated anyway.
I just wrote this one; It's pretty basic, and it does everything that needs to be done. Nothing more.
JASS:
scope NAME initializer Init

    globals
        private constant integer ID = 0
        
        private constant real TIMEOUT = 0.03125
    endglobals
    
    private keyword Data
    
    globals
        private Data array D
        private integer I = 0
        private timer T = CreateTimer()
    endglobals
    
    private struct Data
        // Struct Members
    endstruct
    
    private function Expire takes nothing returns nothing
        local integer i = 0
        local Data d
        loop
            exitwhen i > I
            set d = D<i>
            // Things to do on each expiration of the timer.
            if (SomeCondition) then // For the spell to stop.
                set D<i> = D[ I]
                set I = I - 1
                call d.destroy()
            endif
            
            set i = i + 1
        endloop
        if I == 0 then
            call PauseTimer(T)
        endif
    endfunction

    private function Act takes nothing returns nothing
        local Data d = Data.create()
        // Some stuff to do when the spell starts.
        set I = I + 1
        set D[ I] = d
        if I == 1 then
            call TimerStart(T, TIMEOUT, true, function Expire)
        endif
    endfunction

    private function Cond takes nothing returns boolean
        return GetSpellAbilityId() == ID
    endfunction 

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function Cond))
        call TriggerAddAction(t, function Act)
    endfunction

endscope
</i></i>
 

saw792

Is known to say things. That is all.
Reaction score
280
Darthfett said:
What happens if you have 20 spells go off at one time, but the most that go off later in the game is only 10? You've got 10 * the number of globals not nulled, correct? I thought the only reason globals didn't have to be nulled was because they are re-used. If you don't reuse those globals for the rest of the game, then they just sit in memory. Granted, it's not many leaks, but why not clear it to keep the game running faster?
I believe global array data is already allocated memory when created, so nulling them won't actually save any memory at all.
 

emjlr3

Change can be a good thing
Reaction score
395
holy ass finally one that makes sense, go Romek

my only addition would be another condition function, for grou manipulation and such, you usually need one of those
 

Viikuna

No Marlo no game.
Reaction score
265
Me wants to post too, yeppee!

( My template pwns your templates. Now someone try to pwn my template pls. )

JASS:
scope Template initializer init

globals
    private constant integer ABILITY_ID = &#039;yepp&#039; // no sweat
    private constant real TIMEOUT = .1
endglobals

private struct ability

    boolean end=false
    
    static ability array array
    static timer timer=CreateTimer()
    static filterfunc F
    static integer Total=0
    static ability temp
    group group
    
    static method onInit takes nothing returns nothing
        set .F=Filter(function ability.Filttemellarung)
    endmethod

    static method Filttemellarung takes nothing returns boolean
         local ability a=ability.temp
          &lt; youm might wanna change this  &gt;   
         return false
    endmethod
    
    static method create takes nothing returns ability
        local ability a=ability.allocate()
        if a.group==null then
             set a.group=CreateGroup()
        else
             call GroupClear(a.group)
        endif 
       
       &lt; do some neat stuff here  &gt;
       
        return a
    endmethod
   
    static method periodic takes nothing returns nothing
        local integer i=0
        local ability a
        loop
            exitwhen i&gt;=.Total
            set a=ability.array<i>
            if a.end then
                call a.destroy()
                set .Total=.Total-1
                if .Total&gt;0 then
                    set .array<i>=.array[.Total]
                    set i=i-1
                else
                    call PauseTimer(.timer)
                endif
            else
              
              &lt;  Periodic stuff here  &gt;

            endif
            set i=i+1
        endloop
    endmethod
    
endstruct

private function SpellEffect takes nothing returns boolean
    if GetSpellAbilityId()==ABILITY_ID then
         if ability.Total==0 then
             call TimerStart(ability.timer,TIMEOUT,true,function ability.periodic)
         endif
         set ability.array[ability.Total]=ability.create( &lt; something &gt; )
         set ability.Total=ability.Total+1
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function SpellEffect))
endfunction

endscope</i></i>
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Alright, so a question for all you vJASS spell makers. When you're designing the structure of a spell, what way do you prefer the timers to work? One global timer, looping through all instances of the spell during callback? One timer per instance, recycled timers?

I've always gone the route of one-timer-per-spell-instance, but A lot of people are pointing toward a global timer, it seems. I prefer my method because it's much simpler when it comes to the callback method.

Is it just a preference thing, is it a trade-off between memory amount and processing power, or is one always better than the other?
 

Romek

Super Moderator
Reaction score
963
I use one timer per spell. It's simple, and easy to implement.
Timer Recycling per spell is ridiculous.
One global timer makes your spell much more difficult to implement, and the spell would probably require another external system.
 

emjlr3

Change can be a good thing
Reaction score
395
actually I think you botched that, let me give it a whirl

one timer /instance is easier, but less efficient, since you have more timers expiring
one timer / spell is more efficient, but slightly harder to work with

I prefer one timer / instance when I know there will only be a few instances ever, at max (its easier to use) - otherwise I will go the optimized route (one timer per spell)
 

Romek

Super Moderator
Reaction score
963
> actually I think you botched that, let me give it a whirl
It was a typo. :p

Yeah, I actually always use 1 timer per spell. It's simple and very efficient.
It's also easy to implement, and makes the spell easy to import.

I use more than one timer if it's a very low frequency timer, and there are no additional effects (As otherwise, I still use a high-freq for long durations).
 

Cohadar

master of fugue
Reaction score
209
Yeah, I actually always use 1 timer per spell. It's simple and very efficient.
It's also easy to implement, and makes the spell easy to import.

I use more than one timer if it's a very low frequency timer, and there are no additional effects (As otherwise, I still use a high-freq for long durations).
This is a description of TT v4.0
EDIT: no wait, TT is not so confusing. (you use one timer when???)


PS: this thread is silly.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top