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

[Snippet] RefreshTrigger

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Old.
To fill hole from lacking remove-event native. By no means it removes event itself since it's impossible - instead, it replaced given trigger and reapplies data from previous one.
Note:
- All actions have to be applied via conditions in order to restore trigger properly
- code added as condition must return false
- Does not support TriggerRemoveCondition native

Tell me if support for TriggerRemoveCondition should be given - can upload 2nd version for those who really need it. Although TriggerRemoveNative is not in much of a use since it requires triggercondition instead of boolexpr as a paramether.

Version 3.0.0.0
Simplifies adding and removing conditions.

- renamed again to RefreshTrigger
- code passed through parameter, no longer has to return false
- support for remove condition has been added
- events are no longer removed

JASS:
/*****************************************************************************
*
*    RefreshTrigger v3.0.1.0
*       by Bannar aka Spinnaker
*
*    Simplifies trigger refresh process i.e. adding and removing conditions, as never before.
*
******************************************************************************
*    
*    Requirements:
*       Alloc - choose whatever you like
*       ListTemplate by Bannar
*          hiveworkshop.com/forums/submissions-414/containers-list-t-249011/
*
*    Optional:
*       Table by Bribe
*          hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
*    Important:
*       All actions have to be applied via conditions in order to refresh trigger properly
*
******************************************************************************
*
*    struct Trigger:
*
*       readonly trigger trigger
*          retrieves trigger handle
*
*       static method create takes nothing returns thistype
*          default ctor
*
*       method destroy takes nothing returns nothing
*          default dctor
*
*       method empty takes nothing returns boolean
*          whether trigger has any conditions applied or not
*
*       method conditionCount takes nothing returns integer
*          count of currently added conditions
*
*       method refresh takes nothing returns nothing
*          replaces this trigger with newly created one and reapplies it's conditions
*
*       method addCondition takes code c returns nothing
*          adds condition to this trigger
*
*       method removeCondition takes thistype node returns nothing
*          removes condition node from this trigger
*
*       method clearConditions takes nothing returns nothing
*          clears conditions from this trigger
*
*****************************************************************************/
library RefreshTrigger uses /*
                    */ Alloc /*
                    */ ListTemplate /*
                    */ optional Table

    private struct ConditionItem extends array
        triggercondition condition
        boolexpr bexpr
        implement Alloc

        static method create takes triggercondition tc, boolexpr b returns thistype
            local thistype this = thistype.allocate()
            set this.condition = tc
            set this.bexpr = b
            return this
        endmethod

        method destroy takes nothing returns nothing
            set condition = null
            set bexpr = null
            call deallocate()
        endmethod
    endstruct

    //! runtextmacro DEFINE_LIST("ConditionList", "ConditionItem", "true", "0", "false", "")

    private module TriggerInit
        private static method onInit takes nothing returns nothing
            static if LIBRARY_Table then
                set table = Table.create()
            endif
        endmethod
    endmodule

    struct Trigger extends array
        private ConditionList list
        readonly trigger trigger
        private trigger callback
        implement Alloc

        static if LIBRARY_Table then
            private static Table table
        else
            private static hashtable table = InitHashtable()
        endif

        private static method evaluate takes nothing returns boolean
            static if LIBRARY_Table then
                return TriggerEvaluate( table.trigger[ GetHandleId(GetTriggeringTrigger()) ] )
            else
                return TriggerEvaluate( LoadTriggerHandle(table, 0, GetHandleId(GetTriggeringTrigger()) ) )
            endif
        endmethod

        private method synchronize takes nothing returns nothing
            static if LIBRARY_Table then
                set table.trigger[GetHandleId(trigger)] = callback
            else
                call SaveTriggerHandle(table, 0, GetHandleId(trigger), callback)
            endif
        endmethod

        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.list = ConditionList.create()
            set this.trigger = CreateTrigger()
            set this.callback = CreateTrigger()
            call this.synchronize()
            call TriggerAddCondition(this.trigger, function thistype.evaluate)
            return this
        endmethod

        method destroy takes nothing returns nothing
            static if LIBRARY_Table then
                call table.handle.remove(GetHandleId(trigger))
            else
                call RemoveSavedHandle(table, 0, GetHandleId(trigger))
            endif

            call deallocate()
            call list.destroy()
            set list = 0

            call DestroyTrigger(trigger)
            call DestroyTrigger(callback)
            set trigger = null
            set callback = null
        endmethod

        method empty takes nothing returns boolean
            return list.empty()
        endmethod

        method conditionCount takes nothing returns integer
            return list.size()
        endmethod

        private method findNode takes boolexpr b returns ConditionListNode
            local ConditionListNode node = list.first
            loop
                exitwhen node == 0 or node.data.bexpr == b
                set node = node.next
            endloop
            return node
        endmethod

        method refresh takes nothing returns nothing
            local ConditionListNode node = list.first
            call DestroyTrigger(callback)
            set callback = CreateTrigger()
            loop
                exitwhen node == 0
                set node.data.condition = TriggerAddCondition(callback, node.data.bexpr)
                set node = node.next
            endloop
            call synchronize()
        endmethod

        method addCondition takes code c returns nothing
            local triggercondition tc
            local boolexpr b
            if ( c != null and trigger != null ) then
                set b = Condition(c)
                set tc = TriggerAddCondition(callback, b)
                call list.pushBack( ConditionItem.create(tc, b) )
                set b = null
                set tc = null
            endif
        endmethod

        method removeCondition takes code c returns nothing
            local ConditionListNode node
            local boolexpr b
            if ( c != null and trigger != null ) then
                set b = Condition(c)
                loop
                    set node = findNode(b)
                    exitwhen node == 0
                    call TriggerRemoveCondition(callback, node.data.condition)
                    call list.delete(node)
                endloop
                set b = null
            endif
        endmethod

        method clearConditions takes nothing returns nothing
            call list.clear()
            call TriggerClearConditions(callback)
        endmethod

        implement TriggerInit
    endstruct

