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

Custom Event Data

Level 31
Joined
Jul 10, 2007
Messages
6,306
This tutorial is meant to discuss recursion with event data assigned using your own custom event triggers. Without proper recursion, the wrong data may be accessed.

When events are run, data is normally associated with that event.
GetTriggerUnit
GetAttacker
GetTriggerPlayer

When creating custom events, it is easy enough to do this
JASS:
globals
    private trigger myEvent = CreateTrigger()
    private integer data = 0
endglobals

function OnMyEvent takes boolexpr c returns nothing
    call TriggerAddCondition(myEvent, c)
endfunction
function FireMyEvent takes integer d returns nothing
    set data = d
    call TriggerEvaluate(myEvent)
endfunction
function GetEventData takes nothing returns nothing
    return data
endfunction

But this can result in a problem.
JASS:
function Test takes nothing returns boolean
    local integer d = GetEventData() //d is now 1
    if (d == 1) then
        call FireMyEvent(2) //set d to 2
    endif
    if (d != GetEventData()) then
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "RECURSION ERROR!")
    endif
    return false //to prevent trigger actions from being run for no reason
endfunction
function Init takes nothing returns nothing
    call OnMyEvent(Condition(function test))
    call FireMyEvent(1) //data is now 1
endfunction

In events, event data should always be the same data as it was when the event first fired. In the above scenario, the data was first 1 but then becomes corrupted at 2. While this doesn't seem to be an issue, it can be a serious issue.

JASS:
//this will fire second
function Test2 takes nothing returns boolean
    //remember that the event data become corrupt on the first fire?
    local integer d = GetEventData() //2 on first and second fires
    return false
endfunction

//this will fire first
function Test takes nothing returns boolean
    local integer d = GetEventData() //d is now 1
    if (d == 1) then
        call FireMyEvent(2) //set d to 2
    endif
    if (d != GetEventData()) then
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "RECURSION ERROR!")
    endif
    return false //to prevent trigger actions from being run for no reason
endfunction
function Init takes nothing returns nothing
    call OnMyEvent(Condition(function Test))
    call OnMyEvent(Condition(function Test2))
    call FireMyEvent(1) //data is now 1
endfunction

And thus the major recursion error.

Fire (1)
----run test1 (1->2)
--------Fire (2)
------------run test1 (2)
------------run test2 (2)
----run test2 (2)

There are two methods for solving this recursion error. The first poor way is to create an array type stack.

JASS:
globals
    private trigger myEvent = CreateTrigger()
    private integer stack = 0
    private integer array data
endglobals

function OnMyEvent takes boolexpr c returns nothing
    call TriggerAddCondition(myEvent, c)
endfunction
function FireMyEvent takes integer d returns nothing
    set stack = stack + 1 //increase the stack, thus making a new spot for event data
    set data[stack] = d //event the new spot to our current data
    call TriggerEvaluate(myEvent) //fire
    set stack = stack - 1 //revert to previous data
endfunction
function GetEventData takes nothing returns nothing
    return data[stack]
endfunction

If I were to fire the second example, it would run in this order-
Test1- data 1
Test1- data 2
Test2- data 2
Test2- data 1

For a clearer understanding-
Fire (1)
----run test1 (1)
--------Fire (2)
------------run test1 (2)
------------run test2 (2)
----run test2 (1)

As can be seen, the data is maintained ; D.

The other and better way is by using locals to store the data to save on an array and a global declaration.

JASS:
globals
    private trigger myEvent = CreateTrigger()
    private integer data = 0
endglobals

function OnMyEvent takes boolexpr c returns nothing
    call TriggerAddCondition(myEvent, c)
endfunction
function FireMyEvent takes integer d returns nothing
    local integer prev = data //store the previous data in a local
    set data = d
    call TriggerEvaluate(myEvent)
    set data = prev //set the data back to the previous value
endfunction
function GetEventData takes nothing returns nothing
    return data
endfunction

This essentially does the same thing as the stack method but faster. Array reads are also quite a bit slower than var reads, so it's a better design.
 
Last edited:
Level 26
Joined
Aug 18, 2009
Messages
4,097
When creating custom events, it is easy enough to do this

So you are adding all your actions to a single trigger, thus running all at once and having said problem. Event responses are one thing, but I would surely also like to set the order in which my actions are run. This may originate from different positions/times, so cannot just write one below the other.

Then again, this question should be raised: How shall this react when a triggered action deletes another one still remaining. Shall it be left out? When adding a new action while executing, shall the new one already be included? That are the cases for now at least.
 
Top