1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
  3. We've created a new forum: Overwatch! Discuss gameplay strategies, team composition, lootboxes and much more!
    Dismiss Notice
  4. The dust has finally settled for Hive's first joint Hero Contest. Come check out the results and comment on what you think should be a Neutral Tavern Hero!
    Dismiss Notice
  5. The awesome entries of Mini-Mapping Contest #13 - Ghastly Realm are finished! Vote for your favorite here!
    Dismiss Notice
  6. Last squad standing! The results for Modeling Contest - Squad are out. Congratulate the winners!
    Dismiss Notice
  7. The results for the 4-way challenge is up! Congratulate the winner and take pity on the losers here!
    Dismiss Notice
  8. Looking to expand an altered melee race of yours? Join the Advanced Techtree Contest - Recycle, Revise, Reinvent contest!
    Dismiss Notice

Dummy Recycler v1.25

Submitted by Flux
This bundle is marked as approved. It works and satisfies the submission rules.

  • Code (vJASS):

    library DummyRecycler /*
     
    //                      DummyRecycler v1.25
    //                          by Flux
    //
    //  A system that recycles dummy units while considering their facing angle.
    //  It can be used as attachment dummies for visual effects or as dummy caster.
    //
    //  Why is recycling a unit important?
    //      Because creating a unit is is one of the slowest function in the game
    //      and there are reports that will always leave a permanent tiny bit of
    //      memory (0.04 KB).
    //      On average, retrieving a pending Dummy is approximately 4x faster compared
    //      to creating a new one and recycling a Dummy compared to removing it is
    //      approximately 1.3x faster.
    //      Furthermore, if you're using a lot of "Unit has entered map" events,
    //      using this system will even result to even more better performance
    //      because retrieving Dummy units does not cause that event to run.


        */
    requires /*
           nothing
         
        */
    optional Table/*
            if not found, this system will use a hashtable. Hashtables are limited to
            255 per map.
         
        */
    optional WorldBounds /*
            if not found, this system will initialize its own Map Boundaries.
    //
    //
    //  Features:
    //
    //    -- Dummy Sharing
    //        When a Dummy List gets low on unit count, it will borrow Dummy Units
    //        from the Dummy List with the highest unit count. The transfer is not
    //        instant because the shared Dummy Unit has to turn to the appropriate
    //        angle of its new Dummy List before it can be recycled.
    //        See BORROW_REQUEST.
    //
    //    -- Self-balancing recycling algorithm
    //        Recycled Dummy Units will be thrown to the List having the least number
    //        of Dummy Units.
    //
    //    -- Recycling least used
    //        Allows recycling a Dummy from the Dummy List with the highest
    //        unit count. It is useful when the facing angle of the Dummy Unit
    //        does not matter.
    //        See GetRecycledDummyAnyAngle.
    //
    //    -- Self-adaptation
    //        When there are no free Dummy Units from a Dummy List, it will end up creating
    //        a new unit instead but that unit will be permanently added as a Dummy
    //        Unit to be recycled increasing the overall total Dummy Unit count.
    //
    //    -- Count control
    //        Allows limiting the overall number of Dummy Units.
    //        See MAX_DUMMY_COUNT.
    //
    //    -- Delayed Recycle
    //        Allows recycling Dummy Units after some delay to allocate time for the
    //        death animation of Special Effects to be seen.
    //        See DummyAddRecycleTimer.
    //
    // ******************************************************************
    // ***************************** API: *******************************
    // ******************************************************************
    //
    //  function GetRecycledDummy takes real x, real y, real z, real facing returns unit
    //      - Retrieve an unused Dummy Unit from the List.
    //      - The equivalent of CreateUnit.
    //      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
    //
    //  function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
    //      - Use this function if the facing angle of the Dummy doesn't matter to you.
    //      - It will return a unit from the list having the highest number of unused Dummy Units.
    //      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
    //
    //  function RecycleDummy takes unit u returns nothing
    //      - Recycle the Dummy unit for it to be used again later.
    //      - The equivalent of RemoveUnit.
    //
    //  function DummyAddRecycleTimer takes unit u, real time returns nothing
    //      - Recycle the Dummy unit after a certain time.
    //      - Use this to allocate time for the the death animation of an effect attached to the
    //        Dummy Unit to finish..
    //      - The equivalent of UnitApplyTimedLife.
    //
    //  function ShowDummy takes unit u, boolean flag returns nothing
    //      - Shows/hides Dummy Unit without conflicting with the Locust ability.
    //
    //--------------------
    //      CREDITS
    //--------------------
    //  Bribe - for the MissileRecycler (vJASS) where I got this concept from
    //       http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
    //        - for the optional Table
    //       http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
    //  Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
    //       http://www.wc3c.net/showthread.php?t=101150
    //  Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
    //       http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
    //  Nestharus - for the data structure
    //       http://www.hiveworkshop.com/forums/2809461-post7.html
    //            - for the optional WorldBounds
    //       http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j

    // =============================================================== //
    // ====================== CONFIGURATION ========================== //
    // =============================================================== */


        globals
            //The rawcode of the Dummy Unit
            private constant integer DUMMY_ID = 'dumi'
         
            //The owner of the Dummy Unit
            private constant player OWNER = Player(14)
         
            //The number of indexed angle. The higher the value the:
            // - Lesser the turning time for the Dummy Units.
            // - Higher the total number of Dummy Units created at Map Initialization.
            //          Recommended Value: 10 (Max difference of 18 degrees)
            private constant integer ANGLES_COUNT = 10
         
            //The number of Dummy units per ANGLES_COUNT. The higher the value the:
            // - Higher the number of units that can be recycled per angle, when
            //   no more units are in queue, the system will resort to use CreateUnit.
            // - Higher the total number of Dummy Units created at Map Initialization.
            //    Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
            private constant integer STORED_UNIT_COUNT = 3
         
            //The maximum number of Dummy units that can exist. When the system resort
            //to using CreateUnit, the unit will be permanently added to the Dummy
            //List. To avoid spamming Dummy Units and having too much free Dummy
            //Units to allocate, the maximum number of Dummy Units is capped.
            //               Recommended Value: 80 to 120
            private constant integer MAX_DUMMY_COUNT = 100
         
            //When a certain angle have less than BORROW_REQUEST units in its list,
            //it will start to borrow Dummy Units from the list with the highest
            //Dummy Unit count.
            //      Recommended Value: Half of maximum STORED_UNIT_COUNT
            private constant integer BORROW_REQUEST = 5
         
            //It will only return a Dummy if the current dummy is close
            //to it's appropriate facing angle. This is to avoid returning
            //a Dummy which is still turning to face it's list angle.
            private constant real ANGLE_TOLERANCE = 10.0
         
            //An additional option to automatically hide recycled dummy units in the
            //corner of the map camera bounds
            private constant boolean HIDE_ON_MAP_CORNER = true
        endglobals
     
        //Every time a new dummy unit is retrieved, it will apply this resets
        //If it is redundant/you dont need it, remove it.
        //! textmacro DUMMY_UNIT_RESET
            call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
            call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
            call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
            call ShowDummy(bj_lastCreatedUnit, true)
        //! endtextmacro
    // =============================================================== //
    // ==================== END CONFIGURATION ======================== //
    // =============================================================== //
     
     
        globals
            private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
            private real array angle
            private integer array count
            private integer array countHead
            private integer array countNext
            private integer array countPrev
            private integer array next
            private integer array prev
            private unit array dummy
            private integer upper
            private integer lower
            private integer lastInstance
            private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
        endglobals
     
        static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
            private module BoundsInit
         
                readonly static real x
                readonly static real y
             
                private static method onInit takes nothing returns nothing
                    local rect map = GetWorldBounds()
                    set thistype.x = GetRectMaxX(map)
                    set thistype.y = GetRectMaxY(map)
                    call RemoveRect(map)
                    set map = null
                endmethod
             
            endmodule
         
            private struct Bounds extends array
                implement BoundsInit
            endstruct
        endif
     
        private module M
         
            static if LIBRARY_Table then
                static Table tb
            else
                static hashtable hash = InitHashtable()
            endif
         
            private static method onInit takes nothing returns nothing
                local real add = 360.0/ANGLES_COUNT
                local real a = 0
                local integer this = ANGLES_COUNT
                local integer head = 0
                local integer cHead = JASS_MAX_ARRAY_SIZE - 1   //avoid allocation collision
                local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
                set upper = STORED_UNIT_COUNT
                set lower = STORED_UNIT_COUNT
                static if LIBRARY_Table then
                    set tb = Table.create()
                endif
                //Initialize countHeads
                loop
                    exitwhen i < 0
                    set countNext[cHead] = cHead
                    set countPrev[cHead] = cHead
                    set countHead[i] = cHead
                    set cHead = cHead - 1
                    set i = i - 1
                endloop
                set cHead = countHead[STORED_UNIT_COUNT]  //All heads will be inserted here initially
                //Create the Dummy units
                loop
                    exitwhen a >= 360
                    //Initialize head
                    set next[head] = head
                    set prev[head] = head
                    set count[head] = STORED_UNIT_COUNT
                    set angle[head] = a
                    //Insert head in the Count List
                    set countNext[head] = cHead
                    set countPrev[head] = countPrev[cHead]
                    set countNext[countPrev[head]] = head
                    set countPrev[countNext[head]] = head
                    set i = 0
                    loop
                        exitwhen i >= STORED_UNIT_COUNT
                        //Queued Linked List
                        set next[this] = head
                        set prev[this] = prev[head]
                        set next[prev[this]] = this
                        set prev[next[this]] = this
                        static if HIDE_ON_MAP_CORNER then
                            static if LIBRARY_WorldBounds then
                                set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
                            else
                                set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
                            endif
                        else
                            set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
                        endif
                        call PauseUnit(dummy[this], true)
                        static if LIBRARY_Table then
                            set tb[GetHandleId(dummy[this])] = this
                        else
                            call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
                        endif
                        set this = this + 1
                        set i = i + 1
                    endloop
                    set head = head + 1
                    set a = a + add
                endloop
                set lastInstance = this
            endmethod
         
        endmodule
     
        private struct S extends array
            implement M
        endstruct
     
        private function GetHead takes integer facing returns integer
            if facing < 0 or facing >= 360 then
                set facing = facing - (facing/360)*360
                if facing < 0 then
                    set facing = facing + 360
                endif
            endif
            return R2I((facing*ANGLES_COUNT/360.0))
        endfunction
     
        function ShowDummy takes unit u, boolean flag returns nothing
            if IsUnitHidden(u) == flag then
                call ShowUnit(u, flag)
                if flag and GetUnitTypeId(u) == DUMMY_ID then
                    call UnitRemoveAbility(u, 'Aloc')
                    call UnitAddAbility(u, 'Aloc')
                endif
            endif
        endfunction
     
        function GetRecycledDummy takes real x, real y, real z, real facing returns unit
            local integer head = GetHead(R2I(facing + FACING_OFFSET))
            local integer this = next[head]
            local integer cHead
         
            //If there are Dummy Units in the Queue List already facing close to the appropriate angle
            if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
                //Remove from the Queue List
                set next[prev[this]] = next[this]
                set prev[next[this]] = prev[this]
                //For double free protection
                set next[this] = -1
                //Unit Properties
                set bj_lastCreatedUnit = dummy[this]
                call SetUnitX(bj_lastCreatedUnit, x)
                call SetUnitY(bj_lastCreatedUnit, y)
                call SetUnitFacing(bj_lastCreatedUnit, facing)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
                //! runtextmacro DUMMY_UNIT_RESET()
                //Update Count and Bounds
                set count[head] = count[head] - 1
             
                //------------------------------------------------
                //                 Unit Sharing
                //------------------------------------------------
                if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
                    set count[head] = count[head] + 1
                    set this = next[countNext[countHead[upper]]]
                    call SetUnitFacing(dummy[this], angle[head])
                    //Remove
                    set next[prev[this]] = next[this]
                    set prev[next[this]] = prev[this]
                    //Add to the Current List
                    set next[this] = head
                    set prev[this] = prev[head]
                    set next[prev[this]] = this
                    set prev[next[this]] = this
                    set head = countNext[countHead[upper]]
                    set count[head] = count[head] - 1
                endif
             
                //---------------------------
                //Update Count Lists
                //---------------------------
                //Remove from the current Count List
                set countNext[countPrev[head]] = countNext[head]
                set countPrev[countNext[head]] = countPrev[head]
                //Add to the new Count List
                set cHead = countHead[count[head]]
                set countNext[head] = cHead
                set countPrev[head] = countPrev[cHead]
                set countNext[countPrev[head]] = head
                set countPrev[countNext[head]] = head
             
                //---------------------------
                //  Update Bounds
                //---------------------------
                set cHead = countHead[upper]
                if countNext[cHead] == cHead then
                    set upper = upper - 1
                endif
                if count[head] < lower then
                    set lower = count[head]
                endif
            else
                set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
                call PauseUnit(bj_lastCreatedUnit, true)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
                if dummyCount < MAX_DUMMY_COUNT then
                    set this = lastInstance
                    //For double free protection
                    set next[this] = -1
                    set dummy[this] = bj_lastCreatedUnit
                    static if LIBRARY_Table then
                        set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
                    else
                        call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
                    endif
                    set lastInstance = lastInstance + 1
                endif
                set dummyCount = dummyCount + 1
            endif

            return bj_lastCreatedUnit
        endfunction
     
        function RecycleDummy takes unit u returns nothing
            static if LIBRARY_Table then
                local integer this = S.tb[GetHandleId(u)]
            else
                local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
            endif
            local integer head
            local integer cHead
         
            //If the unit is a legit Dummy Unit
            if this > 0 and next[this] == -1 then
                //Find where to insert based on the list having the least number of units
                set head = countNext[countHead[lower]]
                set next[this] = head
                set prev[this] = prev[head]
                set next[prev[this]] = this
                set prev[next[this]] = this
                //Update Status
                call SetUnitFacing(u, angle[head])
                call PauseUnit(u, true)
                call SetUnitOwner(u, OWNER, false)
                static if HIDE_ON_MAP_CORNER then
                    static if LIBRARY_WorldBounds then
                        call SetUnitX(u, WorldBounds.maxX)
                        call SetUnitY(u, WorldBounds.maxY)
                    else
                        call SetUnitX(u, Bounds.x)
                        call SetUnitY(u, Bounds.y)
                    endif
                else
                    call SetUnitScale(u, 0, 0, 0)
                    call SetUnitVertexColor(u, 0, 0, 0, 0)
                endif
                set count[head] = count[head] + 1
             
                //---------------------------
                //    Update Count Lists
                //---------------------------
                //Remove
                set countNext[countPrev[head]] = countNext[head]
                set countPrev[countNext[head]] = countPrev[head]
                //Add to the new Count List
                set cHead = countHead[count[head]]
                set countNext[head] = cHead
                set countPrev[head] = countPrev[cHead]
                set countNext[countPrev[head]] = head
                set countPrev[countNext[head]] = head
             
                //---------------------------
                //  Update Bounds
                //---------------------------
                set cHead = countHead[lower]
                if countNext[cHead] == cHead then
                    set lower = lower + 1
                endif
                if count[head] > upper then
                    set upper = count[head]
                endif
            elseif this == 0 then
                call RemoveUnit(u)
            debug elseif next[this] != -1 then
                debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
            endif
         
        endfunction
     
        private function Expires takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local integer id = GetHandleId(t)
            static if LIBRARY_Table then
                call RecycleDummy(S.tb.unit[id])
                call S.tb.unit.remove(id)
            else
                call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
                call FlushChildHashtable(S.hash, id)
            endif
            call DestroyTimer(t)
            set t = null
        endfunction

        function DummyAddRecycleTimer takes unit u, real time returns nothing
            local timer t = CreateTimer()
            static if LIBRARY_Table then
                set S.tb.unit[GetHandleId(t)] = u
            else
                call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
            endif
            call TimerStart(t, time, false, function Expires)
            set t = null
        endfunction
     
        function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
            return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
        endfunction
     
        // runtextmacro DUMMY_DEBUG_TOOLS()
     
    endlibrary
     
  • Code (vJASS):

    //                      DummyRecycler v1.25
    //                          by Flux
    //
    //  A system that recycles dummy units while considering their facing angle.
    //  It can be used as attachment dummies for visual effects or as dummy caster.
    //
    //  Why is recycling a unit important?
    //      Because creating a unit is is one of the slowest function in the game
    //      and there are reports that will always leave a permanent tiny bit of
    //      memory (0.04 KB).
    //      On average, retrieving a pending Dummy is approximately 4x faster compared
    //      to creating a new one and recycling a Dummy compared to removing it is
    //      approximately 1.3x faster.
    //      Furthermore, if you're using a lot of "Unit has entered map" events,
    //      using this system will even result to even more better performance
    //      because retrieving Dummy units does not cause that event to run.
    //
    //
    //  Features:
    //
    //    -- Dummy Sharing
    //        When a Dummy List gets low on unit count, it will borrow Dummy Units
    //        from the Dummy List with the highest unit count. The transfer is not
    //        instant because the shared Dummy Unit has to turn to the appropriate
    //        angle of its new Dummy List before it can be recycled.
    //        See BORROW_REQUEST.
    //
    //    -- Self-balancing recycling algorithm
    //        Recycled Dummy Units will be thrown to the List having the least number
    //        of Dummy Units.
    //
    //    -- Recycling least used
    //        Allows recycling a Dummy from the Dummy List with the highest
    //        unit count. It is useful when the facing angle of the Dummy Unit
    //        does not matter.
    //        See GetRecycledDummyAnyAngle.
    //
    //    -- Self-adaptation
    //        When there are no free Dummy Units from a Dummy List, it will end up creating
    //        a new unit instead but that unit will be permanently added as a Dummy
    //        Unit to be recycled increasing the overall total Dummy Unit count.
    //
    //    -- Count control
    //        Allows limiting the overall number of Dummy Units.
    //        See MAX_DUMMY_COUNT.
    //
    //    -- Delayed Recycle
    //        Allows recycling Dummy Units after some delay to allocate time for the
    //        death animation of Special Effects to be seen.
    //        See DummyAddRecycleTimer.
    //
    // ******************************************************************
    // ***************************** API: *******************************
    // ******************************************************************
    //
    //  function GetRecycledDummy takes real x, real y, real z, real facing returns unit
    //      - Retrieve an unused Dummy Unit from the List.
    //      - The equivalent of CreateUnit.
    //      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
    //
    //  function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
    //      - Use this function if the facing angle of the Dummy doesn't matter to you.
    //      - It will return a unit from the list having the highest number of unused Dummy Units.
    //      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
    //
    //  function RecycleDummy takes unit u returns nothing
    //      - Recycle the Dummy unit for it to be used again later.
    //      - The equivalent of RemoveUnit.
    //
    //  function DummyAddRecycleTimer takes unit u, real time returns nothing
    //      - Recycle the Dummy unit after a certain time.
    //      - Use this to allocate time for the the death animation of an effect attached to the
    //        Dummy Unit to finish..
    //      - The equivalent of UnitApplyTimedLife.
    //
    //  function ShowDummy takes unit u, boolean flag returns nothing
    //      - Shows/hides Dummy Unit without conflicting with the Locust ability.
    //
    //--------------------
    //      CREDITS
    //--------------------
    //  Bribe - for the MissileRecycler (vJASS) where I got this concept from
    //       http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
    //        - for the optional Table
    //       http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
    //  Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
    //       http://www.wc3c.net/showthread.php?t=101150
    //  Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
    //       http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
    //  Nestharus - for the data structure
    //       http://www.hiveworkshop.com/forums/2809461-post7.html
    //            - for the optional WorldBounds
    //       http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j

    // =============================================================== //
    // ====================== CONFIGURATION ========================== //
    // =============================================================== //

    //The rawcode of the Dummy Unit
    constant function Dummy_Rawcode takes nothing returns integer
        return 'dumi'
    endfunction

    //The owner of the Dummy Unit
    constant function Dummy_Owner takes nothing returns player
        return Player(14)
    endfunction

    //The number of indexed angle. The higher the value the:
    // - Lesser the turning time for the Dummy Units.
    // - Higher the total number of Dummy Units created at Map Initialization.
    //          Recommended Value: 10 (Max difference of 18 degrees)
    constant function Dummy_Angles takes nothing returns integer
        return 10
    endfunction

    //The number of Dummy units per Dummy_Angle. The higher the value the:
    // - Higher the number of units that can be recycled per angle, when
    //   no more units are in queue, the system will resort to use CreateUnit.
    // - Higher the total number of Dummy Units created at Map Initialization.
    //    Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
    constant function Dummy_StoredUnits takes nothing returns integer
        return 3
    endfunction

    //The maximum number of Dummy units that can exist. When the system resort
    //to using CreateUnit, the unit will be permanently added to the Dummy
    //List. To avoid spamming Dummy Units and having too much free Dummy
    //Units to allocate, the maximum number of Dummy Units is capped.
    //               Recommended Value: 80 to 120
    constant function Dummy_MaxCount takes nothing returns integer
        return 100
    endfunction

    //When a certain angle have less than BORROW_REQUEST units in its list,
    //it will start to borrow Dummy Units from the list with the highest
    //Dummy Unit count.
    //      Recommended Value: Half of maximum Dummy_StoredUnits
    constant function Dummy_BorrowRequest takes nothing returns integer
        return 5
    endfunction

    //It will only return a Dummy if the current dummy is close
    //to it's appropriate facing angle. This is to avoid returning
    //a Dummy which is still turning to face it's list angle.
    constant function Dummy_AngleTolerance takes nothing returns real
        return 10.0
    endfunction

    //An additional option to automatically hide recycled dummy units in the
    //corner of the map camera bounds
    constant function Dummy_HideOnMapCorner takes nothing returns boolean
        return true
    endfunction

    // =============================================================== //
    // ==================== END CONFIGURATION ======================== //
    // =============================================================== //

    function Dummy_GetHead takes integer facing returns integer
        if facing < 0 or facing >= 360 then
            set facing = facing - (facing/360)*360
            if facing < 0 then
                set facing = facing + 360
            endif
        endif
        return R2I((facing*Dummy_Angles()/360.0))
    endfunction

    function ShowDummy takes unit u, boolean flag returns nothing
        if IsUnitHidden(u) == flag then
            call ShowUnit(u, flag)
            if flag and GetUnitTypeId(u) == Dummy_Rawcode() then
                call UnitRemoveAbility(u, 'Aloc')
                call UnitAddAbility(u, 'Aloc')
            endif
        endif
    endfunction

    function GetRecycledDummy takes real x, real y, real z, real facing returns unit
        local integer head = Dummy_GetHead(R2I(facing + 180.0/Dummy_Angles()))
        local integer this = udg_Dummy_Next[head]
        local integer countHead
     
     
        //If there are Dummy Units in the Queue List already facing the appropriate angle
        if this != head and RAbsBJ(GetUnitFacing(udg_Dummy_Unit[this]) - udg_Dummy_Angle[head]) <= Dummy_AngleTolerance() then
            //Remove from the Queue List
            set udg_Dummy_Next[udg_Dummy_Prev[this]] = udg_Dummy_Next[this]
            set udg_Dummy_Prev[udg_Dummy_Next[this]] = udg_Dummy_Prev[this]
            //For double free protection
            set udg_Dummy_Next[this] = -1
            //Unit Properties
            set bj_lastCreatedUnit = udg_Dummy_Unit[this]
            call SetUnitX(bj_lastCreatedUnit, x)
            call SetUnitY(bj_lastCreatedUnit, y)
            call SetUnitFacing(bj_lastCreatedUnit, facing)
            call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
            call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            call ShowDummy(bj_lastCreatedUnit, true)
            //------------------------------------------------
            //       Comment out resets you don't need
            //------------------------------------------------
            call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
            call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
            //Update Count and Bounds
            set udg_Dummy_Count[head] = udg_Dummy_Count[head] - 1
         
            //------------------------------------------------
            //                 Unit Sharing
            //------------------------------------------------
            if udg_Dummy_Count[head] < Dummy_BorrowRequest() and udg_Dummy_Count[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]] > udg_Dummy_Count[head] then
                set udg_Dummy_Count[head] = udg_Dummy_Count[head] + 1
                //Take an instance from the UpperBound list
                set this = udg_Dummy_Next[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]]
                call SetUnitFacing(udg_Dummy_Unit[this], udg_Dummy_Angle[head])
                //Remove
                set udg_Dummy_Next[udg_Dummy_Prev[this]] = udg_Dummy_Next[this]
                set udg_Dummy_Prev[udg_Dummy_Next[this]] = udg_Dummy_Prev[this]
                //Add to the Current List
                set udg_Dummy_Next[this] = head
                set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
                set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
                set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
                set head = udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]
                set udg_Dummy_Count[head] = udg_Dummy_Count[head] - 1
            endif
            //---------------------------
            //Update Count Lists
            //---------------------------
            //Remove from the current Count List
            set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = udg_Dummy_CountNext[head]
            set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = udg_Dummy_CountPrev[head]
            //Add to the new Count List
            set countHead = udg_Dummy_CountHead[udg_Dummy_Count[head]]
            set udg_Dummy_CountNext[head] = countHead
            set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
            set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
            set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
         
            //---------------------------
            //  Update Bounds
            //---------------------------
            set countHead = udg_Dummy_CountHead[udg_Dummy_Upper]
            if udg_Dummy_CountNext[countHead] == countHead then
                set udg_Dummy_Upper = udg_Dummy_Upper - 1
            endif
            if udg_Dummy_Count[head] < udg_Dummy_Lower  then
                set udg_Dummy_Lower = udg_Dummy_Count[head]
            endif
        else
            set bj_lastCreatedUnit = CreateUnit(Dummy_Owner(), Dummy_Rawcode(), x, y, facing)
            call PauseUnit(bj_lastCreatedUnit, true)
            call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            if udg_Dummy_UnitCount < Dummy_MaxCount() then
                set this = udg_Dummy_LastInstance
                set udg_Dummy_Next[this] = -1
                set udg_Dummy_Unit[this] = bj_lastCreatedUnit
                call SaveInteger(udg_Dummy_Hashtable, GetHandleId(bj_lastCreatedUnit), 0, this)
                set udg_Dummy_LastInstance = udg_Dummy_LastInstance + 1
            endif
            set udg_Dummy_UnitCount = udg_Dummy_UnitCount + 1
        endif

        return bj_lastCreatedUnit
    endfunction

    function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
        return GetRecycledDummy(x, y, z, udg_Dummy_Angle[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]])
    endfunction


    function RecycleDummy takes unit u returns nothing
        local integer this = LoadInteger(udg_Dummy_Hashtable, GetHandleId(u), 0)
        local integer head
        local integer countHead
     
        //If the unit is a legit Dummy Unit
        if this > 0 and udg_Dummy_Next[this] == -1 then
            //Find where to insert based on the list having the least number of units
            set head = udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Lower]]
            set udg_Dummy_Next[this] = head
            set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
            set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
            set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
            //Update Status
            call SetUnitFacing(u, udg_Dummy_Angle[head])
            call PauseUnit(u, true)
            call SetUnitOwner(u, Dummy_Owner(), false)
            if Dummy_HideOnMapCorner() then
                call SetUnitX(u, udg_Dummy_X)
                call SetUnitY(u, udg_Dummy_Y)
            else
                call SetUnitVertexColor(u, 0, 0, 0, 0)
            endif
            set udg_Dummy_Count[head] = udg_Dummy_Count[head] + 1
         
            //---------------------------
            //    Update Count Lists
            //---------------------------
            //Remove
            set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = udg_Dummy_CountNext[head]
            set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = udg_Dummy_CountPrev[head]
            //Add to the new Count List
            set countHead = udg_Dummy_CountHead[udg_Dummy_Count[head]]
            set udg_Dummy_CountNext[head] = countHead
            set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
            set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
            set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
         
            //---------------------------
            //  Update Bounds
            //---------------------------
            set countHead = udg_Dummy_CountHead[udg_Dummy_Lower]
            if udg_Dummy_CountNext[countHead] == countHead then
                set udg_Dummy_Lower = udg_Dummy_Lower + 1
            endif
            if udg_Dummy_Count[head] > udg_Dummy_Upper then
                set udg_Dummy_Upper = udg_Dummy_Count[head]
            endif
        elseif this == 0 then
            //User tries to recycle an invalid unit, remove the unit instead
            call RemoveUnit(u)
        endif
     
    endfunction

    function Dummy_TimerExpires takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        call RecycleDummy(LoadUnitHandle(udg_Dummy_Hashtable, id, 0))
        call FlushChildHashtable(udg_Dummy_Hashtable, id)
        call DestroyTimer(t)
        set t = null
    endfunction

    function DummyAddRecycleTimer takes unit u, real time returns nothing
        local timer t = CreateTimer()
        call SaveUnitHandle(udg_Dummy_Hashtable, GetHandleId(t), 0, u)
        call TimerStart(t, time, false, function Dummy_TimerExpires)
        set t = null
    endfunction

    function InitTrig_DummyRecycler_JASS takes nothing returns nothing
        local integer id = Dummy_Rawcode()
        local player owner = Dummy_Owner()
        local integer unitCount = Dummy_StoredUnits()
        local real add = 360.0/Dummy_Angles()
        local real angle = 0
        local integer this = Dummy_Angles()
        local integer head = 0
        local integer countHead = JASS_MAX_ARRAY_SIZE - 1   //avoid allocation collision
        local integer i = R2I(Dummy_MaxCount()/Dummy_Angles() + 0.5)
        local rect r
        set udg_Dummy_Hashtable = InitHashtable()
        set udg_Dummy_Upper = unitCount
        set udg_Dummy_Lower = unitCount
        //Create Map Bounds
        if Dummy_HideOnMapCorner() then
            set r = GetWorldBounds()
            set udg_Dummy_X = GetRectMaxX(r)
            set udg_Dummy_Y = GetRectMaxY(r)
            call RemoveRect(r)
            set r = null
        endif
        //Initialize countHeads
        loop
            exitwhen i < 0
            set udg_Dummy_CountNext[countHead] = countHead
            set udg_Dummy_CountPrev[countHead] = countHead
            set udg_Dummy_CountHead[i] = countHead
            set countHead = countHead - 1
            set i = i - 1
        endloop
        set countHead = udg_Dummy_CountHead[unitCount]  //All heads will be inserted here initially
        //Create the Dummy units
        loop
            exitwhen angle >= 360
            //Initialize head
            set udg_Dummy_Next[head] = head
            set udg_Dummy_Prev[head] = head
            set udg_Dummy_Count[head] = unitCount
            set udg_Dummy_Angle[head] = angle
            //Insert head in the Count List
            set udg_Dummy_CountNext[head] = countHead
            set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
            set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
            set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
            set i = 0
            loop
                exitwhen i >= unitCount
                //Queued Linked List
                set udg_Dummy_Next[this] = head
                set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
                set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
                set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
                //The actual unit
                set udg_Dummy_Unit[this] = CreateUnit(owner, id, udg_Dummy_X, udg_Dummy_Y, angle)
                call PauseUnit(udg_Dummy_Unit[this], true)
                call SaveInteger(udg_Dummy_Hashtable, GetHandleId(udg_Dummy_Unit[this]), 0, this)
                set this = this + 1
                set i = i + 1
            endloop
            set head = head + 1
            set angle = angle + add
        endloop
        set udg_Dummy_LastInstance = this
        set udg_Dummy_UnitCount = Dummy_Angles()*unitCount
    endfunction

    // runtextmacro DUMMY_DEBUG_TOOLS_JASS()
     