endlibrary
Demo code:
JASS:
struct RestoreTriggerTest extends array
    static Trigger trig

    private static method testme takes nothing returns boolean
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "This trigger handle id: "+I2S(GetHandleId(trig.trigger)))
        call CreateUnit(Player(0),'hfoo',0,-100,0)
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        set trig = Trigger.create()

        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "Type \"s\" to summon a footman at your side. Two conditions.")
        call TriggerRegisterPlayerChatEvent(trig.trigger, Player(0), "s", true )
        call trig.addCondition(function thistype.testme)
        call trig.addCondition(function thistype.testme)

        call PolledWait(5.)
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "Conditions have been removed.")
        call trig.removeCondition(function thistype.testme)

        call PolledWait(5.)
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "A single condition has been added.")
        call trig.refresh()
        call trig.addCondition(function thistype.testme)
    endmethod
endstruct
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
There is no other possible way to "unregister" event from trigger then replacing it (trigger) actually.

2nd. There is no native that returns all the conditions applied to given trigger.

Whats wrong with hook with no return value. Jass has it's limits, and even that hooks seem sloppy sometimes there is no other way.

If I've missed a better option to store conditions added to triggers, I'd be pleased to hear.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I don't get it your last post, sorry my english isn't perfect so sometimes I can't catch whats going in ur mind. If you could, elaborate.
Are you suggesting to ignore trigger user data and store boolexpr for given trigger in Table via GetHandleId?

Edit: will fix to use just Table tomorrow; To viewers: please don't repeat stuff that has been already pointed out - I know where my mistake lies ;>
 
Last edited:
For this kind of thing, I'd do a Trigger struct that wraps a trigger object. This way, you can simulate event deletion from a trigger easily. When someone needs event deletion, instead of using the type trigger, he'd use your type Trigger and use methods for adding Conditions, actions and events on his own. :p
This way, you can get rid of the hooks, which are quite expensive O: (WAY TOO EXPENSIVE)
 
Just a quick question: Why would someone actually need this?
The only reason when you would ever need dynamic trigger events is with damage detection systems. And most damage detection systems have the dynamic trigger thing hardcoded in their script (which makes sense, as it makes the whole thing faster and speed is crucial with damage events).

There's only one more event that is not a PlayerUnitEvent, but a specific unit event which is the Attack event. And systems that need this can also just hardcode the dynamic trigger for the sake of speed.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
1st. If you use search enigine, you'd know that from time to time there is requrest/question about "removing" events from trigger.

