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

[vJASS] Help immproving ApI/effeciency of a Herd System

Status
Not open for further replies.
Level 7
Joined
Jan 28, 2012
Messages
266
As stated in the title, I would appreciate help for improving the API of this system.

JASS:
library Behaviors
    globals
        public behavior EVAL_BEHAVIOR = 0
        public boolean END_IT = false
        public integer RETURN = 0
    endglobals
    struct BEHAVIOR extends array
        private thistype recycle
        private static integer total = 0
        private static thistype HEAD = 0
        
        readonly thistype next
        readonly thistype prev
        
        boolexpr eval
        real data1
        real data2
        real data3
        real data4
        real data5
        real data6
        real data7
        real data8
        integer sinstance
        integer myherd
        
        static method allocate takes nothing returns thistype
            local thistype this = HEAD.recycle
            if this == 0 then
                set total = total+1
                set this = total
            else
                set HEAD.recycle = this.recycle
            endif
            return this
        endmethod
        method deallocate takes nothing returns nothing
            set .recycle = HEAD.recycle
            set HEAD.recycle = this
        endmethod
        static method createHead takes boolexpr b,herdtype h returns thistype
            local thistype this = allocate()
            set .eval = b
            set .next = this
            set .prev = this
            return this
        endmethod
        method addNode takes boolexpr b returns nothing
            local thistype head = this
            set this = allocate()
            set .eval = b
            set .next = head
            set .prev = head.prev
            set .prev.next = this
            set head.prev = this
        endmethod
    endstruct
    struct behavior extends array
        private thistype recycle
        private static integer total = 0
        private static thistype HEAD = 0
        //private static force EVAL =  bj_FORCE_PLAYER[0]
        static force EVAL
        
        readonly thistype next
        readonly thistype prev
        
        boolexpr eval
        real data1
        real data2
        real data3
        real data4
        real data5
        real data6
        real data7
        real data8
        integer sinstance
        integer myherd
        boolean running
        boolean initialized
        static method allocate takes nothing returns thistype
            local thistype this = HEAD.recycle
            if this == 0 then
                set total = total+1
                set this = total
            else
                set HEAD.recycle = this.recycle
            endif
            return this
        endmethod
        method deallocate takes nothing returns nothing
            set .recycle = HEAD.recycle
            set .next.prev = .prev
            set .prev.next = .next
            set initialized = false
            set HEAD.recycle = this
        endmethod
        static method createHead takes BEHAVIOR b,herd h returns thistype
            local thistype this = allocate()
            set .eval = b.eval
            set .next = this
            set .prev = this
            set data1 = b.data1
            set data2 = b.data2
            set data3 = b.data3
            set data4 = b.data4
            set data5 = b.data5
            set data6 = b.data6
            set data7 = b.data7
            set data8 = b.data8
            set running = false
            set myherd = h
            set sinstance = 0
            return this
        endmethod
        method addNode takes BEHAVIOR b,herd h returns nothing
            local thistype head = this
            set this = allocate()
            set .eval = b.eval
            set .next = head
            set .prev = head.prev
            set .prev.next = this
            set head.prev = this
            set data1 = b.data1
            set data2 = b.data2
            set data3 = b.data3
            set data4 = b.data4
            set data5 = b.data5
            set data6 = b.data6
            set data7 = b.data7
            set data8 = b.data8
            set running = false
            set myherd = h
            set sinstance = 0
        endmethod
        method operator[]= takes integer i, real data returns nothing
            if i > 4 then
                if i > 6 then
                    if i == 7 then
                        set data7 = data
                    else
                        set data8 = data
                    endif
                else
                    if i == 5 then
                        set data5 = data
                    else
                        set data6 = data
                    endif
                endif
            else
                if i > 2 then
                    if i == 3 then
                        set data3 = data
                    else
                        set data4 = data
                    endif
                else
                    if i == 2 then
                        set data2 = data
                    else
                        set data1 = data
                    endif
                endif
            endif
        endmethod
        method operator[] takes integer i returns real
            if i > 4 then
                if i > 6 then
                    if i == 7 then
                        return data7
                    else
                        return data8
                    endif
                else
                    if i == 5 then
                        return data5
                    else
                        return data6
                    endif
                endif
            else
                if i > 2 then
                    if i == 3 then
                        return data3
                    else
                        return data4
                    endif
                else
                    if i == 2 then
                        return data2
                    else
                        return data1
                    endif
                endif
            endif
           call BJDebugMsg("INDEX OUT OF RANGE")
            return -200000000.0000
        endmethod
        method run takes boolean end returns integer
            set END_IT = end
            set EVAL_BEHAVIOR = this
            //call DisplayTextToPlayer(Player(0),0,0,"RunningBehavior "+I2S(this))
            call ForceEnumPlayersCounted(EVAL, eval, 1)
            //call ForceEnumPlayersCounted(EVAL, Filter(function roam.main), 1)
            return RETURN
        endmethod
        method stop takes nothing returns nothing
            set running = false
            set data1 = 0
            set data2 = 0
            set data3 = 0
            set data4 = 0
            set data5 = 0
            set data6 = 0
            set data7 = 0
            set data8 = 0
            set sinstance = 0
        endmethod
    endstruct
    //struct behaviorN extends behavior
        //private integer some
    //endstruct
    module BehaviorTopOfCode
          local herd h =EVAL_BEHAVIOR.myherd
          local member this = h.leader
          local member leader = this
          local unit u = this.data 
          local boolean allowexit = true
    endmodule
    module ForLeader
        set u = this.data
    endmodule
    module EndForLeader
        set this = h.cycleNext(this)
    endmodule
    module BehaviorForMemberInHerd
        set h.tx = 0
        set h.ty = 0
        set u = this.data
        loop
            if this.refresh() then
    endmodule
    module BehaviorForDead
             else
                  set allowexit = not (this == h.leader and this.next != this)
                  call h.removeMember(this)
    endmodule
    module BehaviorEndForMember
            endif
            set this = h.cycleNext(this)
            exitwhen this == leader and allowexit
            set allowexit = true
            set u = this.data
        endloop
        set u = null
        call h.calculateXYPos()
    endmodule
    function GetEvalBehavior takes nothing returns behavior
        return EVAL_BEHAVIOR
    endfunction
    function GetShouldEnd takes nothing returns boolean
        return END_IT
    endfunction
    function ReturnWhat takes integer returnwhat returns nothing
         set RETURN = returnwhat
    endfunction
