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

TriggerAction Behavior

Status
Not open for further replies.
Good day, everyone.

The TriggerAction in the JASS world had seen better days, but when triggerconditions came into light, it was discovered that triggeractions leaked (caused handle-id count to increase), even when DestroyTrigger was invoked. This led to a radical shift in JASS, from the usage of triggeraction, to a merged triggercondition.

In the test environment of the making of this thread, (v.1.29+ onwards), it was found that triggeractions alone do not cause leaks when destroyed, but when triggerconditions are introduced, the resulting effect would be that triggeractions ended up leaking. This suggests that the list of triggeractions is possibly nested within the list of triggerconditions, if the list of triggerconditions exists in the first place (e.g. there is at least one triggercondition).

For the reason why they (the lists) could not possibly be separate from each other, the presence of leaks reaffirms the stance above, once triggerconditions are added. If they are separate from each other, then the exposed native DestroyTrigger would destroy both lists without much trouble. This leads to a possible data structure of a trigger:

NOTE: Pseudo-code below
JASS:
import GenericList  // Design of pseudo-code inspired by Bannar's ListT

// classes in this context are considered Abstract Classes
class Callback
    readonly functionPointer func
 
    stub method execute takes nothing returns nothing

    delete
        this.func = null
    enddelete

    construct takes functionPointer func
        this.func = func
    endconstruct
endclass

struct TriggerAction extends Callback
    override method execute takes nothing returns nothing
        this.func()
    endmethod
endstruct

struct TriggerCondition extends Callback
    override method execute takes nothing returns Boolean
        return this.func()
    endmethod
endstruct

struct Trigger
    List<T> executeList

    method evaluate takes nothing returns boolean
        if (executeList.typeOf == TriggerAction)
            return true
        ListIterator<TriggerCondition> iter = executeList.first
        while (iter.data.execute()) or (iter.next != 0)
            // As the list of triggeractions is contained in the last node, exclude it, as it can cause issues
            iter = iter.next
        return true
    endmethod

    method execute takes nothing returns nothing
        let tempList = (List<TriggerAction>) executeList.last.data
        ListIterator<TriggerAction> iter = tempList.first
        while (iter != 0)
            iter.data.execute()
            iter = iter.next
    endmethod

    method clearConditions takes nothing returns nothing
        if (executeList.typeOf == TriggerAction)
            return
        ListIterator<TriggerCondition> iter = executeList.first
        while (iter != 0)
            if (iter.typeOf == TriggerCondition>
                del iter.data    // destroy iter.data
            executeList.erase(iter)
            iter = executeList.first
    endmethod

    method clearActions takes nothing returns nothing
        if (executeList.typeOf == TriggerAction)
            ListIterator<TriggerAction> iter = executeList.first
            while (iter != 0)
                del iter.data
                executeList.erase(iter)
                iter = executeList.first
    endmethod

    method addCondition takes functionPointer func returns TriggerCondition
        if (executeList.typeOf == TriggerAction)
            let newList = new List<TriggerCondition>()
            newList<T>.unshift( (TriggerCondition) executeList)
            executeList = newList
        let tempListTypeCast = executeList.last
        let result = new TriggerCondition(func)

        newList.erase(tempListTypeCast)
        newList.push(result)
        newList.push(tempListTypeCast)
        return result
    endmethod

    method addAction takes functionPointer func returns TriggerAction
        let result = new TriggerAction(func)
        if (executeList.typeOf == TriggerCondition)
            let tempList = (List<TriggerAction>) executeList.last.data
            tempList.push(result)
        else
             executeList.push(result)
        return result
    endmethod

    method removeCondition takes TriggerCondition cond returns boolean
       if (executeList.typeOf == TriggerAction)
           return false
       let removedNode = executeList.find(cond)  // type is ListIterator<TriggerCondition>
       if removedNode == null
           return false
       del removedNode.data
       executeList.erase(removedNode)
       return true
    endmethod

    method removeAction takes TriggerAction act returns boolean
        if (executeList.typeOf == TriggerCondition)
            let tempList = (List<TriggerAction>) executeList.last.data
        else
            let tempList = executeList
        let removedNode = tempList.find(act)  // type is ListIterator<TriggerAction>
        if removedNode == null
            return false
        del removedNode.data
        tempList.erase(removedNode)
        return true
    endmethod

    delete
        .clearConditions()
        .clearActions()

        del .executeList
        .executeList = null[/COLOR][/FONT][/LEFT]
    enddelete

    construct
        executeList = new List<TriggerAction>()
    endconstruct
endstruct

This is not a new finding, as some have discovered this before me, but this hopes to improve on how triggers and their subsets, triggeractions and triggerconditions, are internally handled.

EDIT:

Added a Test Map
 

Attachments

  • TriggerActionTest.w3m
    16.9 KB · Views: 38
Last edited:
Status
Not open for further replies.
Top