2nd. Don't tell me about speed dude.. There are many cases where typical system is slower than hardcoded one within the map made for THAT map only. For example - you can skip protection part in many snippets. Next, since some systems contain multiple features it often happens that part of it isn't usefull for you - cut it off.

3rd. Some of jass resources also doesn't make much of a use for common coder, and yet there are there.

4th. This snippet is made for one and one purpose only: you need to "remove" event from trigger -> you got clean way to do it.
 
I don't mind systems with specific and limited uses :p
As long as those uses exist and aren't stupid, it's okay

An example is Nes' Type library. The use is limited to Entity classification, meaning a damage type, a buff type, an ability type, a unit condition type, etc...

This system is limited to trigger refreshing. Some day, someone may find a use for it. It'll be there when the time comes and I don't have a problem if that day never comes because the purpose of the resource is still well defined :p

edit
By the way, AddCondition and ClearConditions should be addCondition and clearConditions so that the API is uniform and consistent :eek:
 
1st. If you use search enigine, you'd know that from time to time there is requrest/question about "removing" events from trigger.
Whenever that request comes up, people usually want to code their own damage detection system. In most cases, people are then suggested to use one of the damage detection systems already available. And if they are intelligent people, they will do so.

2nd. Don't tell me about speed dude.. There are many cases where typical system is slower than hardcoded one within the map made for THAT map only. For example - you can skip protection part in many snippets. Next, since some systems contain multiple features it often happens that part of it isn't usefull for you - cut it off.
Isn't that exactly what I said?

3rd. Some of jass resources also doesn't make much of a use for common coder, and yet there are there.
True. But then again, the purpose is always questionable and is also questioned. Often by me. Seems I'm the only one who actually cares about the purpose of scripts from a mapping perspective...

4th. This snippet is made for one and one purpose only: you need to "remove" event from trigger -> you got clean way to do it.
I did understand that. That's basicly what all dynamic trigger libraries do. No need to repeat that like a mantra. ;)
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
To end this -
Whenever that request comes up, people usually want to code their own damage detection system. In most cases, people are then suggested to use one of the damage detection systems already available. And if they are intelligent people, they will do so.
And if they're not they can go for this script ;>
Isn't that exactly what I said?
^isn't a valid answer since as I (not you) said, RefreshTrigger isn't a unique situation when hardcoding is a way to go if you care about speed and speed only. UnitIndexer -> you dont care about firing events at map init -> cut preloader; TimerUtils -> you don't want so much protection -> cut safety off. I can put multiple examples, but I guess you are smart enough to do it yourself.
True. But then again, the purpose is always questionable and is also questioned. Often by me. Seems I'm the only one who actually cares about the purpose of scripts from a mapping perspective...
I love what Nes/Bribe and meaby few others said about differences between hive/wc3c/thehelper etc. Hive is the most "flexiable" site around, there are no fixed standards and no pesky mods that mix you with dirt if they don't see given resource being used in every map. And ofc, there is no "I own this shit" rule. If you see place for improvement you can go for it. I don't want to link that statement with this small snippet, because there are much more accurate examples in resource section. My answer:
4th. This snippet is made for one and one purpose only: you need to "remove" event from trigger -> you got clean way to do it.


And no, I did no mantra - you wanted my answer, you got it; meaby de javu effect occured to you - but not to me ;)
 
To end this - And if they're not they can go for this script ;>
Well, if they decide not to use a thoroughly tested and approved damage detection system, they will most likely not use triggerRefresh either. ;)


isn't a valid answer since as I (not you) said, RefreshTrigger isn't a unique situation when hardcoding is a way to go if you care about speed and speed only.
That's not the point. It's more that there is only one situation where you actually *need* dynamic events. The infamous onDamage event.
All other things can be covered with static events. If you can come up with just one more example, I will instantly shut my mouth. But the thing is, you can't, because there isn't any. Even the onAttack event I mentioned (I checked that again) is a PlayerUnitEvent.

The reason I mentioned hardcoding was because in this situation it actually matters. Why would you import a whole library to backup one other system that could be done better if it does not make use of it?
This is like building an entire factory just to build one single machine. It makes no sense if you can just build the machine by hand in the first place.

And yes, this also sometimes (actually: quite often) applies to stuff Nes codes. But at least he provides systems that actually make use of those other modules and libraries. So unless you are going to submit your own damage detection system using this, I don't really see a point in it.

