wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Resources > - Submit a resource -
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 10-27-2011, 10:25 AM   #1
Bribe
User
 
Bribe's Avatar
 
Join Date: Mar 2010
Posts: 233

Submissions (1)

Bribe will become famous soon enough (30)Bribe will become famous soon enough (30)

Send a message via AIM to Bribe
Default MissileRecycler

Recycles missile dummy units while considering their facing angle.

Warning: this uses an ObjectMerger statement to create a dummy unit with the rawcode 'dumi'. I haven't written a Nestharus-style safe Lua script nor prepared a test map at this stage. That is on my to-do list still.

Collapse JASS:
library MissileRecycler requires TimerUtils /*

    MissileRecycler v 1.1.0.0
    =========================================================================
    Credits:
    -------------------------------------------------------------------------
    Written by Bribe
    Vexorian, Anitarf and iNfraNe for the dummy.mdx model file

    =========================================================================
    Requirements:
    -------------------------------------------------------------------------
    TimerUtils by Vexorian
    -   wc3c.net/showthread.php?t=101322

    =========================================================================
    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 = 8

    -   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 making this any
    lower, though the max I'd recommend would be 16.

    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 100. 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"

    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 = 8              //# 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.
    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.

        //Timer data indexing vars
        private integer indexN = 0     //Used to create indices.
        private integer array recycle  //Used to recycle indices.
        private unit array timedUnit   //Used for the timer stack.

        //Misc vars
        private unit newUnit           //Returning a local unit leaks the handle index.
        private unit array stack       //Used as a 2-D array of dummy units.
        private integer array stackN   //Used to track multiple indices for the 2-D array.
        private code expireCode        //Prevents trigger evaluations or cloned functions.
    endglobals

    //=======================================================================
    // Returns the index for the given angle. If ANG_N is set to 4, it looks
    // like this:
    //
    // - index[0] = 0 degrees
    // - index[1] = 90 degrees
    // - index[2] = 180 degrees
    // - index[3] = 270 degrees
    //
    private function GetAngleIndex takes real angle returns integer
        return R2I(angle) / ANG_VAL
    endfunction

    //=======================================================================
    // 2-D array allows all numbers to be easily user-configurable.
    //
    private function GetArrayIndex takes integer angleIndex, integer dummyIndex returns integer
        return angleIndex * ANG_STORAGE_MAX + dummyIndex
    endfunction
    
    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 = R2I(facing)
        //Atan2 can return negative values even when converted to degrees.
        if i < 0 then
            set i = i + 360
        endif
        set i = i / ANG_VAL
        if stackN[i] > 0 then
            set stackN[i] = stackN[i] - 1
            set newUnit = stack[GetArrayIndex(i, stackN[i])]
            call SetUnitFacing(newUnit, facing)
            debug call Print("Recycling")
        else
            debug call Print("Creating new")
            set newUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
        endif
        call SetUnitX(newUnit, x)
        call SetUnitY(newUnit, y)
        call SetUnitFlyHeight(newUnit, z, 0)
        return newUnit
    endfunction

    //=======================================================================
    // Prepares a unit to be recycled after it completely adjusts its facing
    // angle.
    //
    private function Recycle takes unit u, integer i returns nothing
        local timer t = NewTimer()
        local integer this = recycle[0]
        
        set i = i * ANG_VAL + ANG_MID     //Turn the small integer into a true angle.
        call SetUnitFacing(u, i)          //Set its facing to the new angle.
        
        //Get the smallest angle between the unit's current facing and the
        //angle it is going to face, because that is how the wc3 engine will
        //turn the unit.
        set i = IAbsBJ(i - R2I(GetUnitFacing(u)))
        if i > 180 then
            set i = i / 360 * 360 + 360 - i
        endif

        //Allocate index for timer data.
        if this == 0 then
            set this = indexN + 1
            set indexN = this
        else
            set recycle[0] = recycle[this]
        endif

        set timedUnit[this] = u           //Index the dummy unit to this instance.
        
        //Gives the unit just enough time to complete its turn.
        call SetTimerData(t, this)
        call TimerStart(t, 0.00406 * i, false, expireCode)
        set t = null
    endfunction

    //=======================================================================
    // Recycle dummy unit after it has finished turning. If the stack is full,
    // try again on a different stack. If all stacks are full, use RemoveUnit.
    //
    private function Expire takes nothing returns nothing
        local integer this = GetTimerData(GetExpiredTimer()) //Get the relevant dummy index
        local integer i = GetAngleIndex(GetUnitFacing(timedUnit[this])) //Get the index from the dummy's facing angle.
        call ReleaseTimer(GetExpiredTimer())
        
        //Deallocate index as it's no longer required.
        set recycle[this] = recycle[0]
        set recycle[0] = this

        if stackN[i] == ANG_STORAGE_MAX then
            set i = ANG_N
            loop
                set i = i - 1
                if stackN[i] < ANG_STORAGE_MAX then
                    call Recycle(timedUnit[this], i)
                    return //Recycled the instance, skip remaining actions.
                endif
                exitwhen i == 0
            endloop
            debug call Print("Stack is full - removing surplus unit")
            call RemoveUnit(timedUnit[this])
        else
            set stack[GetArrayIndex(i, stackN[i])] = timedUnit[this]
            set stackN[i] = stackN[i] + 1
        endif
        set timedUnit[this] = null
    endfunction

    //=======================================================================
    // You should recycle the dummy missile unit when its job is done.
    //
    function RecycleMissile takes unit u returns nothing
        if GetUnitTypeId(u) == DUMMY_ID then
            //Reset the dummy's properties
            call SetUnitVertexColor(u, 255, 255, 255, 255)
            call SetUnitAnimationByIndex(u, 90)
            call SetUnitScale(u, 1, 0, 0)
            call Recycle(u, GetAngleIndex(GetUnitFacing(u)))
        debug else
            debug call BJDebugMsg("[MissileRecycler] Error: Attempt to recycle invalid unit-type.")
        endif
    endfunction
    
    //=======================================================================
    // This module helps to initialize the system in highest priority.
    //
    private module M
        private static method onInit takes nothing returns nothing
            local integer i
            local integer j = ANG_N
            local integer angle
            local real x = GetRectMaxX(bj_mapInitialPlayableArea)
            local real y = GetRectMaxY(bj_mapInitialPlayableArea)
            loop
                set j = j - 1
                set i = ANG_STORAGE_MAX
                set stackN[j] = i
                set angle = j * ANG_VAL + ANG_MID
                loop
                    set i = i - 1
                    set stack[GetArrayIndex(j, i)] = CreateUnit(OWNER, DUMMY_ID, x, y, angle)
                    exitwhen i == 0
                endloop
                exitwhen j == 0
            endloop
            set expireCode = function Expire
        endmethod
    endmodule
    private struct S extends array
        implement M
    endstruct

endlibrary

Last edited by Bribe : 10-31-2011 at 05:53 PM.
Bribe is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 10-31-2011, 10:59 AM   #2
Bribe
User
 
Bribe's Avatar
 
Join Date: Mar 2010
Posts: 233

Submissions (1)

Bribe will become famous soon enough (30)Bribe will become famous soon enough (30)

Send a message via AIM to Bribe
Default

Updated pretty heavily since original upload, also changed library requirement.
Bribe is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 06:24 AM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services