endlibrarya
JASS:
library HERD
    globals
        real HERD_EVENT = 0.00
        constant real EVENT_HERD_DEATH = 1.00
        herd TRIGGER_HERD
        //constant real EVENT_HERD_UNIT_DIES = 2.00
        //constant real EVENT_HERD_ATTACKED = 4.00
        constant real EVENT_HERD_FAILED_BEHAVIOR = 3.00
        behavior TRIGGER_BEHAVIOR
    endglobals
    native UnitAlive takes unit u returns boolean
    struct herdtype extends array
        private thistype recycle
        private static integer total = 0
        private static thistype HEAD = 0
        
        BEHAVIOR head
        integer bcount
        
        integer unitId
        integer redMin
        integer redMax
        integer blueMin
        integer blueMax
        integer greenMin
        integer greenMax
        integer alphaMin
        integer alphaMax
        real sizeMin
        real sizeMax
        
        boolean destroyWhenNoHerds
        integer currentInstances
        integer maxMembers
        method AddBehavior takes boolexpr b returns integer
            call head.addNode(b)
            set bcount = bcount+1
            return bcount
        endmethod
        
        static method allocate takes nothing returns thistype
            local thistype this = HEAD.recycle
            if this == 0 then
                set total = total+1
                set this = total
            else
                set HEAD.recycle = this.recycle
            endif
            return this
        endmethod
        method deallocate takes nothing returns nothing
            //set .next.prev = .prev
            //sset .prev.next = .next
            set .recycle = HEAD.recycle
            set HEAD.recycle = this
        endmethod
        
        static method create takes boolexpr initial, integer unitId,boolean destroyed returns thistype
            local thistype this = allocate()
            set head = BEHAVIOR.createHead(initial,this)
            set .unitId = unitId
            set destroyWhenNoHerds = destroyed
            set redMin = 255
            set redMax = 255
            set blueMin = 255
            set blueMax = 255
            set greenMin = 255
            set greenMax = 255
            set alphaMin = 255
            set alphaMax = 255
            set sizeMin = 1
            set sizeMax= 1
            return this
        endmethod
        method destroy takes nothing returns nothing
            loop
                exitwhen .head.next == .head
                call .head.deallocate()
                set .head = .head.next
            endloop
            call .head.deallocate()
            call deallocate()
        endmethod
        method removeHerd takes nothing returns boolean
            set .currentInstances = .currentInstances-1
            if destroyWhenNoHerds and .currentInstances<=0 then
                call destroy()
                return false
            endif
            return true
        endmethod
        method addHerd takes nothing returns integer
            set currentInstances = currentInstances+1
            return currentInstances
        endmethod
    endstruct
    struct member extends array
        private thistype recycle
        private static integer total = 0
        private static thistype HEAD = 0
        private static integer TICKSTOAGE = R2I(120/.0325)
        
        readonly thistype next
        readonly thistype prev
        readonly unit data
        integer r
        integer b
        integer g
        integer alph
        real    size
        integer tick
        integer age
        
        real x
        real y
        static method allocate takes nothing returns thistype
            local thistype this = HEAD.recycle
            if this == 0 then
                set total = total+1
                set this = total
            else
                set HEAD.recycle = this.recycle
            endif
            return this
        endmethod
        static method createHead takes unit u returns thistype
            local thistype this = allocate()
            set .next = this
            set .prev = this
            set .data = u
            return this
        endmethod
        method addNode takes unit u returns thistype
            local thistype head = this
            set this = thistype.allocate()
            set .data = u
            set .next = head
            set .prev = head.prev
            set .prev.next = this
            set head.prev = this
            return this
        endmethod
        method deallocate takes nothing returns nothing
            set .next.prev = .prev
            set .prev.next = .next
            set .recycle = HEAD.recycle
            set HEAD.recycle = this
        endmethod
        method destroy takes nothing returns nothing
            call .deallocate()
            set b = 0
            set r = 0
            set g = 0
            set alph = 0
            set size = 0
            set data = null
        endmethod
        method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
            set .b = blue
            set .r = red
            set .g = green
            set .alph = alpha
            call SetUnitVertexColor(.data,red,green,blue,alpha)
        endmethod
        method setScale takes real scale returns nothing
            set .size = scale
            call SetUnitScale(.data,scale,scale,scale)
        endmethod
        method refresh takes nothing returns boolean
            if not UnitAlive(data) then
                return false
            endif
            set tick = tick+1
            if tick ==TICKSTOAGE then
               set age = age+1
               set tick = 0
            endif
            return true
        endmethod
    endstruct
    struct herd extends array
        private thistype next
        private thistype prev
        private thistype recycle
        private static integer total = 0
        private static thistype HEAD = 0
        private static timer TIMER = CreateTimer()
        private static real PERODIC = 2.0
        private static integer MAX_WAITS = R2I(1200/PERODIC)
        readonly herdtype htype
        private behavior head
        behavior current
        integer waits
        readonly member leader
        readonly integer flockSize
        real cx
        real cy
        real x
        real y
        real tx
        real ty
        private static method allocate takes nothing returns thistype
            local thistype this = HEAD.recycle
            if this == 0 then
                set total = total+1
                set this = total
            else
                set HEAD.recycle = .recycle
            endif
            set .next = HEAD.next
            set .prev = HEAD
            set .next.prev = this
            set HEAD.next = this
            return this
        endmethod
        private method deallocate takes nothing returns nothing
            set .next.prev = .prev
            set .prev.next = .next
            set .recycle = HEAD.recycle
            set HEAD.recycle = this
        endmethod
        method destroy takes nothing returns nothing
             call deallocate()
             loop
                 exitwhen leader.next == leader
                 call leader.destroy()
                 set leader = leader.next
             endloop
             call leader.destroy()
             call htype.removeHerd()
        endmethod
        method addMember takes unit whichu returns member
            local member u = leader.addNode(whichu)
            call u.setColor(GetRandomInt(htype.redMin,htype.redMax),GetRandomInt(htype.greenMin,htype.greenMax),GetRandomInt(htype.blueMin,htype.blueMax),GetRandomInt(htype.alphaMin,htype.alphaMax))
            call u.setScale(GetRandomReal(htype.sizeMin,htype.sizeMax))
            set flockSize = flockSize+1
            return u
        endmethod
        method removeMember takes member whichone returns nothing
            set flockSize = flockSize-1
            if whichone == .leader then
                set .leader = whichone.next
            endif
            call whichone.destroy()
        endmethod
        method GetType takes nothing returns herdtype
            return .htype
        endmethod
        method GetHerdX takes nothing returns real
            return x
        endmethod
        method GetHerdY takes nothing returns real
             return y
        endmethod
        method calculateXYPos takes nothing returns nothing
             set x = tx/I2R(flockSize)
             set y = ty/I2R(flockSize)
        endmethod
        method cycleNext takes member u returns member
             set u = u.next
             set u.x = GetUnitX(u.data)
             set u.y = GetUnitY(u.data)
             set tx = tx+u.x
             set ty = ty+u.y
             return u
        endmethod
        static method perodic takes nothing returns nothing
            local thistype this = HEAD.next
            local integer return_code
            local boolean moveon
            //call DisplayTextToPlayer(Player(0),0,0,"PERODIC RUNNING")
            if this != 0 then
                
            loop
                set moveon = waits>=MAX_WAITS
                set return_code = current.run(moveon)
                if return_code == 3 then
                    set current = current.next
                    set waits = 0
                else
                    if return_code == 5 then
                        set TRIGGER_HERD = this
                        set TRIGGER_BEHAVIOR = current
                        set HERD_EVENT =EVENT_HERD_FAILED_BEHAVIOR
                        set HERD_EVENT = 0
                    endif
                    set waits = waits + return_code
                endif
                if flockSize < 1 then
                    set TRIGGER_HERD = this
                    set HERD_EVENT = EVENT_HERD_DEATH
                    set HERD_EVENT = 0
                    call this.destroy()
                endif
                set this = this.next
                exitwhen HEAD == this
            endloop
                call TimerStart(TIMER,PERODIC,false,function thistype.perodic)
            endif
        endmethod

        static method create takes herdtype herdId, player pid,real x, real y,integer tostart returns thistype
            local thistype this 
            local integer i = 0
            local integer unitId  = herdId.unitId
            local behavior cur
            local BEHAVIOR bc
            local BEHAVIOR bh
            //call DisplayTextToPlayer(Player(0),0,0,"allocating")
            set this = allocate()
            set htype = herdId
            set .cx =x
            set .cy = y
            //call DisplayTextToPlayer(Player(0),0,0,"CreatingLeader")
            set leader = member.createHead(CreateUnit(pid,unitId,x,y,GetRandomReal(0,360)))
            call leader.setColor(GetRandomInt(htype.redMin,htype.redMax),GetRandomInt(htype.greenMin,htype.greenMax),GetRandomInt(htype.blueMin,htype.blueMax),GetRandomInt(htype.alphaMin,htype.alphaMax))
            call leader.setScale(htype.sizeMax)
            //call DisplayTextToPlayer(Player(0),0,0,"CreatingUnits")
            loop
                exitwhen i >= tostart
                set i = i+1
                call addMember(CreateUnit(pid,unitId,GetRandomReal(0,120)+x,GetRandomReal(0,120)+y,GetRandomReal(0,360)))
            endloop
            set bh = herdId.head
            //call DisplayTextToPlayer(Player(0),0,0,"BEHAVIOR ="+I2S(bh))
            set bc = bh.next
            set .head = behavior.createHead(bh,this)
            set .current = .head
            //call DisplayTextToPlayer(Player(0),0,0,".head ="+I2S(.head))
            set cur=head
            loop
                exitwhen bc == bh
                call DisplayTextToPlayer(Player(0),0,0,"ADDING BEHAVIORS")
                call head.addNode(bc,this)
                set bc = bc.next
            endloop
            if HEAD.prev == this then
                call TimerStart(TIMER,PERODIC,false,function thistype.perodic)
            endif
            return this
        endmethod
    endstruct
    function TriggerRegisterHerdEvent takes trigger t, real herdevent returns event
        return TriggerRegisterVariableEvent( t, "HERD_HERD_EVENT", EQUAL, herdevent )
    endfunction
    //module ForHerdHeader
    //endmodule
    //module ForEnumHerd
        //loop
    //endmodule
    //module EndForEnumHerd
        //endloop
    //endmodule