UnitIndexer -> you dont care about firing events at map init -> cut preloader; TimerUtils -> you don't want so much protection -> cut safety off. I can put multiple examples, but I guess you are smart enough to do it yourself.
Those are very bad examples to prove your case, as they do something quintessential, that you can not do otherwise. You do not use a unit indexer or a timer system for speed. You use them for what they do.
At least, this was before hashtables got implemented.
I got to say that the addition of hashtables made both UnitIndexing systems and timer systems practically obsolete, unless there's some really specific cases where a system is actually worth the additional overhead, like CTL.


I love what Nes/Bribe and meaby few others said about differences between hive/wc3c/thehelper etc. Hive is the most "flexiable" site around, there are no fixed standards and no pesky mods that mix you with dirt if they don't see given resource being used in every map. And ofc, there is no "I own this shit" rule. If you see place for improvement you can go for it. I don't want to link that statement with this small snippet, because there are much more accurate examples in resource section.
Meh... you have a point here. Alright, I'll keep shut. It doesn't matter anyway, as I'm not saying it's a bad resource in general, just has very very limited use.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
there are no fixed standards

Actually, Hive does have standards now (at least in the JASS section).

There are commenting standards, API standards, and naming standards.

There are also quality standards : P.

In the Spells section, anything that works can get approved, it'll just get a low rating ;D (standards still applied, just not enforced).
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Nes.. you cant compare those to wc3c ones =)

Zwiebelchen, you scored some points ;> I admire that like none else you actually write more than a sentence lol.
Yeah mostly even on my own I'd prefer to implement/hardcode something like this into my resource directly. But again, now someone can just link or CnP this in Trigger/Scripts when noobs ask for solution. You talk about onAttack/onDamage events only - what is true - yet I found myself helping out "noobs" with reseting trigger for events like Hero Picked an Item / Unit dies and such shit. I don't know what they need this for but do I care?
 
The standards are only moderately flexible, I mean, we need to all agree on a standard for naming and API because it will allow us to write and implement code that is consistent with itself.
Without standards for naming, code can look like this:

JASS:
library ThisLibrary requires RequiredLibrary, REQUIRED_LIBRARY, REQLIB, required_library, __requiredlibrary
    globals
        constant integer Stuff = 0
        private integer TEMPDATA0 = 0
        private integer TempData1 = 0
    endglobals
    
    struct my_struct extends NOTMYSTRUCT // reference to WINAPI structs :D
        integer MY_INT
        integer someOtherInteger

        static method My_Method takes nothing returns nothing
            call some_other_struct.METHOD()
        endmethod
    endstruct

endlibrary

With a single convention everyone follows here, this is what that same code will look like:

JASS:
library ThisLibrary requires RequiredLibrary0, RequiredLibrary1, RequiredLibrary2, RequiredLibrary3, RequiredLibrary4
    globals
        constant integer STUFF = 0
        private integer tempData0 = 0
        private integer tempData1 = 0
    endglobals
    
    struct MyStruct extends NotMyStruct
        integer myInt
        integer someOtherInteger

        static method myMethod takes nothing returns nothing
            call SomeOtherStruct.method()
        endmethod
    endstruct

endlibrary

Documentation must contain a listing of the API, a library name and an author name + required resource links at the very least, and this is a pretty good thing to have IMO

I wouldn't force someone to make a library use UnitIndexer for example. Using AIDS or AutoIndex is fine too, because lots of users use those.
Table by Vexorian is acceptable over Table by Bribe, but I recommend Table by Bribe because it's clearly the better resource, with all due respect to our beloved Vexorian of course <3

I wouldn't say that's 100% flexible, but at the end of the day, I find it okay :p
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Sorry for going off topic a bit more

We have other standards too. Like in naming, if you do a method call setHp, we'll require you to change it to a method operator called hp. If you don't abstract something out, like building an entire unit indexer in your system, we will require you to use a unit indexer (building one in is a no go). These strict requirements are only present in the JASS section. In Spells, you can get away with practically anything, we'll just deduct your rating for every offense : p.

There are design considerations too. If we clearly see a better design and your stuff isn't using it, your thing won't get approved until it uses that design : |. The best design wins. In the case where there are dif pros/cons to two different designs, then two entire different resources doing the exact same thing utilizing those two different designs are acceptable. It isn't acceptable when a resource is submitted that uses an inferior design or the same design as a present resource.

