Snippet Advent

Bribe

vJass errors are legion
Reaction score
67
Similar to Event by Nestharus, but without the messy API and the unreadable code.

It obviously needs to be a new submission as I don't like that Jesus4Lyf's Event requires you to use ugly trigger API. This hides the ugliness while keeping it just as dynamic. But faster.

JASS:
library Advent requires LinkedListModule /*

    ==========================================================================
    Advent v3.1.0.0
    --------------------------------------------------------------------------
    Easy, light, dynamic & complete event registration

    ==========================================================================
    Credits:
    --------------------------------------------------------------------------
    Created by Bribe
    Idea by Jesus4Lyf and Nestharus
    Many API additions thanks to Dirac
    
    ==========================================================================
    Requirements:
    --------------------------------------------------------------------------
    LinkedListModule by Dirac: hiveworkshop.com/forums/showthread.php?t=206552
    
    ==========================================================================
    Intro:
    --------------------------------------------------------------------------
    Advent allows you to create an Advent, add code to its execution queue,
    remove certain code from its queue, fire all added codes, fire just one
    single code and even destroy the Advent should its task be finished.

    I created this library for some key reasons. For one, the existing Event
    libraries have quite verbose API and their cross compatibility has quite
    large problems. Nestharus wanted a fast solution and Jesus4Lyf wanted a
    fully-dynamic solution.

    Why not have both?

    Advent combines tight code with complex functionality while not adding any
    unnecessary performance overhead. If you know your code will run at any
    point in the game, you can add permanent code that runs more quickly. If
    you need dynamic code, you can take advantage of the AdventReg type which
    allows you to do so.

    You can also destroy Advents which completely destroys any & all code reg-
    istered to them. I imagine this would only be used for some really complex
    systems; it's mostly here for completeness.

    ==========================================================================
    API Guide:
    --------------------------------------------------------------------------
    function CreateAdvent
        takes nothing
            returns Advent

        Create a new Advent instance. You must create it before anything else.

        * Advent adv = Advent.create()

    function DestroyAdvent
        takes Advent adv
            returns nothing

        Perhaps this is here just for completeness but it effectively allows
        the system to be completely dynamic.

        * call adv.destroy()

    function RegisterAdvent
        takes Advent adv, code c, boolean permanent
            returns AdventReg

        Register some code to be run when the Advent is fired. If you want to
        be able to unregister the code at some point, set the permanent* flag
        to false. If false, the function returns an AdventReg that is used to
        identify it.

        * AdventReg adreg = adv.registerEx(code) //Dynamic
        * call adv.register(code)                //Permanent

    function UnregisterAdventReg
        takes AdventReg node
            returns nothing

        Unregister an AdventReg from an Advent.

        * call adreg.unregister()

    function FireAdvent
        takes Advent adv
            returns nothing

        Run all code that was registered to the Advent.

        * call adv.fire()

    function FireAdventData
        takes Advent adv, integer data
            returns nothing

        Run all code that was registered to the Advent with an extra parameter
        to pass a data integer. This is useful for certain systems to support
        a single integer as a piece of data.

        * call adv.fireData(data)

    function GetAdventData
        takes nothing
            returns integer

        Returns the data specified by FireAdventData.

        * set data = Advent.data

    function GetAdventReg
        takes nothing
            returns AdventReg

        Returns the registry of the AdventReg that is currently firing. It is
        pointless to use this if you aren't using dynamic Advent registries.

        * set data = Advent.reg.data
            - Yeah AdventReg has a data member now, thanks to Dirac.

    function FireCode
        takes code c, integer data
            returns nothing

        Call the code directly. This is useful if you are late registering an
        Advent and you want this code to fire anyway, without firing the full
        Advent. This function also helps to keep Advent.data readonly because
        that is a very sensitive variable.

        * Advent.fireCode(code, data)
*/
    globals
        private force f
        private trigger array trig
    endglobals

    private module Init
        private static method onInit takes nothing returns nothing
            set f = bj_FORCE_PLAYER[0]
        endmethod
    endmodule
    
    private struct List extends array
        implement LinkedList
        implement Init
    endstruct
    
    struct Advent extends array
        
        //Data attached to the currently-firing Advent.
        readonly static integer data = 0

        //The AdventReg which is currently firing.
        readonly static AdventReg reg = 0
        
        static method create takes nothing returns thistype
            return List.createNode()
        endmethod
        
        method destroy takes nothing returns nothing
            local List node = List(this).next
            if not List(this).head then
                loop
                    call DestroyTrigger(trig[node])
                    set trig[node] = null
                    exitwhen node == this
                    set node = node.next
                endloop
                call List(this).flushNode()
            debug else
                debug call BJDebugMsg("[Advent] Attempt to destroy invalid instance: " + I2S(this))
            endif
        endmethod

        method register takes code c returns nothing
            if trig[this] == null then
                set trig[this] = CreateTrigger()
            endif
            call TriggerAddCondition(trig[this], Filter(c))
        endmethod

        method registerEx takes code c returns AdventReg
            local AdventReg node = List.allocate()
            call List(this).insertNode(node)
            set trig[node] = CreateTrigger()
            call TriggerAddCondition(trig[node], Filter(c))
            return node
        endmethod
        
        method fire takes nothing returns nothing
            set .reg = List(this).next
            loop
                call TriggerEvaluate(trig[.reg])
                exitwhen .reg == this
                set .reg = List(.reg).next
            endloop
        endmethod
        
        method fireData takes integer whichData returns nothing
            local integer pdat = .data
            set .data = whichData
            call this.fire()
            set .data = pdat
        endmethod
        
        static method fireCode takes code c, integer whichData returns nothing
            local integer pdat = .data
            set .data = whichData
            call ForForce(f, c)
            set .data = pdat
        endmethod

    endstruct

    struct AdventReg extends array

        //For attaching data to registries, for example a struct instance.
        integer data

        method unregister takes nothing returns nothing
            if trig[this] != null and not List(this).head then
                call DestroyTrigger(trig[this])
                set trig[this] = null
                call List(this).removeNode()
                call List(this).deallocate()
            debug else
                debug call BJDebugMsg("[Advent] Attempt to unregister invalid AdventReg: " + I2S(this))
            endif
        endmethod

    endstruct

    function CreateAdvent takes nothing returns Advent
        return Advent.create()
    endfunction

    function DestroyAdvent takes Advent adv returns nothing
        call adv.destroy()
    endfunction

    function FireAdvent takes Advent adv returns nothing
        call adv.fire()
    endfunction

    function FireAdventData takes Advent adv, integer data returns nothing
        call adv.fireData(data)
    endfunction

    function FireCode takes code c, integer data returns nothing
        call Advent.fireCode(c, data)
    endfunction

    function RegisterAdvent takes Advent adv, code c, boolean permanent returns AdventReg
        if permanent then
            call adv.register(c)
            return 0
        endif
        return adv.registerEx(c)
    endfunction

    function UnregisterAdvent takes AdventReg reg returns nothing
        call reg.unregister()
    endfunction

    function GetAdventData takes nothing returns integer
        return Advent.data
    endfunction

    function GetAdventReg takes nothing returns AdventReg
        return Advent.reg
    endfunction