EXAMPLE OF USING THE SYSTEM
Example

  • Demo in GUI
  • Events
  • Player - Player 1 (Red) types a chat message containing gui as An exact match
  • Conditions
  • Actions
  • -------- ----------------------- --------
  • -------- for MUIness --------
  • -------- ----------------------- --------
  • Custom script: local effect e
  • Custom script: local unit u
  • -------- ----------------------- --------
  • -------- Retrieved a recycled dummy, use (Last Created Unit) to assign it to a variable --------
  • -------- ----------------------- --------
  • Custom script: call GetRecycledDummy(0, 0, 0, udg_Demo_Angle)
  • Set Demo_Unit = (Last created unit)
  • -------- ^Though no unit creation actually takes place --------
  • -------- ----------------------- --------
  • -------- Create the Effect --------
  • -------- ----------------------- --------
  • Special Effect - Create a special effect attached to the origin of Demo_Unit using abilities\weapons\WyvernSpear\WyvernSpearMissile.mdl
  • -------- ----------------------- --------
  • -------- In the name of MUI --------
  • -------- ----------------------- --------
  • Custom script: set e = bj_lastCreatedEffect
  • Custom script: set u = udg_Demo_Unit
  • -------- ----------------------- --------
  • -------- Change size to make the demo more visible --------
  • -------- ----------------------- --------
  • Animation - Change Demo_Unit's size to (200.00%, 200.00%, 200.00%) of its original size
  • -------- ----------------------- --------
  • Game - Display to (All players) the text: ([JASS] Retrieved a Dummy with Attached Effect where facing = + (String(Demo_Angle)))
  • -------- ----------------------- --------
  • -------- Increase the Angle to make the next dummy face a different angle --------
  • -------- ----------------------- --------
  • Set Demo_Angle = (Demo_Angle + 4.50)
  • Wait 8.00 seconds
  • Custom script: call DestroyEffect(e)
  • Custom script: call DummyAddRecycleTimer(u, 2.0)