There are lots of other things too : ).

We're as strict as wc3c is in the JASS section, but in a different way. We accept more stuff than they do =). Also, once again, all of these strict rules are only for the JASS section. Some people have to literally rewrite their entire resource several times before it'll get approved. Other people's resources just get dropped because their designs are inferior, thus offering nothing new. If those designs were to be perfected, then they would likely go to one of the current approved designs (as the user likely won't come up with something entirely new on their own).

The first issue with this *was* the design, but that issue was fixed. It was a highly invasive system through the use of hooks. All triggers, even those that would have never used this system, would have been latched in, which was not acceptable. It was not a performance issue.

The current issue, for me at least, is the design. Using Or is a cheap way of getting around a good design. Stop being lazy and use linked list. Mag recommended Table, but I doubt that you are going to have over 8000 different conditions in a map. Consider how many triggers would ever use a system like this... very few if any.

Fix that and then I'll give this another look =).
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Lol Troll-Brain; you.. you troll ;>
Look at the bottom of Jass Resources: there are 7 (!!!) mods. So sweet. I don't know why Nes dislikes wolf and prefers pitty grunt.

I got impression that after writing:
I admire that like none else you actually write more than a sentence lol.
there were some nice replies =)

I cant be lazy working 8h/day but yeah linked list is a good idea. Will do.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated. Assuming that number of members created via create() method + number of added conditions will be < 8191 I've decided to use singular list with front and prev marking the tail of conditions for given handle.

Heard that Or() native is slower than adding conditions. But does addition of listed list speed things significantly? Remember that in such case, you need to call TriggerAddCondition once per condition node in list.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
RemoveCondition native takes triggercondition, yet I don't belive this is what method removeCondition should take.

AddCondition native returns triggercondition, thats why currently addCondition method mimics navite.

Do you suggest to return Trigger type instead of triggercondition in addCondition? Next, if removeCondition method would require Trigger type paramether, then given trigger should be cleared of conditions, and then reapplied with them again except for taken node.
Am I correct?
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Don't you think that storing conditions in Trigger type isn't intuitive? Naming sucks ;/

JASS:
local Trigger trig = Trigger.create()
local Trigger condition = trig.add(function thistype.testme)

call trig.remove(condition)
Meaby split code between Trigger type and additional one like Conditions/Boolexpr or smthing.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Hello, ressurecting this. Take a look on rewritten lib below, I guess approach is better than last time. As of now, it allows to add conditions via code func with .removeCondition method sticking to the same pattern. Whatmore, this gives you an opportunity to remove conditions via code func instead of triggercondition object. Remember though, removeCondition will clear trigger from every instance of such condition - afterall you could add this condition multiple times.
I could add a remove-first-found method if community wishes to.

Restore method will refresh trigger and apply all conditions that weren't removed before.
JASS:
library RestoreTrigger uses optional Alloc

static if not LIBRARY_Alloc then
    module Alloc
        private static integer instanceCount = 0
        private thistype recycle
   
        static method allocate takes nothing returns thistype
            local thistype this
   
            if (thistype(0).recycle == 0) then
                set instanceCount = instanceCount + 1
                set this = instanceCount
            else
                set this = thistype(0).recycle
                set thistype(0).recycle = thistype(0).recycle.recycle
            endif

            debug set this.recycle = -1
   
            return this
        endmethod
   
        method deallocate takes nothing returns nothing
            set this.recycle = thistype(0).recycle
            set thistype(0).recycle = this
        endmethod
    endmodule