endlibrary
JASS:
library Example initializer init
    globals
        public BEHAVIOR ROAM
        public BEHAVIOR BREED
        public BEHAVIOR STAND
        
        public herdtype ZOMBIES
        public herdtype FEL_STALKER
        public herdtype FEL_BEAST
        public herdtype SHEEP
        
        public real MIN_X =GetRectMinX(bj_mapInitialPlayableArea)
        public real MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
        public real MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)
        public real MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
        
        private constant integer CONTINUE = 3
        private constant integer FAIL     = 5
        private constant integer WAIT     = 1
        constant integer ORDER_MOVE = 851986
    endglobals
    struct roam extends array
    static method main takes nothing returns boolean
        local behavior b = GetEvalBehavior()
        local real x
        local real y
        local real d
        local real angle
        implement BehaviorTopOfCode
        //call DisplayTextToPlayer(Player(0),0,0,"RunningRoam")
        if Behaviors_END_IT then
            set b.running = false
            set Behaviors_RETURN = CONTINUE
        else
            if not b.initialized then
                //call DisplayTextToPlayer(Player(0),0,0,"Initializing")
                set b.data7= SquareRoot((b.data1)*(b.data1)+(b.data2)*(b.data2))
                set b.data5 = h.cx - b.data1
                if b.data5 < MIN_X then
                    set b.data5 = MIN_X+500
                endif
                set b.data6 = h.cy-b.data2
                if b.data6 < MIN_Y then
                    set b.data6 = MIN_Y+500
                endif
                set b.data1 = h.cx + b.data1
                if b.data1 > MAX_X then
                    set b.data1 = MAX_X-500
                endif
                set b.data2 = h.cy+b.data2
                if b.data2 > MAX_Y then
                    set b.data2 = MAX_Y+500
                endif
                set b.initialized = false
            endif
             //call DisplayTextToPlayer(Player(0),0,0,"RunningRoam not End_IT")
            if not b.running then
                 //call DisplayTextToPlayer(Player(0),0,0,"b.running")
                 set b.running = true
                //loop
                    //set b.data3 = GetRandomReal(b.data5,b.data1)
                    //set b.data4 = GetRandomReal(b.data6,b.data2)
                    set x = h.x-h.cx
                    set y = h.y-h.cy
                    //call DisplayTextToPlayer(Player(0),0,0,"DoinAtan")
                    set angle =Atan2(y,x)+6.28318+GetRandomReal(-3.14159,3.14159)
                    //call DisplayTextToPlayer(Player(0),0,0,"Calculating D")
                    set d = SquareRoot((y)*(y)+(x)*(x))
                    //call DisplayTextToPlayer(Player(0),0,0,"GettingRandomValue")
                    //call DisplayTextToPlayer(Player(0),0,0,R2S(b.data7))
                    set d = GetRandomReal(b.data7/(d+.01)-400,b.data7)
                    //call DisplayTextToPlayer(Player(0),0,0,"GotRandomValue")
                    set x = Cos(angle)*d+h.cx
                    set y = Sin(angle)*d+h.cy
                    if x > b.data1 then
                        set x = b.data1
                    elseif x<b.data5 then
                        set x = b.data5
                    endif
                    if y > b.data2 then
                        set y = b.data2
                    elseif y<b.data6 then
                        set y = b.data6
                    endif
                    set b.data3 = x
                    set b.data4 = y
                    //call DisplayTextToPlayer(Player(0),0,0,R2S(x))
                    //exitwhen (b.data3-h.x)*(b.data3-h.x) > 1440000 or (b.data4-h.y)*(b.data4-h.y) > 1440000
                //endloop
            else
                if (b.data3-h.x)*(b.data3-h.x) < 360000 or  (b.data4-h.y)*(b.data4-h.y) < 360000 then
                    set b.running = false
                    set Behaviors_RETURN = CONTINUE
                    return false
                endif
            endif
            set x = b.data3
            set y = b.data4
            //call DisplayTextToPlayer(Player(0),0,0,"targetX ="+R2S(b.data3))
            implement BehaviorForMemberInHerd
                //call KillUnit(u)
                if (GetUnitCurrentOrder(u) != ORDER_MOVE) then
                    //call DisplayTextToPlayer(Player(0),0,0,"IssuedOrder"+I2S(OrderId("move")))
                    call IssuePointOrderById(u,ORDER_MOVE,GetRandomReal(x-500,x+500),GetRandomReal(y-500,y+500))
                    //call IssuePointOrder(u,"move",GetRandomReal(x-500,x+500),GetRandomReal(y-500,y+500))
                endif
            implement BehaviorForDead
            implement BehaviorEndForMember
        endif
        set Behaviors_RETURN = WAIT
        return true
    endmethod
    endstruct
    private function init takes nothing returns nothing
        local herd this
        set behavior.EVAL = CreateForce()
        set MIN_X =GetRectMinX(bj_mapInitialPlayableArea)
        set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
        set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)
        set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
        //call DisplayTextToPlayer(Player(0),0,0,"CreatingHtypeZomb")
        set ZOMBIES = herdtype.create(Filter(function roam.main), 'nzom',false)
        set ZOMBIES.head.data1 = 8000
        set ZOMBIES.head.data2 = 8000
        set ZOMBIES.head.data7 = 10000
        set ZOMBIES.alphaMin = 50
        set ZOMBIES.alphaMax = 103
        //call DisplayTextToPlayer(Player(0),0,0,"CreatingHerd")
        set this = herd.create(ZOMBIES, Player(0),0,0,12)
        //call DisplayTextToPlayer(Player(0),0,0,"HerdCreated")
        set FEL_STALKER = herdtype.create(Filter(function roam.main),'nfel',false)
        set FEL_STALKER.head.data1 = 10000
        set FEL_STALKER.head.data2 = 10000
        set FEL_STALKER.sizeMin    =.7
        set FEL_STALKER.greenMin   =120
        set FEL_STALKER.blueMin    =120
        //set this = herd.create(FEL_STALKER,Player(1),3000,0,15)
        set FEL_BEAST = herdtype.create(Filter(function roam.main),'npfl',false)
        set FEL_BEAST.head.data1 = 8000
        set FEL_BEAST.head.data2 = 8000
        set FEL_BEAST.sizeMin = 1.2
        set FEL_BEAST.sizeMax = 1.5
        set FEL_BEAST.alphaMin = 200
        set FEL_BEAST.blueMin = 100
        //set this = herd.create(FEL_BEAST,Player(2),-3000,0,14)
    endfunction
endlibrary
 
Last edited:
Level 7
Joined
Jan 28, 2012
Messages
266
what should I call data1 data2 etc...? i do use an operator to set them, maybe i should make them private.
@mcill i removed getevalsBehaviors Public state.

Updates Planned
I am planning on adding behaviors for when a member dying, when they are attacked etc...
 
Level 7
Joined
Jan 28, 2012
Messages
266
@mckill, I might decide to remove the data variables all together, and instead have the user use arrays for it... that probably would be better, since you might need units, other Herds etc as attached information.

offtopic c6??, i go e4
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
Instead of data1-8, you could do this:

JASS:
private static real array pData

method operator [] takes integer i returns real
    return thistype.pData[(this-1)*8+i]
endmethod


method operator []= takes integer i,real r returns nothing
    set thistype.pData[(this-1)*8+i]=r
endmethod

This way the system could only handle ~1000 Behaviour instances... big deal.
 
Status
Not open for further replies.
Top