Speed comparison


Changelog

Changelog:
v1.00 - [6 April 2016]
- Initial Release

v1.10 - [8 April 2016]
- No longer comes in 'Basic' and 'Advance' versions. 'Basic' version is removed.
- Changed API name, removed the "Dummy_" prefix.
- Improved the data structure, all operations are now O(1).
- Recycling a unit is now the equivalent of removing a unit, thus users should use AddRecycleTimer to allow the death time of the attached effect to show.

v1.11 - [9 April 2016]
- Excess recycled units are no longer removed, but permanently added to the list.
- GetRecycledUnit will no longer return units that are still turning.
- Added a vJASS version since it is different from Bribe's MissileRecycler.

v1.12 - [21 April 2016]
- Added double free protection.
- Fixed real-type variable comparison bug.

v1.13 - [9 May 2016]
- Renamed API, uses the word 'dummy' instead of 'unit'.
- Replaced "boolean caster" with "real z" as input argument.
- Recycling the dummy no longer changes the position of the dummy.

v1.20 - [16 May 2016]
- Fixed Angle Difference calculation bug when facing is 0 degrees and list angle is 360 degrees ends up creating new units.
- Fixed a bug in GetRecycledDummyAnyAngle. Fixed by
angle[countNext[upper]]
->
angle[countNext[countHead[upper]]]