endif

    struct ConditionNode extends array
        triggercondition condition
        boolexpr bexpr
        thistype next
        thistype prev

        implement Alloc

        static method create takes triggercondition tc, boolexpr b returns thistype
            local thistype this = thistype.allocate()
            set this.condition = tc
            set this.bexpr = b
            return this
        endmethod

        method destroy takes nothing returns nothing
            set this.condition = null
            set this.bexpr = null
            call this.deallocate()
        endmethod
    endstruct

    struct ConditionList extends array
        implement Alloc
        readonly ConditionNode first
        readonly ConditionNode last
        private integer count

        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.count = 0
            set this.first = 0
            set this.last = 0
            return this
        endmethod

        method clear takes nothing returns nothing
            local ConditionNode node = this.first
            local ConditionNode temp
            loop
                exitwhen 0 == node
                set temp = node.next
                call node.destroy()
                set node = temp
            endloop
            set this.first = 0
            set this.last = 0
            set this.count = 0
        endmethod

        method destroy takes nothing returns nothing
            call this.clear()
            call this.deallocate()
        endmethod

        method empty takes nothing returns boolean
            return 0 == this.count
        endmethod

        method size takes nothing returns integer
            return this.count
        endmethod

        method pushBack takes triggercondition tc, boolexpr b returns nothing
            local ConditionNode node = ConditionNode.create(tc, b)
            if ( this.count > 0 ) then
                set this.last.next = node
                set node.prev = this.last
                set this.last = node
            else
                set this.first = node
                set this.last = node
                set this.first.prev = 0
            endif
            set node.next = 0
            set this.count = this.count + 1
        endmethod

        method pushFront takes triggercondition tc, boolexpr b returns nothing
            local ConditionNode node = ConditionNode.create(tc, b)
            if ( this.count > 0 ) then
                set this.first.prev = node
                set node.next = this.first
                set this.first = node
            else
                set this.first = node
                set this.last = node
                set this.first.next = 0
            endif
            set node.prev = 0
            set this.count = this.count + 1
        endmethod

        method popBack takes nothing returns nothing
            local ConditionNode node 
            if ( this.count > 0 ) then
                set node = this.last
                set this.last = this.last.prev
                if ( this.last == 0 ) then
                    set this.first = 0
                else
                    set this.last.next = 0
                endif
                call node.destroy()
                set this.count = this.count - 1
            else
                // popBack on empty list
            endif
        endmethod

        method popFront takes nothing returns nothing
            local ConditionNode node 
            if ( this.count > 0 ) then
                set node = this.first
                set this.first = this.first.next
                if ( this.first == 0 ) then
                    set this.last = 0
                else
                    set this.first.prev = 0
                endif
                call node.destroy()
                set this.count = this.count - 1
            else
                // popFront on empty list
            endif
        endmethod

        method delete takes ConditionNode node returns nothing
            if ( node != 0 ) then
                if ( node == this.first ) then
                    call popFront()
                elseif ( node == this.last ) then
                    call popBack()
                else
                    set node.prev.next = node.next
                    set node.next.prev = node.prev
                    call node.destroy()
                    set this.count = this.count - 1
                endif
            else
                // deleting invalid node
            endif
        endmethod

        method findNode takes boolexpr b returns ConditionNode
            local ConditionNode node = this.first
            loop
                exitwhen node == 0 or node.bexpr == b
                set node = node.next
            endloop
            return node
        endmethod
    endstruct
        
    struct Trigger extends array
        private ConditionList list
        private trigger trig
        implement Alloc

        method operator trigger takes nothing returns trigger
            return this.trig
        endmethod

        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.list = ConditionList.create()
            set this.trig = CreateTrigger()
            return this
        endmethod

        method destroy takes nothing returns nothing
            call this.list.destroy()
            call DestroyTrigger(this.trig)
            set this.trig = null
            call this.deallocate()
        endmethod

        method restore takes nothing returns nothing
            local ConditionNode node = this.list.first
            call DestroyTrigger(this.trig)
            set this.trig = CreateTrigger()
            loop
                exitwhen node == 0
                set node.condition = TriggerAddCondition(this.trig, node.bexpr)
                set node = node.next
            endloop
        endmethod

        method addCondition takes code c returns nothing
            local triggercondition tc
            local boolexpr b
            if ( c != null and this.trig != null ) then
                set b = Condition(c)
                set tc = TriggerAddCondition(this.trig, b)
                call this.list.pushBack(tc, b)
                set b = null
                set tc = null
            endif
        endmethod

        method removeCondition takes code c returns nothing
            local ConditionNode node
            local boolexpr b
            if ( c != null and this.trig != null ) then
                set b = Condition(c)
                loop
                    set node = this.list.findNode(b)
                    exitwhen node == 0
                    call TriggerRemoveCondition(this.trig, node.condition)
                    call this.list.delete(node)
                endloop
                set b = null
            endif
        endmethod

        method clearConditions takes nothing returns nothing
            call this.list.clear()
            call TriggerClearConditions(trig)
        endmethod
    endstruct

