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

[System] CodeStack

JASS:
/**
 *  CodeStack
 *      by Anachron
 *
 *  requires
 *      - Table by Bribe
 *      - HandleObject by Anachron
 *
 *  This library is a solution to generate dynamic code constructs that
 *  can be modified whenever required. They provide a very easy and flexible
 *  interface that can be used to get almost any functionality that you could need.
 *  
 *  This system also is able to clear the whole stack and remove all actions from it.
 *  
 *  CodeStack.create() --> Creates a stack of actions with a new trigger
 *  CodeStack.createEx(triggerVariable) --> Create a stack of actions with your custom trigger
 *  {var}.addCode(function xyz) --> Add something to your codestack
 *  {var}.remCode(function zyx) --> Remove something from your codestack
 *  {var}.remCodeByIndex(index) --> Remove code when you know which index it has
 *  {var}.execute() --> Run all actions defined in your codestack
 *  {var}.clear() --> Remove every existing action from you stack
 *
 */
library CodeStack requires Table, HandleObject

    struct Code
        implement HandleObject
        
        public conditionfunc CondFunc = null
    
        public static method create takes code theCode returns thistype
            local conditionfunc theCondFunc = Condition(theCode)
            local thistype this = thistype.new(theCondFunc)

            set .CondFunc = theCondFunc
            
            return this
        endmethod
    endstruct
    
    struct CodeTrigger
        implement HandleObject
        
        public trigger SelfHandle = null
    
        public static method create takes trigger theTrigger returns thistype
            local thistype this = thistype.new(theTrigger)
            
            set .SelfHandle = theTrigger
            
            return this
        endmethod
    endstruct

    module CodeStackModule
        private Table           CodeTable       = 0
        private Table           IdTable         = 0
        private integer         CodeAmount      = 0
        private CodeTrigger     ExecTrigger     = 0
        
        public static method create takes nothing returns thistype
            return thistype.createEx(CreateTrigger())
        endmethod
        
        public static method createEx takes trigger theTrigger returns thistype
            local thistype this = thistype.allocate()
            
            set .CodeTable = Table.create()
            set .IdTable = Table.create()
            set .ExecTrigger = CodeTrigger.create(theTrigger)
            
            return this
        endmethod
        
        public method addCode takes code theCode returns Code
            local Code theRealCode = Code.create(theCode)
            local integer triggerId = GetHandleId(.ExecTrigger.SelfHandle)
            local triggercondition condTrigCond = null
        
            if not theRealCode.INSTANCES.has(triggerId) then
                set condTrigCond = TriggerAddCondition(.ExecTrigger.SelfHandle, theRealCode.CondFunc)
                set theRealCode.INSTANCES.triggercondition[triggerId] = condTrigCond
        
                set .CodeTable[.CodeAmount] = theRealCode
                set .IdTable[integer(theRealCode)] = .CodeAmount
            endif
        
            return theRealCode
        endmethod
        
        public method remCode takes code theCode returns boolean
            local Code theRealCode = Code.create(theCode)
            
            if not .IdTable.has(integer(theRealCode)) then
                return false
            endif
            
            return .remCodeByIndex(.CodeTable[integer(theRealCode)])
        endmethod
        
        public method remCodeByIndex takes integer index returns boolean
            local Code theRealCode = .CodeTable[index]
            local integer triggerId = GetHandleId(.ExecTrigger.SelfHandle)
            local triggercondition codeTrigCond = null
                        
            set codeTrigCond = theRealCode.INSTANCES.triggercondition[triggerId]
            call TriggerRemoveCondition(.ExecTrigger.SelfHandle, codeTrigCond)
            call theRealCode.INSTANCES.remove(triggerId)
            
            call .CodeTable.remove(index)
            set .IdTable[.CodeTable[.CodeAmount -1]] = index
            set .CodeTable[index] = .CodeTable[.CodeAmount -1]
            set .CodeAmount = .CodeAmount -1
            return true
        endmethod
        
        public method execute takes nothing returns boolean
            return TriggerEvaluate(.ExecTrigger.SelfHandle)
        endmethod
        
        public method clear takes nothing returns nothing
            local integer codeId = 0
        
            loop
                exitwhen .CodeAmount <= 0
                call .remCodeByIndex(.CodeAmount -1)
            endloop
        endmethod
    endmodule
    
    struct CodeStack 
        implement CodeStackModule
    endstruct

endlibrary

Requirements:
New Table by Bribe

JASS:
library SaveableStruct requires Table
        
    module SaveableStruct
        public      static      Table       INSTANCES       = 0
        public      static      integer     NOT_EXISTING    = -1
        public                  integer     id              = -1
        public      static      integer     SELF            = 0

        public method save takes nothing returns nothing
            set thistype.INSTANCES[.id] = integer(this)
        endmethod
        
        public method clear takes nothing returns nothing
            call thistype.INSTANCES.flush()
        endmethod
        
        public method remove takes nothing returns boolean
            local boolean exists = thistype.exists(.id)
            call thistype.INSTANCES.remove(.id)
            return exists
        endmethod
        
        public static method exists takes integer id returns boolean
            return thistype.INSTANCES.has(id)
        endmethod
        
        public static method load takes integer id returns thistype
            if not thistype.exists(id) then
                return thistype.NOT_EXISTING
            endif
            return thistype(thistype.INSTANCES[id])
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.INSTANCES = Table.create()
        endmethod
    endmodule

endlibrary