- Added a demo in GetRecycledDummyAnyAngle.
- Improved DummyDisplay debugging tool.
- Fixed unstable list when a list reaches a zero count. Fixed by
exitwhen i == 0
->
exitwhen i < 0
on
//Initialize countHeads
.
- Extra Dummy units are no longer permanently added and are removed instead to avoid having too much free Dummy units in the list, counteract spamming and to give better count control.

v1.21 - [17 May 2016]
- Re-added the feature where extra Dummy Units created are permanently added to the Dummy List.
- Added a parameter to set the maximum number of recyclable Dummy Units.
- Dummy Sharing will no longer requires the sharer (list with higher dummy count) to meet a condition. In other words, the ENABLE_SHARE/Dummy_EnableShare() parameter was removed.
- Optimized angle difference calculation, avoiding costly trigonometric and inverse trigonometric functions.
- Rephrased double free protection debug message.

v1.22 - [21 May 2016]
- Added an additional feature to move recycled units in the corner of the Map Camera Bounds.

v1.23 - [22 July 2016]
- Properly cleaned optional Table.
- Removed unnecessary PauseUnit upon retrieval.
- Added a lua Object Merger so that users can generate the dummy unit without copying from the test map.

v1.24 - [14 August 2016]
- vJASS version now uses a Module initializer.
- Improved description a bit.

