1. The long-awaited results for Concept Art Contest #11 have finally been released!
    Dismiss Notice
  2. Join Texturing Contest #30 now in a legendary battle of mythological creatures!
    Dismiss Notice
  3. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  4. Hivers united and created a bunch of 2v2 melee maps. Vote for the best in our Melee Mapping Contest #4 - Poll!
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice

[System] MissileRecycler

Discussion in 'JASS Resources' started by Bribe, Oct 27, 2011.

  1. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Ok, I found myself needing a dummy recycler and this is written horribly... so I am writing my own that is actually written well ; |.


    #1, you shouldn't be using a queue for this
    #2, you should be able to recycle dead units
    #3, you should be recycling the units after a certain amount of time to allow death effects to recycle. Think of recycling as corpse decay =)
    #4, you should be getting units that have the same facing as the target facing... you aren't doing that right now. This makes the facing stuff pretty pointless.
    #5, you shouldn't be resetting the unit. Leave that up to the user. For example, Particle already does this, so it's pointless extra overhead. The only thing you should be changing is the facing and the x,y coords.

    So because I'm writing a bunch of stuff and I can't wait for you to update this (which you'll likely never do anyways), I'm writing my own ;p.
     
  2. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,419
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Code (vJASS):
    private constant real DEATH_TIME = 2. //Allow the special effect on
            //the unit to complete its "death" animation in this timeframe. Must
            //be higher than 0.74 seconds to allow the unit time to turn. This
            //number should not be lower than the maximum death-animation time of
            //your missile-units' effect attachments, just to be safe.


    This is a dummy unit recycler, not a unit recycler. Since most dummies have no corpse, you can't revive them so adding support for that would mean creating new units == ruins the point of the system.

    But anyway, I think this system works fine as it is. =)
     
  3. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    I see, interesting...

    and killing the unit would not make it produce an effect ;o.


    There is still the angle problem tho ;\.

    edit
    Also you need to use ShowUnit because even if the unit is off the screen with no model, wc3 renders it.

    I created 7200 units. With all of them shown with no models at the edge of the map (couldn't see them), wc3 used 700 megs of memory and the game was unplayable. With all of them hidden, wc3 used 163 megs of memory and game ran at 64 fps as if they weren't even there.

    Be a man and use ShowUnit, lol.
     
    Last edited: Mar 29, 2012
  4. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    The dummies are invisible (dummy.mdx file) so there is no problem there.

    ShowUnit is pretty heavyweight function call.

    I specifically said in the documentation that you can remove "resets" which you don't need.

    Safety is there by default. Enthusiasts can remove the safeties if they are confident they won't break the resource, but I don't recommend it of course.

    There is no problem with the angle, you change ANG_N to a higher number to increase the accuracy (8 is a little small). In my map I have ANG_N set to 12 and ANG_STORAGE_MAX set to 8.

    A queue is being used so it's FIFO. Then I use timestamps to make sure units are not getting recycled while they are still turning or their effect's death animation is still playing. As purge said, the timestamp can be configured via DEATH_TIME.

    Again there is no problem with the angle. I don't know where you are getting your ideas from.
     
  5. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    yes there is.. Don't believe me, do a test for yourself. My comp ran 64 fps with like 40000 units at 200 megs of memory when I hid them. When I didn't hide them (edge of map etc, no models), it was using like 700 megs of memory and wc3 was running at 1 fps. If you aren't going to remove the units, then you better hide them. Projectile creation/destruction isn't the main issue... it's projectile movement and keeping projectiles in the background from hogging resources (which yours currently does) that is ;o. Every unit you have on the map that isn't hidden hogs precious resources. I plan to hide all units that aren't currently auto hidden in my own resources (DummyCaster, etc). Btw, my DummyRecycler loads up 7200 units and uses almost nothing and does not slow wc3 down at all. Loading up all of the units means that no new unit will ever be created : ).


    Btw... how do you retrieve the target angle???

    If you want an angle of 270, how does your system return a unit that is facing 270 degrees? And I don't mean SetUnitFacing, I mean the unit is already facing 270 degrees. Furthermore, how does it retrieve a unit facing closest to the desired angle? For example, if 30 degrees is passed in, it shouldn't return a unit facing 150 degrees.

    I saw nothing in your resource that supported the above.
     
  6. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Look out for the init function, I commented how it generally works.

    Hiding units also hides the death effect from playing.
     
  7. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    But you're going to hide them after they expire right?

    edit
    had no problems getting death animation to run with a unit that was previously hidden

    edit
    This just in, pausing a unit also makes wc3 not use resources O-o.

    How fast is pause unit?

    edit
    pause unit is really fast and makes it so that wc3 does not hog resources. Use pause unit instead ;o.

    edit
    here is memory usage for pause unit and hide unit

    306,338 -> hide unit
    353,360 -> pause unit

    So paused units do consume more resources than hidden units, but PauseUnit is much, much faster. wc3 ran at 64 fps for both.

    I'm going to limit them to the same count to see if the memory usage is still different. If it turns out to be the same, then that just means pause unit was able to create many more units than hide unit was able to do ;o.

    edit
    apparently, they are both truly the same count. Hide unit uses less resources than pause unit does =o. I'm starting to wonder now if you couldn't just make a projectile system with paused projectiles and then unpause them before removing the effect =o.

    edit
    BRILLIANT, EFFECTS RAN, TRIGGERS RAN, SET UNIT FACING RAN, everything ran!! woo ;D

    keep all dummies paused!! O_O
     
    Last edited: Mar 29, 2012
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Nice find, Nestharus. I have added another mention in the credits for you for PauseUnit.

    I disagree with caching a lot of units. I only support ANG_N * ANG_STORAGE_MAX dummy units and I recommend to users to keep the multiple of those less than 100. Though I suppose with PauseUnit coming into force I could even recommend up to 200.

    Caching larger amounts of dummies is not advisable because not everyone has a lot of RAM. For example, I have 1GB of RAM and Windows 7 already consumes much of that. You have to consider who your target market is: warcraft 3 players. People with modern gaming rigs are less likely to play games that don't push their machines in the graphics range.

    Also, I like handle ID offsetting when I make my own maps just because I don't ever hit the handle stack limit. If I pre-cached thousands of units then I'd be wasting my time trying to offset, running a huge risk of overflow.

    This is how it works with the queues: I modulate the passed angle value to make sure the angle passed in is between 0 and 360, then I use truncating division by the value of 360 / ANG_N. I made sure each queue head points to the return of that truncating division and so each dummy unit which has that pre-cached facing angle is indexed to that queue and if the queue is empty I simply create a new unit. It works flawlessly. A higher ANG_N increases realism but requires more dummy units to fill each angle.

    Update 1.2.1.0 - now has optional support for UnitIndexer, keeps all dummy units paused to consume fewer resources.
     
  9. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    uh, you might want to use a heap for deallocation so that you always make the unit counts in each queue even. I'm actually doing some pretty cool stuff with heaps and so on to ensure that you always get a unit with a good angle ; ).
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Currently I don't use a heap, for simplicity. If it were up to me I would reject your resource as soon as you write it, too.

    I am tired of your uncompromising "never going to use someone else's library" nonsense. For what? You didn't even test or understand how my resource worked before "assuming" it doesn't work? Your choices can sometimes suck, man.

    There has to be some better investment for you than to try to "show everyone up" all the time. Your resource re-writes are not something I am going to entertain for much longer.

    I agree with your previous statement that the approve/reject system should be replaced by something more like Digg.com's approach, where things can get up-voted or buried. That way you can see how many people honestly don't want to see most of the stuff we publish let alone use it.
     
  11. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Ahem, you don't do balancing on the angles at all...


    Also, that Projectile system I wrote is getting a rewrite. All projectile systems including it are written wrong. I came up with a good design for projectiles earlier today.

    edit
    you also don't do custom indexing of dummy units and you don't have a get dummy unit index function


    The heap load balancing is very simple and it allows you to always deallocate to an angle with the least amount of units in it. When loading up a new unit, if the angle with the smallest amount of units has much less than the angle with the largest amount of units, some of the units from the large set are moved to the small set, thus keeping balance maintained and allowing you to never have to create units if you preload enough units. Never having to create new units saves on allocation speed. Btw, heaps have little overhead.

    edit
    figured out a better way to do it than a heap ;D
     
    Last edited: Mar 29, 2012
  12. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    If you know there will be a lot of missiles used throughout most of the game, you can increase ANG_STORAGE_MAX to an overstocked value. This would accomplish the same thing.

    I can implement a heap if it keeps you from trying to re-write this resource, and I have been meaning to do something like that for a while but I have not had time to think it through.

    The dummy indexing could be useful. But I think this + particle would do that decently, don't you think?

    Anitarf also wrote an xedummy implementation after he saw MissileRecycler which was better for him and does balance the dummy units across all facing angles, however it is o(n*2) or something as he uses recursive function calls.
     
  13. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Bribe, I actually came up with a great way to do the recycling super fast because I realized a behavior.

    When I'm done with the lib, you can take what you want or w/e. Because you're always adding or removing one, you are only moving the thing 1 position. This means that you can use a linked list (as it's forwards or backwards).

    For load balancing, you're moving many positions, but that's np as it's rare =).


    I've been working on this lib for like 4 hours now =o.
     
  14. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Technically, it's still a "flavor" to balance the dummy units. If you don't balance, the system still works fine.

    Here's what it does: when you recycle a dummy, the system first finds what angle the dummy is already facing. If that queue has room for the dummy unit, it puts it there. If not, then it searches to find the first available slot. If there are no available slots, the "recycle" variable will point to 0 and the dummy unit is removed, skipping the need to iterate over each queue head only to find out there is no hospitable queue.

    Balancing the angles, if it can be done with o(1) or o(2) complexity, will only save you a handful of easy peasy loops anyway. This is the loop:

    Code (vJASS):

                    set i = ANG_N
                    loop
                        set i = i - 1
                        exitwhen stackN[i] < ANG_STORAGE_MAX
                    endloop
     


    Since ANG_N should never be higher than 16, you get an average of 8 iterations within the loop (if the dummy's default queue is full or if all the queues are not full). It's not the best but it's a planetary trajectory from the worst.
     
  15. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Bribe, take a look at this =)

    Code (vJASS):

        private struct List extends array
            integer count
            private thistype next
            private thistype prev
           
            static method operator first takes nothing returns thistype
                return thistype(0).next
            endmethod
            static method operator last takes nothing returns thistype
                return thistype(0).prev
            endmethod
            method forward takes nothing returns nothing
                local thistype node = next
                if (count > node.count) then
                    set next = node.next
                    set next.prev = this
                    set node.next = this
                    set prev = node
                    set thistype(0).next = node
                    set node.prev = 0
                endif
            endmethod
            method backward takes nothing returns nothing
                local thistype node = prev
                if (count < node.count) then
                    set prev = node.prev
                    set prev.next = this
                    set node.prev = this
                    set next = node
                    set thistype(0).prev = node
                    set node.next = 0
                endif
            endmethod
            method backwardMany takes nothing returns nothing
                local thistype node = prev
                loop
                    exitwhen count_p < node.count
                    set node = node.prev
                endloop
                set prev.next = 0
                set thistype(0).prev = prev
                set next = node
                set prev = node.prev
                set node.prev = this
                set prev.next = this
            endmethod
            static method add takes thistype this returns nothing
                set prev = thistype(0).prev
                set thistype(0).prev.next = this
                set thistype(0).prev = this
            endmethod
        endstruct
     


    That is the list of list angles. The add just adds a new angle to the list. It is not the dummy units ;o.

    Because count pretty much always changes by 1, the node will either go forward or backward. The sort is O(1) then. The only time that the count changes by more than one is when you are moving units from one node to another node because the difference between the two nodes is too great ^)^. This makes sure that all lists have around the same number of units in them ;p.
     
  16. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Honestly I was thinking of a different approach that would only need 1 or 2 methods. Basically a stack that stores the precise order of vacant dummy slots, so if I take two from 45 and one from 90, the stack would be: [0]=45, [1]=45, [2]=90. That way there is no need to find vacant slots but it is not exactly "balanced" though balancing is not always useful (for example if you are taking more dummies from one angle than another angle). This way you NEVER need a loop :D
     
  17. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Actually, I just came up with a method that keeps it all balanced without needing a loop =P.
     
  18. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Here is my work in progress (untested, but looking good):

    Code (vJASS):
    library MissileRecycler initializer Init requires optional UnitIndexer /*

        MissileRecycler v 1.2.1.0
        =========================================================================
        Credits:
        -------------------------------------------------------------------------
        Written by Bribe
        Vexorian, Anitarf and iNfraNe for the dummy.mdx model file
        Nestharus for the Queue data structure and for finding that paused units
            consume very few CPU resources.

        =========================================================================
        Requirements:
        -------------------------------------------------------------------------
        -   Updated JassHelper by Cohadar

        =========================================================================
        Introduction:
        -------------------------------------------------------------------------
        Recycling dummy units is important because the CreateUnit call is one of,
        if not the, most processor-intensive native in the entire game. Creating
        just a couple dozen dummy units in a single thread causes a visible frame
        glitch for that instant. The overhead is even higher if you are using a
        Unit Indexing library in the map which causes some extra evaluations per
        new unit.

        There are also reports of removed units leaving a little trail of RAM
        surplus in their wake. I have not been able to reproduce this so I don't
        know if this is still a factor in 1.26.

        I was motivated to create this system because removed units might be un-
        safe in very large numbers and CreateUnit is a very heavy process.

        The thing that makes this system different than others is the fact that
        it considers the facing angle of the dummies being recycled, which I have
        never seen another system even attempt. Considering the facing angle is
        important because it takes almost 1 second for the unit to turn around,
        which looks especially weird if you are creating a unit-trail or if you
        are shooting arrow-shaped objects as projectiles. For fireball effects or
        effects that generally don't depend on facing angle, this system would be
        a bit wasteful.

        Worst case scenario, it will take 0.09 seconds for the projectile to turn
        to the angle you need. This is 1/8 of the normal worst case scenario if
        you weren't recycling dummies considering facing. On average, it takes
        roughly 0.045 seconds to turn to the angle you need (which is not even
        noticable). However, I have made this completely configurable and you are
        able to change the values to whatever needs you have.

        =========================================================================
        Calibration Guide:
        -------------------------------------------------------------------------
        The thing that surprised me the most about this system was, no matter how
        complex it turned out, it became very configurable. So I should let you
        know what the constants do so you know if/how much you want to modify.

        constant integer ANG_N = 16

        -   How many different angles are recognized by the system. You can't do
        360 different angles because then you're going to have thousands of dummy
        units stored and that's ridiculous, the game lags enough at 1000 units.
        Increasing ANG_N increases realism but decreases the chance that a dummy
        unit will be available to be recycled. I don't recommend changing this be-
        cause at 16 resolution you will not be able to see the dummy unit turn
        (a higher ANG_N would be redundant, for example).

        constant integer ANG_STORAGE_MAX = 12

        -   How many dummy units are stored per angle. This limit is important
        because you might have a spike at one point in the game where many units
        are created, which could lead to many of those dummy units never being
        used again.
            In general, I advise that the factor of ANG_N x ANG_STORAGE_MAX does
        not exceed 200. More than that is too much in my opinion.
            Preloads ANG_N x ANG_STORAGE_MAX dummy units. Preloading dummies is
        useful as it dumps a lot of CreateUnit calls in initialization where you
        won't see a frame glitch.

        =========================================================================
        API Guide:
        -------------------------------------------------------------------------
        You obviously need some functions so you can get a recycled dummy unit or
        recycle it. Therefore I provide these:

        function GetRecycledMissile
            takes real x, real y, real z, real facing
                returns unit

            Returns a new dummy unit that acts as a projectile missile. The args
            are simply the last three arguments you'd use for a CreateUnit call,
            with the addition of a z parameter to represent the flying height -
            it isn't the absolute z but relative to the ground because it uses
            SetUnitFlyHeight on that value directly.

        function RecycleMissile
            takes unit u
                returns nothing

            When you are done with that dummy unit, recycle it via this function.
            This function is pretty intelligent and resets that unit's animation
            and its facing angle so you don't have to.
    */

        //=======================================================================
        // Save the map, then delete the exclaimation mark in the following line.
        // Make sure that you don't have an object in your map with the rawcode
        // 'dumi' and also configure the model path (war3mapImported\dummy.mdl)
        // to the dummy.mdx model created by Vexorian.
        //! external ObjectMerger w3u ewsp dumi unam "Missile Dummy" ubui "" uhom 1 ucol 0.01 umvt "None" umvr 1.00 utar "" uspa "" umdl "war3mapImported\dummy.mdl" umxr 0.00 umxp 0.00 ushr 0 uerd 0.00 udtm 0.00 ucbs 0.00 uble 0.00 uabi "Aloc,Amrf"

        //Thanks to Vexorian that Optimizer 5.0 no longer kills natives
        native UnitAlive takes unit id returns boolean

        globals
            //-------------------------------------------------------------------
            // You must configure the dummy unit with the one created from the
            // ObjectMerger statement above.
            //
            private constant integer DUMMY_ID = 'dumi'      //The rawcode of the dummy unit.
            private          player  OWNER    = Player(15)  //The owner of the dummy unit.

            private constant integer ANG_N = 16             //# of indexed angles. Higher value increases realism but decreases recycle frequency.
            private constant integer ANG_STORAGE_MAX = 12   //Max dummies per indexed angle. I recommend lowering this if you increase ANG_N.

            private constant real DEATH_TIME = 2. //Allow the special effect on
            //the unit to complete its "death" animation in this timeframe. Must
            //be higher than 0.74 seconds to allow the unit time to turn. This
            //number should not be lower than the maximum death-animation time of
            //your missile-units' effect attachments, just to be safe.
        endglobals

        globals
            private constant integer ANG_VAL = 360 / ANG_N //Generate angle value from ANG_N.
            private constant integer ANG_MID = ANG_VAL / 2 //The middle value of angle value.

            //Misc vars
            private unit array stack       //Recycled dummy units.
            private real array timeStamp   //Prevents early recycling of units.
            private integer array queueNext
            private integer array queueLast
            private integer recycle = 0
            private timer gameTime  = CreateTimer() //Used for visual continuity.
            private group protect   = CreateGroup() //Used to prevent double frees.
            private integer array queueStack
            private integer queueStackN = 0 //Used to avoid searching the queues.
        endglobals

        static if DEBUG_MODE then
            private function Print takes string s returns nothing
                //Un-comment this next line if you want to know how the system works:
                //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 999, "[MissileRecycler] " + s)
            endfunction
        endif

        //=======================================================================
        // Get a recycled dummy missile unit. If there are no recycled dummies
        // that are already facing the angle you need, it creates a new dummy for
        // you.
        //
        function GetRecycledMissile takes real x, real y, real z, real facing returns unit
            local integer i = ModuloInteger(R2I(facing), 360) / ANG_VAL
            local integer this = queueNext[i]
            local unit u
            if this != 0 and TimerGetElapsed(gameTime) >= timeStamp[this] then
                //Dequeue this
                set queueNext[i] = queueNext[this]
                if queueNext[i] == 0 then
                    set queueLast[i] = i
                endif
                //Recycle this index
                set queueLast[this] = recycle
                set recycle = this
                //Add queue index to available stack
                set queueStack[queueStackN] = i
                set queueStackN = queueStackN + 1
                //Old unit will return as new
                set u = stack[this]
                call SetUnitFacing(u, facing)
                call GroupRemoveUnit(protect, u)
                debug call Print("Recycling")
            else
                debug call Print("Creating new")
                static if LIBRARY_UnitIndexer then
                    set UnitIndexer.enabled = false
                endif
                set u = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
                static if LIBRARY_UnitIndexer then
                    set UnitIndexer.enabled = true
                endif
                call PauseUnit(u, true)
            endif
            call SetUnitX(u, x)
            call SetUnitY(u, y)
            call SetUnitFlyHeight(u, z, 0)
            set bj_lastCreatedUnit = u
            set u = null
            return bj_lastCreatedUnit
        endfunction

        //=======================================================================
        // You should recycle the dummy missile unit when its job is done.
        //
        function RecycleMissile takes unit u returns nothing
            local integer i
            local integer this
            if GetUnitTypeId(u) == DUMMY_ID and UnitAlive(u) and not IsUnitInGroup(u, protect) then
                if queueStackN == 0 then
                    debug call Print("Stack is full - removing surplus unit")
                    call RemoveUnit(u)
                    return
                endif
                //Get recycled dummy node instance.
                set this = recycle
                set recycle = queueLast[this]
                //Get the last vacant queue index.
                set queueStackN = queueStackN - 1
                set i = queueStack[queueStackN]
                //Enqueue this
                set queueNext[queueLast[i]] = this
                set queueLast[i] = this
                set queueNext[this] = 0
                //Allow a time barrier for the effect to destroy/turn to complete.
                set timeStamp[this] = TimerGetElapsed(gameTime) + DEATH_TIME
                set stack[this] = u
                //Prevent double-free of this unit.
                call GroupAddUnit(protect, u)
                //Reset the dummy's properties.
                call SetUnitFacing(u, i * ANG_VAL + ANG_MID)
                call SetUnitVertexColor(u, 255, 255, 255, 255)
                call SetUnitAnimationByIndex(u, 90)
                call SetUnitScale(u, 1, 0, 0)
                //call PauseUnit(u, false) -- you can disable "resets" that you don't need to worry about.
            debug else
                debug call BJDebugMsg("[MissileRecycler] Error: Attempt to recycle invalid unit.")
            endif
        endfunction

        //=======================================================================
        // Map the dummy units to their facing angles (map below is if ANG_N is
        // 4 and ANG_STORAGE_MAX is 3).
        //
        // angle[0] (0)   -  [4] [5] [6]
        // angle[1] (90)  -  [7] [8] [9]
        // angle[2] (180) - [10][11][12]
        // angle[3] (270) - [13][14][15]
        //
        private function Init takes nothing returns nothing
            local integer end
            local integer i = ANG_N
            local integer n = i
            local integer angle
            local real x = GetRectMaxX(bj_mapInitialPlayableArea)
            local real y = GetRectMaxY(bj_mapInitialPlayableArea)
            static if LIBRARY_UnitIndexer then
                set UnitIndexer.enabled = false
            endif
            loop
                set i = i - 1
                set queueNext[i] = n
                set angle = i * ANG_VAL + ANG_MID
                set end = n + ANG_STORAGE_MAX
                set queueLast[i] = end - 1
                loop
                    set queueNext[n] = n + 1
                    set stack[n] = CreateUnit(OWNER, DUMMY_ID, x, y, angle)
                    call PauseUnit(stack[n], true)
                    set n = n + 1
                    exitwhen n == end
                endloop
                set queueNext[n - 1] = 0
                exitwhen i == 0
            endloop
            static if LIBRARY_UnitIndexer then
                set UnitIndexer.enabled = true
            endif
            call TimerStart(gameTime, 1000000., false, null)
        endfunction

    endlibrary
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    see, you're using queues ; p

    rather than queues, you should be using arrays. It'd be must faster ; ).


    I'm almost done with my take on it =o.
     
  20. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,773
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Does your structure use a FIFO-esqueue approach? I used to use arrays for it but I changed it to FIFO queues because of the timestamps, so there is some time for the dummy unit(s) to turn and to play their effect's death animation. It takes about 3/4 of a second for the unit to turn 180 degrees, for example, but it takes even longer to finish the death animation so that's why I use a 2 second recycling delay (configurable of course). The FIFO structure just makes it so that the timestamps are not necessary, but they are very good for a failsafe in more extreme circumstances.