Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Snippet] Dummy Reuser

Discussion in 'JASS Resources' started by watermelon_1234, Sep 1, 2010.

  1. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    This snippet is intended to allow different unit-type dummy units to be recycled.

    The documentation is inside the code. A test-map is also attached with a simple spell showing the recycling in action.

    Code (vJASS):
    //==========================================================================================
    // DummyReuser v2.01 by watermelon_1234
    //******************************************************************************************
    // A textmacro for structs designed to reuse/recycle dummy units of any type.
    //
    // This is mostly aimed for reusing dummy units with models that can play multiple animations,
    // allowing a specific animation to be played unlike special effects which can only play
    // birth, standing, or death animations consistently.
    //##########################################################################################
    //
    // Instructions:
    //  To first use this, you must declare some kind of struct. Once you've declared the struct,
    //  just add the following line:
    //
    //  //! runtextmacro DummyReuser("DUMMY_UNIT_TYPEID")
    //
    //  where DUMMY_UNIT_TYPEID is the unit-type id of the dummy.
    //
    // API:
    //  * static method get takes player owner, real x, real y, real facing returns unit *
    //  Returns a dummy unit that you can use for a player at map coordinates x and y.
    //  Note that facing is in degrees like CreateUnit.
    //
    //  * static method release takes unit u returns nothing *
    //  Releases a dummy unit for recycling.
    //
    //##########################################################################################
    //
    // Importing:
    //  1. Copy this trigger.
    //  2. Configure the system to your liking
    //
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //
    // Credits:
    //  - azlier for improvements on my earlier code
    //  - Bribe for more improvements
    //  - PurgeandFire111 for showing me how to make structs efficient, though it no longer applies
    //  - Anachron for CustomAura which gave me an idea to use a textmacro for this
    //
    //==========================================================================================

    library DummyReuser

        globals
            // The owner for the dummy unit when it's in storage
            private constant player     DUMMY_STORAGE_OWNER     = Player(15)
            // Max number of dummies to be stored
            private constant integer    DUMMY_MAX_COUNT         = 18      
        endglobals  
       
    //==========================================================================================
    //                              END OF CONFIGURATION
    //==========================================================================================
       
        // Prepares a dummy unit for use and returns it
        public function Get takes unit u, player owner, real x, real y, real facing returns unit
            call SetUnitOwner(u, owner, true)
            call SetUnitFacing(u, facing)              
            call PauseUnit(u, false)
            call ShowUnit(u, true)  
            if UnitRemoveAbility(u, 'Aloc') then
                call UnitAddAbility(u, 'Aloc')
            endif
            call SetUnitX(u, x)
            call SetUnitY(u, y)
            return u
        endfunction
       
        // Returns true if a dummy unit was stored
        public function Store takes unit u, integer id, integer count returns boolean
            // Safety check to prevent silly mistakes
            if not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) == id and count < DUMMY_MAX_COUNT then
                call ShowUnit(u, false) // Using ShowUnit as that's the most consistent way of hiding any dummy unit
                call SetUnitOwner(u, DUMMY_STORAGE_OWNER, false)          
                call PauseUnit(u, true)
                return true  
            endif
            debug call BJDebugMsg("DummyReuser: Removed dummy unit either due to max limit or wrong dummy unit")
            call RemoveUnit(u)
            return false
        endfunction
    endlibrary

    //! textmacro DummyReuser takes DUMMY_UNIT_TYPEID
        private static unit array dr_stack
        private static integer dr_count = 0    
       
        static method get takes player owner, real x, real y, real facing returns unit
            if dr_count == 0 then
                return CreateUnit(owner, $DUMMY_UNIT_TYPEID$, x, y, facing)
            endif        
            set dr_count = dr_count - 1   // The stack is zero-based
            return DummyReuser_Get(dr_stack[dr_count], owner, x, y, facing)
        endmethod

        static method release takes unit u returns nothing
            if DummyReuser_Store(u, $DUMMY_UNIT_TYPEID$, dr_count) then  
                set dr_stack[dr_count] = u
                set dr_count = dr_count + 1
            endif
        endmethod
    //! endtextmacro
    Changelog
    v2.01

    • Made the textmacro produce less code
    • No longer stores dummy in a different place
    • Globals are no longer public
    • Added constant that can specify a max number of dummy units that can be recycled
    • Supports dummy units with Locust (again)

    v2.00

    • New way of getting and recycling dummy units.
    • No longer supports dummy units that don't have Locust.
    • Updated test map to use xemissile instead of xeprojectile.
    • No longer uses UnitAlive.

    v1.01b


    • Used a more efficient way of allocating and deallocating the struct.

    v1.01a


    • No longer uses onDestroy.
    • Macro uses static if for locust.

    v1.01

    • Changed system to template for structs recycling dummy units.

    v1.00

    • Released.


    Additional Credits:

    • Vexorian for xe system, TimerUtils, and BoundSentinel
    • EotA by DarnYak whose spell inspired the test-map one.
     

    Attached Files:

    Last edited: Jun 13, 2012
  2. azlier

    azlier

    Joined:
    Oct 3, 2008
    Messages:
    353
    Resources:
    4
    JASS:
    4
    Resources:
    4
    This just seems... overcomplicated.

    And why would you EVER need more than one dummy unit type?

    I think something like this might work better.

    Code (vJASS):
    library DummyRecycler

    globals
        private constant integer DUMMY_TYPE = 'hfoo'
        //The unit type that dummies should utilize.
       
        private          player HOLDER_PLAYER = Player(15)
        //The player that holds dummies when they are not in use.
    endglobals

    struct RecycledDummy

        unit unit
       
        static method create takes player p, real x, real y, real facing returns thistype
            local thistype this = thistype.allocate()
            if unit == null then
                set unit = CreateUnit(p, DUMMY_TYPE, x, y, facing)
            else
                call ShowUnit(unit, true)
                call PauseUnit(unit, false)
                call SetUnitOwner(unit, p, true)
                call SetUnitX(unit, x)
                call SetUnitY(unit, y)
                call SetUnitFacing(unit, facing)
            endif
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call ShowUnit(unit, true)
            call PauseUnit(unit, true)
            call SetUnitOwner(unit, HOLDER_PLAYER, false)
        endmethod
       
    endstruct

    endlibrary


    Here's an example of use.

    Code (vJASS):
    function Icthys takes nothing returns nothing
        local RecycledDummy d = RecycledDummy.create(Player(0), 0, 0, 0)
        call IssueTargetOrder(d.unit, "blah", GetTriggerWidget()) //Or something...
        //...
        //And later:
        call d.destroy()
    endfunction
     
  3. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    The main reason I made this was when I wanted to have "special effects" play a certain animation; the only reason stopping me from using special effects is that they can't play animations. An example of this is the spell I provided in the test-map.

    Your idea looks much better than mine though.
    Maybe I'll try to make it so reuser can be extended to provide the multiple unit type support? Or something like that.
     
  4. azlier

    azlier

    Joined:
    Oct 3, 2008
    Messages:
    353
    Resources:
    4
    JASS:
    4
    Resources:
    4
    That's a scenario I've never even heard of. However, if it's that important you could always try to add different unit types to mine, however complicated that may be...
     
  5. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    I don't really see the point in this. You would use a hybrid unit recycler and damage detection system (the former making the latter incredibly easy) for any spawn based maps, and any projectile systems or spells should be using xe(fx), which recycles its units anyway.
     
  6. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    Well if you guys don't seem to find any use for this then feel free to graveyard it.
    I just wish I had these opinions presented in this thread before I put this here.

    Edit:
    Just before I completely give up on this, how about this?

    Code (vJASS):
    library NewDummyReuser
        globals
            public constant player DUMMY_STORAGE_OWNER     = Player(15)
            // Coordinates for the storage of all dummy units
            public constant real   DUMMY_STORAGE_X         = 0
            public constant real   DUMMY_STORAGE_Y         = 0  
        endglobals    
    endlibrary

    //! textmacro NewDummyReuser
        unit dummy

        static method create takes player owner, real x, real y, real facing returns thistype
            local thistype this = thistype.allocate()
            if .dummy == null then
                set .dummy = CreateUnit(owner,DUMMY_UNIT_TYPEID,x,y,facing)
            else
                call SetUnitOwner(.dummy,owner,true)
                call SetUnitFacing(.dummy,facing)
                call SetUnitX(.dummy,x)
                call SetUnitY(.dummy,y)
                call PauseUnit(.dummy,false)
                call ShowUnit(.dummy,true)
            endif
            return this
        endmethod

        method onDestroy takes nothing returns nothing
            if GetWidgetLife(.dummy) > 0.405 then
                call SetUnitOwner(.dummy,NewDummyReuser_DUMMY_STORAGE_OWNER,false)
                call SetUnitX(.dummy,NewDummyReuser_DUMMY_STORAGE_X)
                call SetUnitY(.dummy,NewDummyReuser_DUMMY_STORAGE_Y)
                call PauseUnit(.dummy,true)
            else
                set .dummy = null
            endif
        endmethod
    //! endtextmacro
    To use it, you need to create a struct and then just have
    //! runtextmacro NewDummyReuser()
    inside the struct.
    In addition, you'll need to have a global constant integer DUMMY_UNIT_TYPEID to define what unit type you're using.
    The script would basically be a template for a struct that can recycle dummy units.
    It's not really mine anymore though since it's basically a copy of azlier's struct.
     
    Last edited: Sep 2, 2010
  7. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    if GetWidgetLife(.dummy) > 0.405 then
    is redundant, just use 0.
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,374
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    UnitAlive
    far out-performs GetWidgetLife / IsUnitType.

    busterkomo, when using GetWidgetLife it should be checked against 0.405; once that value is reached or overshot, the unit is registered as 'dead' by the game.

    The problem with recycling dummies is that the unit-facing is not instant. I should write a script that collects dummies and stores them according to various facing-angles. The more dummies are in the stack, the more acute the angles would get.
     
  9. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    GetWidgetLife will never return > 0 if the unit is dead. Once it's life reaches 0.405 it is flagged as dead and its health is set to 0.
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,374
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    While that makes sense, SetWidgetLife() on a dead unit can, and does, change its 'health' (while not reviving it).
     
  11. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    If someone is careless enough to call SetWidgetLife() on a dead unit then they deserve to experience problems.
     
  12. azlier

    azlier

    Joined:
    Oct 3, 2008
    Messages:
    353
    Resources:
    4
    JASS:
    4
    Resources:
    4
    Some abilities can also mess with dead units' health. I think one is Stone Form. UnitAlive is the best method though.
     
  13. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    Seeing as how azlier did not object to this idea in his last post, I updated the first post with the system now serving as a template for structs to recycle units.

    I didn't use UnitAlive before in my last post because I just wanted to present the idea.
     
  14. azlier

    azlier

    Joined:
    Oct 3, 2008
    Messages:
    353
    Resources:
    4
    JASS:
    4
    Resources:
    4
    I guess this looks good now, even if I don't really comprehend the uses of having multiple recyclable unit types. Approved.
     
  15. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    So this is where it went watermelon! Good job!

    ps. UnitAlive sucks :p Bribe, don't discuss this with me haha :D
     
  16. D4RK_G4ND4LF

    D4RK_G4ND4LF

    Joined:
    Feb 4, 2009
    Messages:
    1,196
    Resources:
    20
    Models:
    3
    Spells:
    15
    Tutorials:
    2
    Resources:
    20
    having multiple dummy types is really important
    you can't recycle special effects but they leak a lot of memory if destroyed (at least it was like that when gexxo @ inwarcraft.de made a few tests years ago)
    so if you want to have different special effects and don't want to leak memory having different dummy types is the only way
    also it is faster to just hide/show dummys instead of creating and destroying special effects
    i'd base it of special effect string instead of unit id though
    (other things are dummys with different movement types, flying units fuck up on cliff edges etc.)

    and if you really want to destroy special effects you will have to add a delay before putting the dummy back on the stack because special effects take a few seconds to be destroyed
    so if you allocate a new dummy it might still have the old dying special effect to it
    another case would be the special effect death animation not shown properly

    the facing can be solved with SetUnitLookAt and an appropiate dummy
     
  17. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,374
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    onDestroy is wicked and makes it so you can't run this in an array struct. The textmacro's boolean should be placed in a static-if. The macro shouldn't request .allocate(), either, it should have its own allocator.
     
  18. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    So just change onDestroy to destroy and deallocate the struct there?

    I'm not really sure what you want here; do you want me to make a new static method that's not create?
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,374
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I think more along the lines of...

    static method releaseDummy takes unit whichUnit returns nothing
    // stuff
    endmethod

    static method newDummy takes <args> returns unit
    // stuff
    endmethod
     
  20. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    I'm sorry but I'm still failing to understand what you want me to do. :\

    The current recycling method depends on struct instances being allocated.
    Do you want me to try doing this a different way?

    Also I tried updating the code with the other suggestions.