v1.25 - [14 December 2016]
- Added ShowDummy function which allows you to show/hide a Dummy without problems.
- Retrieved Dummy Units are automatically shown by default even if they were hidden previously.



Keywords:
reduce, reuse, recycle, dummy
Contents

Dummy Recycler v1.25 (Map)

Reviews
Moderator
3rd May 2016 Your resource has been reviewed by BPower. In case of any questions or for reconfirming the moderator's rating, please make use of the Quick Reply function of this thread. Review: Highly recommended for projectile and fx...
  1. 3rd May 2016

    General Info

    Your resource has been reviewed by BPower.
    In case of any questions or for reconfirming the moderator's rating,
    please make use of the Quick Reply function of this thread.

    Review:

    Highly recommended for projectile and fx utility systems or
    for anyone who needs a lot of dummy units for his/her code.

    The option to share units among unit lists is very neat.
    I ran a couple of stress tests and everything seems to be in perfect order.

    In the vJass version the onInit function could be placed into a module initializer.

    Troubleshooting:

    • Nothing

    Review changelog:
    1. -
     
  2. Almia

    Almia
    Joined:
    Apr 24, 2012
    Messages:
    4,786
    You can still make the DummyRecycler without using a hashtable :D

    Overall, I think this should still be based on Bribe's Recycler :(

    [edit]
    GetRecycledDummy is much better than Dummy_GetRecycledUnit, same goes to RecycleDummy :D

    also, I think the recycle delay should be the same for all dummies, i mean, declare a time constant, then apply that to all recycled dummies

    Also, Allow the users to disable their Unit Indexers to prevent dummy indexing.
     
  3. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    How (but still being O(1))? Anyway, it doesn't matter much because hashtables are O(1).

    I honestly doesn't understand how Bribe's work. Didn't also bother to have a detailed look at it. Also, where's the fun in that if I'm just gonna copy someone else's design? I like thinking my own design.


    Agree, will change that in the next update.

    Actually, the AddRecycleTimer is just a bonus feature. Users will mostly just recycle the Dummy instantly. It will still show
    effect
    being destroyed because the Dummy's are not moved anywhere when recycled.

    I don't use Unit Indexer so I don't know how to do that.
     

    Attached Files:

    Last edited: Apr 8, 2016
  4. Almia

    Almia
    Joined:
    Apr 24, 2012
    Messages:
    4,786
    Add a function where they put the Indexer disabler in(see ToggleIndexer function in MissileRecycler)
     
  5. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    This does exactly what Bribe's does.

    A good one will always throw the unit into the least populated angle in O(1).


    I'll give you a hint on how to do this.

    list[1] = angles with a population of 1
    list[2] = angles with a population of 2
    list[3] = angles with a population of 3
    list[x] = angles with a population of x


    at most, only 3 lists may be active at a time. You would have a lower bound and an upper bound. Whenever an angle goes below the lower bound for population, it takes from the upper bound. When the upper bound becomes empty, the upper and lower bound are shifted down. As things get put in, then lower bound angles get populated first as they are the most sparse. When the lower bound becomes empty, the lower bound and upper bound are shifted up. This is how my recycler works and how you can achieve O(1) recycling while optimally throwing units into the most sparse angles : ).



    You'll also need the following

    list[90] = units facing direction 90
    list[270] = units facing direction 270
    list[x] = units facing direction x


    Angles are added to the first set of lists. Units are added to the second set of lists. You'll also need angle counts.


    count[angle] = value (how many units are in the list for an angle)





    This is the very best way to do unit recycling. Bribe has yet to implement this. It's only in my library, which is broken : p.
     
  6. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    However, when a Recycled Unit is thrown to the list with the least number, they will be able to see the Dummy Unit turning while it gets destroyed. Actually, that reminds me, this system currently throw recycled units to it's original list, where it should throw it to the list nearest to the current angle of the recycled unit so that the dummy will not be seen turning.

    This is still not O(1) because what if list[1] is empty? Then it will go to the next until it reaches a non-empty list?
    Example, all list contains 5 units. Then it will go to list[1] then list[2] then list[3] up to list[5].


    Currently, it's worst case scenario (DummyRecyclerEx) is O(n) and best case is O(1).
     
  7. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    The below is a snapshot of what the data structure looks like. If you were to retrieve a unit from angle 45, it'd be pushed into 0, which is below the lower bound. Because the upper bound is not empty, it would take from the upper bound and push angle 45 back to a count of 1. Angle 90 would be pushed down from 2 to 1.

    0 would never go to -1 because a unit is not popped off of a list unless that list isn't empty and has a ready unit.

    When recycling a unit, you'd always throw it into an angle in the lower bound. This keeps everything as evenly distributed as possible.

    When the upper bound becomes empty, bounds go down by 1. When the lower bound becomes empty, bounds go up by 1. For bounds, you only need the lower bound variable.

    [​IMG]



    Hope this makes things more clear : )


    If you still don't understand, I can try doing an animation ^_^
     

    Attached Files:

    Last edited: Apr 6, 2016
  8. IcemanBo

    IcemanBo

    Code Moderator

    Joined:
    Sep 6, 2013
    Messages:
    4,429
    ^ Make an animation.
     
  9. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    Thanks Nestharus! That makes it more clear.
    No need to animate but if you want to, feel free to do it.

    Though I disagree on this part, it should be thrown to the closest angle it is currently facing then the lower bound will take a unit from the upper bound. Else, when a unit facinf 80 degrees is recycled and the lower bound is 270 degrees, the recycled unit will be seen turning as the effect attached to it destroyed.
     
  10. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    Er, it's not going to be seen turning because it won't be visible. If you recycle it and it's seen turning after it's recycled, you're doing something wrong : P.
     
  11. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    Why would it be not visible? A destroyed
    effect
    still must show the death animation.
     
  12. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    because when it's recycled, it will already not have any effects on it...

    that's like calling something like RemoveUnit and complaining that the unit is visible after removing it. It doesn't even make sense >.<.


    If your recycling doesn't work like RemoveUnit, you've got some serious problems.


    Sending to the nearest angle doesn't provide any benefit because the recycle method should work in the same way RemoveUnit does. Once you call it, the unit vanishes immediately. I think good recycling will move the unit to the edge of the bounds of the map so that it's not visible.


    Recycle != KillUnit. Recycle = RemoveUnit : ).



    Now if you want to show the death animation on top of that, then you recycle on a delay.


    Not moving the unit can have unintended consequences in a map as it may interact with other elements of the map. For example, unit in range.
     
  13. BPower

    BPower
    Joined:
    Mar 18, 2012
    Messages:
    1,752
    Dummies, units with 'Aloc' in general, neither pop up in units in range enumeration nor do they fire the unit comes in range event.

    They fire unit enter region events.
     
  14. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    Then perhaps the SetUnitFacing bit should be done on a timer queue to allow for death animations? Maybe set facing after 3 seconds, which should be more than enough time for any death animation.
     
  15. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    Updated.
    - No longer comes in 'Basic' and 'Advance' versions. 'Basic' version is removed.
    - Changed API name, removed the "Dummy_" prefix.
    - Improved the data structure, all operations are now O(1).
    - Recycling a unit is now the equivalent of removing a unit, thus users should use AddRecycleTimer to allow the death time of the attached effect to show.
     
  16. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    You should recycle newly created units too. Shouldn't remove "excess" units. The data structure can adapt to new units being put into it.
     
  17. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    Okay, and on that part, it should not have been KillUnit but RemoveUnit. But it doesn't matter anyway since I'll change it.

    Do you think I also should add a safety mechanism on recycling unit. Because currently, let's say I recycle a unit facing 0 degree and it is thrown to 180 degrees (with empty list) so it is now starting to turn. However while turning, I retrieved a unit from the 180 degrees list but the only unit in the list is still turning. So should I only add it when the unit is completely turning already and the action should have created a new unit instead? However, it would require calculation based on turning speed and angle difference.
     
  18. Nestharus

    Nestharus
    Joined:
    Jul 10, 2007
    Messages:
    6,122
    if GetUnitFacing == angle, return unit
    otherwise create unit
     
  19. Flux

    Flux
    Joined:
    Feb 6, 2014
    Messages:
    2,099
    How the fuck did I fail to think that? :)
    Thanks!


    Update:
    - Excess recycled units are no longer removed, but permanently added to the list.
    - GetRecycledUnit will no longer return units that are still turning.
    - Added a vJASS version since it is different from Bribe's MissileRecycler.