endlibrary
 

luorax

Invasion in Duskwood
Reaction score
67
Shouldn't the documentation list all the available methods as well? I like the struct API much more and I always have to scroll down the script to see them.

Well, although I'm perfectly fine with J4L's Event (and there's no way I'd ever use any of Nestharus' so called scripts), but I might give this a try. It looks cool so far and that 5 minutes of work doesn't hurt if I can get some extra FPS.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Uf, how I hate wrapping my head around lists...
Did you test it? I really can't tell nor test if the list thingies are correct.

But anyway, suppose it's working great, why did you name it Advent? Doesn't ring any bells for me at least. Does it come from Adventure =)?
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
After some diagramming the list thingies proved correct. So I guess it's "safe to take an adventure" with Advent =).
 

Bribe

vJass errors are legion
Reaction score
67
Should I really list the struct API? The functions cover everything, the "struct" I consider intuitive & more advanced as it is.

I wonder how I would list the struct API without it looking redundant in the documentation.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
Some people prefer struct syntax. In my documentation, I only documented the function wrappers. Then I just listed the struct interface normally, eg:
JASS:
/* 
*  Functions
*
*      function blah takes nothing returns nothing
*          - explanation
*      function hoorah takes nothing returns nothing
*          - explanation
*
*  struct MyStruct
*
*      static method blah takes nothing returns nothing
*      static method hoorah takes nothing returns nothing
*/


