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
 
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.
 
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 =)?
 
After some diagramming the list thingies proved correct. So I guess it's "safe to take an adventure" with Advent =).
 
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.
 
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.
 
I have integrated struct API with the documentation in a non-redundant way. Should be good now.
 
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).
 
Nestharus Event use [ljass]register[/ljass] to register a condition to that event and [ljass]registerTrigger[/ljass] to register a trigger to that event.
 
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.
 
"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.
 
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.
 
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.
 
I like mine better, it has data attachment capacities.
If you use mine i'll use yours.
 
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
 
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.
 
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
 
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.
 
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 The Helper:
    News portal has been retired. Main page of site goes to Headline News forum now
  • The Helper The Helper:
    I am working on getting access to the old news portal under a different URL for those that would rather use that for news before we get a different news view.
  • Ghan Ghan:
    Easily done
    +1
  • The Helper The Helper:
    https://www.thehelper.net/pages/news/ is a link to the old news portal - i will integrate it into the interface somewhere when i figure it out
  • Ghan Ghan:
    Need to try something
  • Ghan Ghan:
    Hopefully this won't cause problems.
  • Ghan Ghan:
    Hmm
  • Ghan Ghan:
    I have converted the Headline News forum to an Article type forum. It will now show the top 20 threads with more detail of each thread.
  • Ghan Ghan:
    See how we like that.
  • The Helper The Helper:
    I do not see a way to go past the 1st page of posts on the forum though
  • The Helper The Helper:
    It is OK though for the main page to open up on the forum in the view it was before. As long as the portal has its own URL so it can be viewed that way I do want to try it as a regular forum view for a while
  • Ghan Ghan:
    Yeah I'm not sure what the deal is with the pagination.
  • Ghan Ghan:
    It SHOULD be there so I think it might just be an artifact of having an older style.
  • Ghan Ghan:
    I switched it to a "Standard" article forum. This will show the thread list like normal, but the threads themselves will have the first post set up above the rest of the "comments"
  • The Helper The Helper:
    I don't really get that article forum but I think it is because I have never really seen it used on a multi post thread
  • Ghan Ghan:
    RpNation makes more use of it right now as an example: https://www.rpnation.com/news/
  • The Helper The Helper:
  • The Helper The Helper:
    What do you think Tom?
  • tom_mai78101 tom_mai78101:
    I will have to get used to this.
  • tom_mai78101 tom_mai78101:
    The latest news feed looks good
  • The Helper The Helper:
    I would like to see it again like Ghan had it the first time with pagination though - without the pagination that view will not work but with pagination it just might...
  • The Helper The Helper:
    This drink recipe I have had more than a few times back in the day! Mind Eraser https://www.thehelper.net/threads/cocktail-mind-eraser.194720/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top