endlibrary

Demo:
JASS:
struct RestoreTriggerTest extends array
    static Trigger trig

    private static method testme takes nothing returns boolean
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "handle id before restore: "+I2S(GetHandleId(trig.trigger)))
        call CreateUnit(Player(0),'hfoo',0,-100,0)
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        set trig = Trigger.create()
        call TriggerRegisterPlayerChatEvent(trig.trigger, Player(0), "s", true )
        call trig.addCondition(function thistype.testme)
        call trig.addCondition(function thistype.testme)
        call PolledWait(5.)
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "Conditions have been removed.")
        call trig.removeCondition(function thistype.testme)
        call PolledWait(5.)
        call trig.addCondition(function thistype.testme)
        call trig.restore()
        call TriggerRegisterPlayerChatEvent(trig.trigger, Player(0), "s", true )
    endmethod
endstruct

endlibrary
 
This could definitely be useful, here are some comments. Try not to get mad this time.

  • static if not LIBRARY_Alloc then -> this is a bad idea. Just useimplement optional Alloc
  • These list implementations are only providing code bloat, in my opinion.
  • I prefer RefreshTrigger over RestoreTrigger for the library name. The previous version had this name anyway.
  • Possibly make the "trig" member readonly?
  • Shouldn't ConditionList and ConditionNode be private?
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Thanks for replies! There is much too do.

But, before I replay to this..
THANKSSSSSSSSSSSSSSSSSSSSSSS TRIGGERHAPPY for this: implement optional :"D I've forgotten about this!
You have just improved List<T> and other librarires of my collection to the next lvl. Comparator will be instead a module which you can/might not pass as an argument within DECLARE_XXX macro thus providing superior solution to what I've currently been using :"DDDDD

Will come soon with info in regard to this lib, need to quickly fix few things ; PP
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated. Renamed to RefreshTrigger, the former name of this snippet.

Implementing Alloc module (within code block) was a bad idea of mine, even though it was meant to be just a placeholder, (op wasnt changed) it was bad.
As of now, Alloc is in use. Currently I've added support only for List library of mine. However, support for others might be included in the future. Uses updated List<T> (list thread hasnt been updated yet, update includes ADVANCE parameter, thus implementation becomes small if desired).

Updated documentation. Added empty and count methods.
Removed method operator trigger; instead member trig was renamed to trigger and has been given readonly directive as suggested.

Implemented list is a list of ConditionItem instances. Struct ConditionItem is private as it should be.


Edit: for this to be complete; kind of event cache should be added. However, there are so many different events, plus the custom events..
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated. Events are no longer removed.

As of now, as suggested, each instance stores two triggers: one for events, one for conditions. I might even change add/remove/clear (Conditions) to: add/remove/clear (Code). It seems to be more intuituve, plus you pass code parameter instead of boolexpr yet trigger recieves "actions" via TriggerAddConditions and is evaluated instead of executed.

This way we gain another feature - this trigger wont change, thus its handle id wont change -> what does that mean? Everything you could be saving via its handle id is no longer invalid or dereferenced.

Edit: @Ruke, after some considerations, if this trigger isn't destroyed; why does refresh method exists? ^.^ It was meant to remove events via destroying this trigger and then reappling conditions to make sure everything is fine. You had to add events you want to be added manualy though. I'm getting feeling that uploading this last update isnt good idea after all :p
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Listed API does not match with the actual code:
method removeCondition takes code c returns nothing

I'm supprised that this works. I'm asking because this is how your findNode method is working. Gonna test myself.
JASS:
local code c      = function Foo
local boolexpr a = Condition(c)
local boolexpr b = Condition(c)
if a == b then
// This can return true?
endif

There is not too much debug only code, which helps to eradicate wrong usage.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
yes, Condition and Filter will return the same handle for the same function throughout the map script, so in your case, a == b will always evaulate to true, even if you do one in one function, the other in another function, in different times even, and then check, its always going to be true. This is how my shitty DDS used to store function callbacks
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Can you please explain why this would be useful? Typically, trigger refreshing is all about removing events from null units. This resource implies lots of conditions were attached to the trigger, but doesn't automate the event reconstruction. That is making this resource look rather bloated and inconvenient to use.

Please give some examples as to how this would be the right choice for a given situation. Your description completely lacks the "why".
 
Top