If they need an explanation they can just look at the obvious function wrapper equivalent, so no need for a repeated explanation.
 

Bribe

vJass errors are legion
Reaction score
67
I have integrated struct API with the documentation in a non-redundant way. Should be good now.
 

luorax

Invasion in Duskwood
Reaction score
67
One more thing:

In the most cases you don't need dynamic events. Wouldn't it be better to rename "registerStatic" to "register" and "register" to "registerDynamic"? It'd also make it easier to replace J4L's old event library (dunno what's about Nes' one, I don't use any of his so-called codes), because it uses "register" too (and it doubles the work if you have to edit that part too).
 

Laiev

Hey Listen!!
Reaction score
188
Nestharus Event use [ljass]register[/ljass] to register a condition to that event and [ljass]registerTrigger[/ljass] to register a trigger to that event.
 

Bribe

vJass errors are legion
Reaction score
67
It follows normal programming convention to have the keyword "static" instead of "dynamic".

Perhaps I should rename it to "register" for static and "registerEx" for dynamic. That is more user-friendly IMO.

Edit: swap made. Updated to 2.0.
 

luorax

Invasion in Duskwood
Reaction score
67
"registerDynamic" was just an idea, "registerEx" is fine for me.

I've updated my map to use this instead of Event and it seems to work fine.
 

Dirac

22710180
Reaction score
147
With the use of registerEx you can actually attach data to Advents once they're registered.
It looks like this system should be using LinkedListModule since it basically does everything it has.
If you take a look at my damage system i already coded this for the DamageDealtEvent and DamageTakenEvent structs.
 

Bribe

vJass errors are legion
Reaction score
67
LinkedListModule's methods won't inline, so there's no point in making it a requirement.

And you basically disproved your own argument. Since your DamageDealtEvent / DamageTakenEvent does this, but by not requiring Advent, why do you think I should require your little struct when you don't require Advent? It's a slippery slope.
 

Dirac

22710180
Reaction score
147
I like mine better, it has data attachment capacities.
If you use mine i'll use yours.
 

Dirac

22710180
Reaction score
147
I'll just show you how i need this snippet to be
JASS:
library Advent uses LinkedListModule
    
    globals
        private integer AdventData = 0
    endglobals
    
    struct Advent extends array
        implement LinkedList
        
                integer data
        private trigger trig
        
        static method create takes nothing returns thistype
            local thistype this=createNode()
            if trig==null then
                set trig=CreateTrigger()
            else
                call TriggerClearConditions(trig)
            endif
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            call this.flushNode()
        endmethod
        
        method register takes code c returns nothing
            call TriggerAddCondition(trig, Filter(c))
        endmethod
        
        method registerEx takes code c returns integer
            local thistype node = allocate()
            if trig==null then
                set trig=CreateTrigger()
            else
                call TriggerClearConditions(trig)
            endif
            call this.insertNode(node)
            call node.register(c)
            return node
        endmethod
        
        method unregister takes thistype node returns nothing
            call this.removeNode()
            call this.deallocate()
        endmethod
        
        method fire takes nothing returns nothing
            loop
                set AdventData=data
                call TriggerEvaluate(trig)
                set this=next
                exitwhen head
            endloop
        endmethod
        
    endstruct
    
    function CreateAdvent takes nothing returns Advent
        return Advent.create()
    endfunction
    
    function DestroyAdvent takes Advent a returns nothing
        call a.destroy()
    endfunction
    
    function RegisterAdvent takes Advent a, code c, boolean permanent returns integer
        if permanent then
            call a.register(c)
            return 0
        endif
        return a.registerEx(c)
    endfunction
    
    function UnregisterAdvent takes Advent a, integer node returns nothing
        call a.unregister(node)
    endfunction
    
    function FireAdvent takes Advent a returns nothing
        call a.fire()
    endfunction
    
    function SetAdventData takes Advent a, integer data returns nothing
        set a.data=data
    endfunction
    
    function GetAdventData takes nothing returns integer
        return AdventData
    endfunction
    
endlibrary

It's funny because i already coded this for my Damage library
Setting the advent's data on it's creation makes a lot more sense than setting it when fired, but with this version you can do both (for those who need it)
I also took the liberty to include LinkedListModule as i see no reason why it shouldn't be here.
If you take this advise i'll add Advent as a requirement to Damage
 

Bribe

vJass errors are legion
Reaction score
67
Trigger recycling is not good. The reason I didn't implement it is because of handle spikes - some parts of the game may allocate huge chunks of triggers and leave them rotting for the rest of the game.

I would add a seperate entry for data of specific registries (I think that's what you meant), but the Advent.data variable points to dynamic data being run through the event (for example the index of a unit) and I do not want that public because the user could set it arbitrarily and mess up the event, which could be done with recursive event firing as well if the user is able to set it on his/her own.

Also, LinkedList is too much overhead, as can be clearly demonstrated here you are only using create, destroy, insert and remove. LinkedList ports a bunch of methods that are useless to Advent.
 

Dirac

22710180
Reaction score
147
LinkedList ports a bunch of methods that are useless to Advent.
Name one, because i think you went through all the methods already.

Ok trigger recycling is bad, but the data is meant to be managed by the user, if you use my damage library you'll see why. you can do stuff like this
JASS:
DamageTakenEvent damage
unit target

static method onDamage takes nothing returns nothing
    call UnitDamageTarget(Damage.target,thistype(Damage.data).target,Damage.amount,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null)
endmethod

static method onCast takes nothing returns nothing
    local thistype this=allocate()
    set this.damage=DamageTakenEvent.register(GetUnitUserData(GetTriggerUnit()),function thistype.onDamage)
    set this.damage.data=this
    set this.target=GetSpellTargetUnit()
endmethod
 

Bribe

vJass errors are legion
Reaction score
67
Added a way to retrieve the index of dynamic code - GetAdventReg. This is akin to Jesus4Lyf's GetTriggeringEventReg.

Thanks goes to Dirac for convincing me it is a good thing to have.
 

Dirac

22710180
Reaction score
147
JASS:
method register takes code c returns nothing
    set .count[this]=.count[this]+1
    call TriggerAddCondition(.trigs[this], Filter(c))
endmethod

method registerEx takes code c returns thistype
    local thistype node
    if .count[this]==0 then
        set node=this
    else
        set node=.allocate()
    endif
    set .prev[node] = .prev[this]
    set .next[.prev[this]] = node
    set .prev[this] = node
    set .next[node] = this
    call node.register(c)
    return node
endmethod

This would be to prevent having a null trigger on the list

EDIT: I just noticed the unregister method makes no sense
JASS:
call this.unregister(this)

Srly, and why does all methods return integer? They should return Advent
JASS:
method unregister takes nothing returns nothing
    debug if .trigs[this] != null then
        set .next[.prev[this]] = .next[this]
        set .prev[.next[this]] = .prev[this]
        set .count[this]=.count[this]-1
        call this.deallocate()
    debug else
        debug call BJDebugMsg("[Advent] Attempt to unregister invalid node of Advent: " + I2S(this))
    debug endif
endmethod
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top