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

[vJASS] Trigger inside struct

Status
Not open for further replies.

EdgeOfChaos

E

EdgeOfChaos

I don't even know if what I'm trying to do is possible, but I'd very much like a way to do this.

I want to use a timer inside of a struct to register a piece of code to it, whenever the struct is instantiated. This is what I have:

JASS:
struct Patroler extends MazeObstacle

    real x2
    real y2
    timer t1
    timer t2
    
    method getX2 takes nothing returns real
        return .x2
    endmethod

    method setX2 takes real x2 returns nothing
        set .x2 = x2
    endmethod

    method getY2 takes nothing returns real
        return .y2
    endmethod

    method setY2 takes real y2 returns nothing
        set .y2 = y2
    endmethod
    
    method checkArea1 takes nothing returns nothing
    
    endmethod
    
    method start takes nothing returns nothing
        set .t1 = CreateTimer()
        set .t2 = CreateTimer()
        call TimerStart(.t1,0.33,true,function .checkArea1)
    endmethod
    
    static method create takes integer id, boolean kill, real x, real y, real x2, real y2 returns Patroler
        local Patroler new = Patroler.allocate(id,kill,x,y)
        call new.setX2(x2)
        call new.setY2(y2)
        call new.start()
        return new
    endmethod
endstruct

It gives me a syntax error on this line:
JASS:
call TimerStart(.t1,0.33,true,function .checkArea1)

It's obviously not detecting the piece of code "checkArea1". How would I get it to detect this, if possible?
 
First your timer callback function must be static,
because it's called by timer and not by an instance.

And insisde struct the functions (static methods) are referenced like:

call TimerStart(.t1, 0.33, true, function checkArea1)
->
call TimerStart(.t1, 0.33, true, function thistype.checkArea1)

Hint: Always use thistype over the struct name. :)
 

EdgeOfChaos

E

EdgeOfChaos

From my knowledge of Static, that will make it so there is one trigger for every instance of the struct I create, right? I want it to create a new timer and run its own code for every instance created of the struct (as I need to use the instance variables of the struct). Is there any way to do that?
 
Level 23
Joined
Feb 6, 2014
Messages
2,466
From my knowledge of Static, that will make it so there is one trigger for every instance of the struct I create, right? I want it to create a new timer and run its own code for every instance created of the struct (as I need to use the instance variables of the struct). Is there any way to do that?

Take a look at TimerUtils or do it yourself by storing the instance in the hash together with the created timer id as one of the hash keys.
 
'static' actually doesn't make any implications about triggers.

To create a new timer and run code for every instance created for the struct, you will need to attach data to the timer. Typically, you can do this with hashtables or a timer system. The simplest timer system to use is TimerUtils.

You would copy the library into your map, and then use it like so:
JASS:
struct Patroler extends MazeObstacle

    real x2
    real y2
    timer t1
    timer t2
    
    method getX2 takes nothing returns real
        return .x2
    endmethod

    method setX2 takes real x2 returns nothing
        set .x2 = x2
    endmethod

    method getY2 takes nothing returns real
        return .y2
    endmethod

    method setY2 takes real y2 returns nothing
        set .y2 = y2
    endmethod
    
    static method checkArea1 takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer()) // fetch instance
        // ... write your code as normal
        // ... instead of DestroyTimer(.t1), use ReleaseTimer(.t1)
    endmethod
    
    method start takes nothing returns nothing
        set .t1 = NewTimer()  // Use 'NewTimer' to fetch a timer
        set .t2 = NewTimer()
        call SetTimerData(.t1, this) // Attach the instance to it
        call TimerStart(.t1,0.33,true,function thistype.checkArea1)
    endmethod
    
    static method create takes integer id, boolean kill, real x, real y, real x2, real y2 returns Patroler
        local Patroler new = Patroler.allocate(id,kill,x,y)
        call new.setX2(x2)
        call new.setY2(y2)
        call new.start()
        return new
    endmethod
endstruct
 
You can also use hashtables if you don't like TimerUtils.

Basicly, attaching a struct to a timer would look like this then:
call SaveInteger(HASH, GetHandleId(t), 0, this)

And retrieving it in the static callback like this:
JASS:
local timer t = GetExpiredTimer()
local thistype this = LoadInteger(HASH, GetHandleId(t), 0)

//... your code here