JASS:
library HandleObject

    module HandleObject
        implement SaveableStruct
        
        public handle myHandle = null
        
        public static method new takes handle theHandle returns thistype
            local integer handleId = GetHandleId(theHandle)
            local thistype this = thistype.load(handleId)
            
            if this == thistype.NOT_EXISTING then
                set this = thistype.allocate()
                set this.myHandle = theHandle
                set this.id = handleId
                call .save()
            endif
            
            return this
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call .remove()
        endmethod
    endmodule

endlibrary

Example (+ Map)
JASS:
library Event initializer test requires CodeStack

    struct Event
        private CodeStack funcs = 0
        public string name = null
        
        public static method create takes string theName returns thistype
            local thistype this = thistype.allocate()
            
            set .name = theName
            set .funcs = CodeStack.create()
            
            return this
        endmethod
        
        public method register takes code theCode returns Code
            return .funcs.addCode(theCode)
        endmethod
        
        public method unregister takes code theCode returns boolean
            return .funcs.remCode(theCode)
        endmethod
        
        public method fire takes nothing returns boolean
            return .funcs.execute()
        endmethod
    endstruct
    
    globals
        unit testUnit = null
    endglobals
    
    private function createUnit takes nothing returns boolean
        call BJDebugMsg("Create Unit")
        set testUnit = CreateUnit(Player(0), 'hpea', 0., 0., 0.)
        return true
    endfunction
    
    private function killUnit takes nothing returns boolean
        call BJDebugMsg("Kill Unit")
        call KillUnit(testUnit)
        return true
    endfunction
    
    private function test takes nothing returns nothing
        local Event myEvent = Event.create("AwesomeEvent")
        
        call myEvent.register(function createUnit)
        call myEvent.register(function killUnit)
        call myEvent.fire()
        
        call myEvent.unregister(function killUnit)
        call myEvent.fire()
    endfunction
    
endlibrary

What do you guys think about it?
 

Attachments

  • CodeStack_v.0.1.0.w3m
    30 KB · Views: 55
I'd encourage you to make this thing as light as possible :/

I mean, the Code struct should be nothing more than this:

JASS:
struct Code
    
    boolexpr b

    static method create takes code func returns thistype
        local thistype this = allocate()
        set this.b = Filter(func)
        return this
    endmethod
endstruct

And a CodeStack, or CodeArray would be nothing more than a list/stack of Code structs :eek:
 
Magheridon96, did you even read my code?
Inside the thistype.new function I do all the save/creating on need.
I will reuse it in many parts of my code so I made it abstract.

Edit: New version will include a few fixes and a new example what to do with events:

JASS:
library Action initializer init requires Event, TimerUtils
    
    struct Action
        private Table EventTable = 0
        private Table IdTable = 0
        private integer EventAmount = 0
        
        public static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            
            set .EventTable = Table.create()
            set .IdTable = Table.create()
            
            return this
        endmethod
        
        public method addEvent takes Event theEvent, real triggerPoint returns Event
            set .EventTable[integer(theEvent)] = .EventAmount
            set .EventTable.real[.EventAmount] = triggerPoint
            set .IdTable[.EventAmount] = integer(theEvent)
            set .EventAmount = .EventAmount +1
            
            return theEvent
        endmethod
        
        public method remEvent takes Event theEvent returns boolean
            local integer index = .EventTable[integer(theEvent)]
        
            if not .EventTable.has(integer(theEvent)) then
                return false
            endif
            
            set .IdTable[index] = .EventTable[.EventAmount -1]
            set .EventTable[integer(.IdTable[index])] = index
            set .EventAmount = .EventAmount -1
            
            return true
        endmethod
        
        private static method fireEvent takes nothing returns nothing
            local timer expTimer = GetExpiredTimer()
            local Event theEvent = Event(GetTimerData(expTimer))
            
            call theEvent.fire()
            call ReleaseTimer(expTimer)
        endmethod
        
        public method fire takes nothing returns nothing
            local integer i = 0
            
            loop
                exitwhen i >= .EventAmount
                
                if .EventTable.timer[i] == null then
                    set .EventTable.timer[i] = NewTimerEx(.IdTable[i])
                endif
                call TimerStart(.EventTable.timer[i], .EventTable.real[i], false, function thistype.fireEvent)
                
                set i = i +1
            endloop
        endmethod
    endstruct
    
    private function createUnit takes nothing returns nothing
        call CreateUnit(Player(0), 'hpea', 0., 0., 0.)
    endfunction
    
    private function init takes nothing returns nothing
        local Action myAction = Action.create()
        
        call myAction.addEvent(Event.new(), 2.99).register(function createUnit)
        call myAction.addEvent(Event.new(), 5.24).register(function createUnit)
        call myAction.addEvent(Event.new(), 10.24).register(function createUnit)
        
        call myAction.fire()
    endfunction
    
endlibrary
 
Oh come on, don't be like that, I read your code thrice :l

There is one thing up for debate in terms of semantics:

JASS:
local CodeStack stack = CodeStack.create()
call stack.addCode(function dealTwentyDamage)
call stack.addCode(function dealTwentyDamage)

If dealTwentyDamage is a function that deals 20 damage to a unit X, would I expect 40 damage to be dealt to this unit upon executing the code stack? Or would I expect the system to ignore the addition of the already included function?

With regular triggers, adding a condition twice would make the code run twice as well. (At least, in my experience)

I don't know what the function should do, which is why I'm putting it up for debate :l
 
Top