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

Multi-unit Instanceability With Timers (vJASS)

Level 17
Joined
Jun 17, 2007
Messages
1,433
This tutorial requires a decent understanding of vJASS to fully comprehend. A read through the JASSHelper manual should be enough.

This tutorial will cover two methods of achieving multi-unit instance-ability using two methods; each ideal for different tasks.


Many of you may have read Dynasti's tutorial on struct indexing, however, that code isn't modulare. Certainly you'd rather not have to create a global timer, a global integer, an integer array, etc... . This is where the glory of TimedLoop comes in. Here is an example of the standard form of struct indexing:

JASS:
scope SomeSpell initializer Init

globals
    private constant integer SPELL_ID = 'A000'
    private constant real HP = 5
    private constant real INTERVAL = 0.1
    private timer Tim = CreateTimer()
    private integer array Queue
    private integer Total = 0
endglobals

struct SomeStruct

    integer x = 0
    unit    u = null

    static method Loop takes nothing returns nothing
        local thistype dat
        local integer i = 0
        loop
            exitwhen i >= Total
            set dat = Queue[i]
            if dat.x < 10 then
                set dat.x = dat.x + 1
                call SetWidgetLife(dat.u, GetWidgetLife(dat.u) + HP)
            else
                call dat.destroy()
                set Total = Total - 1
                set Queue[i] = Queue[Total]
                set i = i - 1
            endif
            set i = i + 1
        endloop
        if Total == 0 then
            call PauseTimer(Tim)
        endif
    endmethod
    
    static method create takes unit u returns thistype
        local thistype dat = thistype.allocate()
        set dat.u = u
        if Total == 0 then
            call TimerStart(Tim, INTERVAL, true, function SomeStruct.Loop)
        endif
        set Queue[Total] = dat
        set Total = Total + 1
        return dat
    endmethod
    
endstruct

private function Actions takes nothing returns nothing
    call SomeStruct.create(GetTriggerUnit())
endfunction

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

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

endscope
The above is a simple spell which heals a unit every .10 seconds. The problem is that none of this code is being reused, and it's adding unneeded complexity to our script. 65 lines could easilly be reduced. Surely we can do better? This is where TimedLoop comes in. To use TimedLoop, we have to 'implement' it. I'll comment the next script to help you understand it.

JASS:
scope SomeSpell initializer Init

globals
    private constant integer SPELL_ID = 'A000'
    private constant real HP = 5
endglobals

struct SomeStruct

    integer x = 0
    unit    u = null
    
    implement TimedLoop

    method onTimedLoop takes nothing returns boolean
        //The method must be name 'onTimedLoop' and must
        //return boolean.
        set .x = .x + 1
        call SetWidgetLife(.u, GetWidgetLife(.u) + HP)
        if .x >= 10 then
            return TimedLoop_STOP
            //When you want it to stop an instace of a struct, you must return false.
            //the TimedLoop_STOP is simply a constant.
        endif
        return TimedLoop_CONTINUE
        //Otherwise, return true
    endmethod
    
    static method create takes unit u returns thistype
        local thistype dat = thistype.allocate()
        set dat.u = u
        call dat.startTimedLoop()
        //This must be called once for every struct instance.
        return dat
    endmethod
    
endstruct

private function Actions takes nothing returns nothing
    call SomeStruct.create(GetTriggerUnit())
endfunction

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

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

endscope





I'll finish the second half later today.
 
Last edited:
Level 11
Joined
Feb 22, 2006
Messages
752
First of all...what is TimedLoop?

Second of all...this seems more like an advertisement for TimedLoop than a tutorial because really all it says is: use TimedLoop because it's better. A tutorial needs to be more. It needs to explain the concepts behind this stuff; explaining what TimedLoop does is a good start, and also what are the reasons that you might want to use it over other methods. Give some examples of good situations in which to use it and examples of situations where you want to avoid it.
 
Level 17
Joined
Jun 17, 2007
Messages
1,433
First of all...what is TimedLoop?
TimedLoop. Yeah, I forgot to hyperlink my OP.

Second of all...this seems more like an advertisement for TimedLoop than a tutorial because really all it says is: use TimedLoop because it's better.
Yes, it is better. But it's more than an advertisement.


A tutorial needs to be more. It needs to explain the concepts behind this stuff; explaining what TimedLoop does is a good start,
Since when did tutorials need to explain the concepts behind anything? I'm teaching by example and supplying information to complete a specific task.

and also what are the reasons that you might want to use it over other methods.
Do you see how the second set of hidden tags are empty? I'd have to finish the part on the other method before explaining when to use one over the other.

Just like arp said, I have no idea why I would use this over the standard method.
Have fun wasting your time.
 
Level 11
Joined
Feb 22, 2006
Messages
752
Since when did tutorials need to explain the concepts behind anything?

Not all tutorials do, but tutorials on open-ended coding should. There is no way that the author can anticipate everything that readers may want to do with their code, so inevitably there's going to be somebody that needs to know how to create struct indexing or struct attachment, but needs an implementation that is not compatible with the specific examples you're giving. If your tutorial just gives step by step instructions on how to implement these examples, then that person is out of luck. He'll basically have to reverse engineer your code to figure out what's going on. You can save him the trouble by including that info in the tutorial.
 
Since when did tutorials need to explain the concepts behind anything? I'm teaching by example and supplying information to complete a specific task.

That's what they should always do. That's what a tutorial is, no? To explain something? If you're giving someone a wad of code you shouldn't just say "here's some example code, now you figure out the rest". You need to go through step-by-step and explain how to use it and what each part is for, etc.
 
Top