//don't forget to clean up the leaks
call FlushChildHashtable(HASH, GetHandleId(t)
call DestroyTimer(t)
set t = null


Using timer systems is obsolete since the implementation of hashtables in 1.24.
 
@Zwieb why are you so against TimerUtils? It is not much overhead in my opinion.
I'm not against TimerUtils per se. I just don't see a reason to use it anymore now that we got hashtables.

There imho are many problems with TimerUtils that just make it not worth it for me:
1) it's not a hive resource (surely, that is a weak reason, but theoreticly, this is an enforced requirement in the hive Jass section)
2) it is one of far too many timer systems out there (CTL, T32, you name it), since the WC3 community failed to communicate a standard when it still mattered... if every spell imported uses a different timer system, you end up imported a huge amount of junk systems in your map just to support the spells
3) It's literally importing 400 lines of code just to save one additional function call. Btw, for the same reason I don't use unit indexers anymore (hashtables do the same).
4) no TESH syntax highlighting for the timer. Call me a weirdo, but I don't like having huge blocks of non-highlighted code.

There's a lot of irrational dislike of using other peoples' systems/libraries in the wc3 modding community for some reason.
There is also a lot of baseless accusations.
 
Level 23
Joined
Feb 6, 2014
Messages
2,466
There imho are many problems with TimerUtils that just make it not worth it for me:
1) it's not a hive resource (surely, that is a weak reason, but theoreticly, this is an enforced requirement in the hive Jass section)
2) it is one of far too many timer systems out there (CTL, T32, you name it), since the WC3 community failed to communicate a standard when it still mattered... if every spell imported uses a different timer system, you end up imported a huge amount of junk systems in your map just to support the spells
3) It's literally importing 400 lines of code just to save one additional function call. Btw, for the same reason I don't use unit indexers anymore (hashtables do the same).
4) no TESH syntax highlighting for the timer. Call me a weirdo, but I don't like having huge blocks of non-highlighted code.
1. But..but..Vexorian made TimerUtils. And also a hiver developed TimerUtilsEx which is resting peacefully in the graveyard.
2. CTL/T32 are for having a single timer, in loops, far different than TimerUtils.
3. Number of lines don't matter, functionality does. The 400 lines is caused by recycling the timer.
4. TESH can be configured to underline specific functions (the blue underline). And that's only three lines of uncolored text (NewTimerEx, GetTimerData and ReleaseTimer).
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
Stuff that timer systems provide:
*Timer recycling. Negligible gains.
*Registering callbacks. Saves some lines at a negligible cost.
*Callback combining. Potentially massive performance gains, but not for everyone.

For most people it's not worth it to have a timer system.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
TUtils is the closest thing to a standard that's ever existed in the Jass community. Perhaps it's somewhat habit as most of the people using it (myself included) spent tons of time using GC and H2I and then structs when they existed, and hashtables showed up relatively late in our modding lives. Incidentally, unit indexers usually give you some extra stuff (for example, if I recall correctly grim's at least still contains a few extra events).
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
The problem is a lot of the systems are out right poorly engineered or depend on strange exotic syntax which might not fit your personal coding style.

At a low level there are 2 ways to achieve dynamic function calls. First is by attaching the function to a trigger as either actions or conditions (different restrictions), using a trigger variable as your function reference variable and then running the trigger at your call site. The other is by using the string of the function name with a string variable as your function reference variable and then using the native to call functions by name at your call site. Both approaches rely on register variables (global variables which are used for specific purposes) to pass arguments to the function. At the start of the function you need to dump all the register variable passed arguments to local variables. The register variables can be recycled for different purposes (such as other function signatures) since they will still be used in a compatible way.

I think vJASS uses the trigger approach with register variables for argument passing wrapped in some neat "more readable" syntax. This approach can also be used to start new trigger threads (SC2 GUI uses something very similar).
 
The problem is a lot of the systems are out right poorly engineered or depend on strange exotic syntax which might not fit your personal coding style.

At a low level there are 2 ways to achieve dynamic function calls. First is by attaching the function to a trigger as either actions or conditions (different restrictions), using a trigger variable as your function reference variable and then running the trigger at your call site. The other is by using the string of the function name with a string variable as your function reference variable and then using the native to call functions by name at your call site. Both approaches rely on register variables (global variables which are used for specific purposes) to pass arguments to the function. At the start of the function you need to dump all the register variable passed arguments to local variables. The register variables can be recycled for different purposes (such as other function signatures) since they will still be used in a compatible way.

I think vJASS uses the trigger approach with register variables for argument passing wrapped in some neat "more readable" syntax. This approach can also be used to start new trigger threads (SC2 GUI uses something very similar).
I, uh, don't quite get how that has anything to do with the timer system discussion?
 
Status
Not open for further replies.
Top