1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Fill your cup and take your pick among the maps best suited for this year's Hive Cup. The 6th Melee Mapping Contest Poll is up!
    Dismiss Notice
  3. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  4. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Proper Bone Decay Suspension System

Submitted by J2Krauser
This bundle is marked as pending. It has not been reviewed by a staff member yet.
Introduction

Proper Bone Decay Suspension (PBDS) allows users to properly suspend/pause the bone decay of units, and also resume it at will. Additionally, the script itself contains every piece of information required to understand why it's necessary, how it works, what it contains, how to implement, and use it. However, I'll also add any relevant information in a less verbose manner here as well.

How to Implement
  1. If one downloads the test map, open its trigger editor, and copy the folder named "PBDS" into their map.
    • Additionally, the script can be copy and pasted without touching the test map.
  2. Set the two following variables:
    • FLESH_DECAY should be what your map's Decay Time (sec) - Flesh gameplay constant is.
    • BONE_DECAY should be what your map's Decay Time (sec) - Bones gameplay constant is.
      • By default these constants are 2.00 and 88.00 respectively. They can be accessed under Advanced → Gameplay Constants...
  3. You're done.
    1. Furthermore, in the Lua version, these variables are called Setup.FleshDecay and Setup.BoneDecay respectively.
    2. Also, if one wishes to copy just the Lua script, PBDSInit() has to be called manually on map initialization. Copying the folder from the test map automates this process.
How to Use

Call the following function on units currently in the bone decay phase to suspend/resume it:
Code (vJASS):
function PBDSUnitSuspendDecay takes unit u, boolean suspend returns nothing
Code (Lua):
function PBDSUnitSuspendDecay(u, suspend)
Suspension example:
Code (vJASS):
call PBDSUnitSuspendDecay(myUnit, true)
Code (Lua):
PBDSUnitSuspendDecay(myUnit, true)
With the second passed argument being true, the bone decay of myUnit is suspended.

Resume example:
Code (vJASS):
call PBDSUnitSuspendDecay(myUnit, false)
Code (Lua):
PBDSUnitSuspendDecay(myUnit, false)
With the second passed argument being false, the bone decay of myUnit is resumed.

Supported Patch

In the library itself I recommend using at least the version I saved it on, but 1.31 should just about be fine. Anything before that would most likely not work, as I'm using new natives. I didn't test the system on older patches, so my personal recommendation stays 1.32.8.


  • As mentioned before, copying this into one's map is enough without touching the test map.
    Note: The script here has FLESH_DECAY and BONE_DECAY set to their default values, while the test map has a custom BONE_DECAY value. Keep this in mind.
    Code (vJASS):

    library ProperBoneDecaySuspension initializer PBDSInit /* v1.0.0
    ********************************************************************************
    *                                                                              *
    *    This system allows users to properly suspend the bone decay of units,     *
    *    and resume it at will. More specific info can be found further down.      *
    *                                                                              *
    *    Author:            J2Krauser                                              *
    *                                                                              *
    *    v1.0.0:            06.09.2020                                             *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] REQUIREMENTS                                                           *
    *                                                                              *
    *        1. As I scripted (and saved) it using the latest version of           *
    *        Warcraft, and editor at the time (1.32.8 - 1.32i), I recommend        *
    *        using the system on at least these. However, it should work on        *
    *        older patches just as well as long as one doesn't go too far back,    *
    *        therefore this is more of an advice than a real requirement.          *
    *        2. Nothing. No unit indexer dependency, or anything of the sort.      *
    *        I found this important to point out.                                  *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] HOW TO IMPLEMENT                                                       *
    *                                                                              *
    *        1. Copy the folder called "PBDS".                                     *
    *        2. Paste it into your map.                                            *
    *        3. Read the HOW TO USE section.                                       *
    *        4. Read the VARIABLES section.                                        *
    *        5. Read the FUNCTIONS section.                                        *
    *        6. Read the NOTES section.                                            *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] HOW TO USE                                                             *
    *                                                                              *
    *        After having copied the system into your map, perform the quick       *
    *        setup below. Only two things are necessary if you have modified       *
    *        the gameplays constants in your map for flesh and/or bone decay:      *
    *        1. Set FLESH_DECAY to "Decay Time (sec) - Flesh" in your map.         *
    *        2. Set BONE_DECAY to "Decay Time (sec) - Bones" in your map.          *
    *                                                                              *
    *        Additionally, you could fiddle with PERIOD, but I recommend not       *
    *        to touch it. At this point everything should just work, the system    *
    *        should just do its thing.                                             *
    *                                                                              *
    *        There is only one public function you have to use called              *
    *        PBDSUnitSuspendDecay. It's fully explained further down, but the      *
    *        gist of it is:                                                        *
    *        If you want to suspend a unit's bone decay:                           *
    *            call PBDSUnitSuspendDecay(unit, true)                             *
    *        If you want to resume it:                                             *
    *            call PBDSUnitSuspendDecay(unit, false)                            *
    *                                                                              *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] SETUP                                                                  *
    *                                                                              */

        globals
            private constant real PERIOD            = 0.5

            private constant real FLESH_DECAY       = 2.00
            private constant real BONE_DECAY        = 88.00
        endglobals

    /*******************************************************************************
    *                                                                              *
    *    [] VARIABLES                                                              *
    *                                                                              *
    *        REAL                                                                  *
    *        1. PERIOD: How often the the periodic trigger runs which does the     *
    *        heavy lifting of this system. Can be changed if someone really        *
    *        wants to, but I recommend it stays at its default 0.5 set by me.      *
    *                                                                              *
    *        REAL                                                                  *
    *        2. FLESH_DECAY: In the editor: Advanced -> Gameplay Constants...      *
    *        Here, find "Decay Time (sec) - Flesh", and this variable should       *
    *        hold the same value one finds there.                                  *
    *                                                                              *
    *        REAL                                                                  *
    *        3. BONE_DECAY: In the editor: Advanced -> Gameplay Constants...       *
    *        Here, find "Decay Time (sec) - Bones", and this variable should       *
    *        hold the same value one finds there.                                  *
    *                                                                              *
    *                                   <<[**]>>                                   *
    *        Starting here, the rest are set up by my script, and do not have      *
    *        to be touched manually.                                               *
    *                                   <<[**]>>                                   *
    *                                                                              *
    *                                                                              *
    *        HASHTABLE                                                             *
    *        4. pbdsDecayingUnitsHash: The main data structure the system uses     *
    *        to function.                                                          *
    *        It's structured in the following way:                                 *
    *            [KEY]: UNIT HANDLE                     INTEGER                    *
    *            [0]:   BONE DECAY                      REAL                       *
    *            [1]:   SUSPENDED                       BOOLEAN                    *
    *                                                                              *
    *        In other words, the key is a unit.                                    *
    *        0 holds its current bone decay timer, counting down. Basically the    *
    *        amount of seconds left till it's removed from the game, and           *
    *        disappears. 1 is a boolean that's true if the bone decay is           *
    *        suspended at the moment, which means the bone decay timer at 0 is     *
    *        not progressing; it's false if the unit is decaying, and will         *
    *        eventually get removed.                                               *
    *                                                                              *
    *        UNIT GROUP                                                            *
    *        5. pbdsDecayingUnitsUG: Contains every unit that's in the             *
    *        hashtable. The bottom line is, these are units which are in the       *
    *        process of bone decay. They are eligible to be suspended.             *
    *                                                                              *
    *        TRIGGER                                                               *
    *        6. trgUnitDeath: The trigger that fires for every unit death. If      *
    *        the unit is decayable, it'll be eligible for bone decay               *
    *        suspension, therefore it gets stored in pbdsDecayingUnitsHash and     *
    *        pbdsDecayingUnitsUG.                                                  *
    *                                                                              *
    *        TRIGGER                                                               *
    *        7. trgPeriodic: Periodically executed as long as the unit group is    *
    *        not empty. Counts down the bone decay timers, removes decayed         *
    *        units.                                                                *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] FUNCTIONS                                                              *
    *                                                                              *
    *        function PBDSUnitSuspendDecay                                         *
    *        takes unit u, boolean suspend                                         *
    *        returns nothing                                                       *
    *            u:       The unit to suspend or resume bone decay for.            *
    *            suspend: If true, suspends the decay. If false, resumes it.       *
    *            HOW IT WORKS:                                                     *
    *            1. Check if the unit is in pbdsDecayingUnitsUG. If not, do        *
    *            nothing.                                                          *
    *            2. If "suspend" is true:                                          *
    *                1. If the boolean at index 1 for this unit in                 *
    *                pbdsDecayingUnitsHash is false, set it to true, then call     *
    *                native UnitSuspendDecay.                                      *
    *                2. Call native SetUnitTimeScale. This native is used to       *
    *                to set the unit's animation speed to 0% (0.00) which          *
    *                prevents its corpse from disappearing over time.              *
    *            3. If "suspend" is false:                                         *
    *                1. If the boolean at index 1 for this unit in                 *
    *                pbdsDecayingUnitsHash is true, set it to false.               *
    *                2. native UnitSuspendDecay isn't necessary to be called       *
    *                again here with false to resume the decay, as the system      *
    *                will just remove the unit anyway once it decayed.             *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] NOTES                                                                  *
    *                                                                              *
    *        1. I tried using SetUnitTimeScale to speed up the bone decay          *
    *        animation instead of manually removing the corpse once the timer      *
    *        is up. This doesn't work. I don't know the exact functionality of     *
    *        the game in the background, but it seems to me as if it's fully       *
    *        hardcoded once the decay is resumed how long it will take for the     *
    *        corpse to disappear, and changing animation speed has no effect on    *
    *        it whatsoever. Why is this important to mention? Because corpses      *
    *        will immediately disappear instead of quickly fading away. This is    *
    *        normal with this system.                                              *
    *                                                                              *
    *        2. Why is the system necessary? Wouldn't it work to just call         *
    *        the natives UnitSuspendDecay, and SetUnitTimeScale appropriately,     *
    *        then call it a day? No, it would not work. Let me explain how         *
    *        UnitSuspendDecay works to show why this system is needed.             *
    *        Basically, once the decay is suspended, the corpse will still         *
    *        disappear over time. This is why SetUnitTimeScale to 0.00 is used     *
    *        so that the animation is halted. Bone decay animations also have      *
    *        a fixed length much like every other, therefore just suspending       *
    *        the decay would keep the unit around (I know this is a bit hard       *
    *        to grasp for people who aren't that into these things.), but its      *
    *        corpse would be gone. You could still raise the unit, or resurrect    *
    *        it, the corpse would just not be there. Halting the animation         *
    *        using SetUnitTimeScale is what keeps the corpse around.               *
    *                                                                              *
    *        So? What's the issue with just calling UnitSuspendDecay again with    *
    *        false, then SetUnitTimeScale with 1.00? Well, this is where the       *
    *        way it works comes into the picture. You see, if you do this, it      *
    *        does NOT resume anything. You might think that would be the           *
    *        expected behaviour, but it actually starts the unit's bone decay      *
    *        all over again. You notice this once you have a few units which       *
    *        didn't all die exactly at the same moment, then you suspend their     *
    *        bone decay just to resume it again. Their corpses will all fade       *
    *        away at the exact same moment, exactly after the amount of seconds    *
    *        you have "Decay Time (sec) - Bones" set to in gameplay constants.     *
    *                                                                              *
    *        That's why this system is needed if you want to actually resume       *
    *        bone decay instead of restarting it at its full duration.             *
    *                                                                              *
    ********************************************************************************/


        globals
            private hashtable pbdsDecayingUnitsHash = InitHashtable()
            private group pbdsDecayingUnitsUG       = CreateGroup()
            private trigger trgUnitDeath            = CreateTrigger()
            private trigger trgPeriodic             = CreateTrigger()
        endglobals

        private function TrgUnitDeathActions takes nothing returns nothing  // On any unit's death.
            local unit thisUnit = GetTriggerUnit()

            if (BlzGetUnitBooleanField(thisUnit, UNIT_BF_DECAYABLE)) then                                        // If the unit's death type has "Does decay" in there,
                call TriggerSleepAction(BlzGetUnitRealField(thisUnit, UNIT_RF_DEATH_TIME) + FLESH_DECAY + 0.03)  // wait out its "Art - Death Time (seconds)" + FLESH_DECAY + a tiny amount to make sure it's in bone decay,
                call SaveReal(pbdsDecayingUnitsHash, GetHandleId(thisUnit), 0, BONE_DECAY)                       // save it in the hashtable
                call SaveBoolean(pbdsDecayingUnitsHash, GetHandleId(thisUnit), 1, false)                         // with the boolean at index 1 set to false by default as its decay is not suspended automatically,
                call GroupAddUnit(pbdsDecayingUnitsUG, thisUnit)                                                 // then add it to pbdsDecayingUnitsUG.
                call EnableTrigger(trgPeriodic)                                                                  // Finally, start the periodic trigger in case the unit group was empty beforehand.
            endif                                                                                                // Note: Death Time is the amount of seconds from the unit's death till it starts decaying.
        endfunction                                                                                              // Note: Decaying starts with flesh decay, after which bone decay sets in.

        private function TrgPeriodicActions takes nothing returns nothing  // Runs every PERIOD seconds if pbdsDecayingUnitsUG is not empty.
            local integer i = 0
            local group tempUG = CreateGroup()  // A temporary group used to get rid of phantom units in pbdsDecayingUnitsUG.
            local unit enumUnit
            local real remaining = 0.00

            call BlzGroupAddGroupFast(pbdsDecayingUnitsUG, tempUG)  // Copy the contents of the original group into the temporary one,
            call GroupClear(pbdsDecayingUnitsUG)                    // then clear out the original. The temporary will be worked with.
            set enumUnit = BlzGroupUnitAt(tempUG, i)                // enumUnit is the variable used to cycle through the group's units.

            loop
                exitwhen i >= BlzGroupGetSize(tempUG)

                if (not(LoadBoolean(pbdsDecayingUnitsHash, GetHandleId(enumUnit), 1))) then             // If the boolean at index 1 for this unit is false, its decay is not suspended,
                    set remaining = LoadReal(pbdsDecayingUnitsHash, GetHandleId(enumUnit), 0) - PERIOD  // therefore keep the countdown going.
                    if (remaining > 0.00) then                                                          // If the the timer hasn't expired yet,
                        call SaveReal(pbdsDecayingUnitsHash, GetHandleId(enumUnit), 0, remaining)       // keep going.
                    else
                        call FlushChildHashtable(pbdsDecayingUnitsHash, GetHandleId(enumUnit))          // Otherwise, remove the unit from the hashtable, then remove the unit itself.
                        call RemoveUnit(enumUnit)                                                       // This is where the corpse disappears.
                    endif
                endif

                if (GetUnitTypeId(enumUnit) != 0) then                // If the unit is not removed yet, its unit-type is not zero. If it's removed, it's a phantom unit.
                    call GroupAddUnit(pbdsDecayingUnitsUG, enumUnit)  // Only existing units (technically, corpses) are copied back into the original group.
                endif

                set i = i + 1
                set enumUnit = BlzGroupUnitAt(tempUG, i)
            endloop
            call DestroyGroup(tempUG)
            set tempUG = null

            if (BlzGroupGetSize(pbdsDecayingUnitsUG) < 1) then  // Disable this trigger if there are no decaying corpses currently.
                call DisableTrigger(GetTriggeringTrigger())
                return
            endif
        endfunction

        private function PBDSInit takes nothing returns nothing
            call TriggerRegisterAnyUnitEventBJ(trgUnitDeath, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddAction(trgUnitDeath, function TrgUnitDeathActions)

            call DisableTrigger(trgPeriodic)
            call TriggerRegisterTimerEvent(trgPeriodic, PERIOD, true)
            call TriggerAddAction(trgPeriodic, function TrgPeriodicActions)
        endfunction

    /*******************************************************************************
    *                                                                              *
    *    [] PUBLIC                                                                 *
    *                                                                              *
    ********************************************************************************/


        function PBDSUnitSuspendDecay takes unit u, boolean suspend returns nothing  // Suspends bone decay of "u" if "suspend" is true, resumes it if false.
            if (IsUnitInGroup(u, pbdsDecayingUnitsUG)) then
                if (suspend) then
                    if (not(LoadBoolean(pbdsDecayingUnitsHash, GetHandleId(u), 1))) then
                        call SaveBoolean(pbdsDecayingUnitsHash, GetHandleId(u), 1, true)
                        call UnitSuspendDecay(u, true)
                        call SetUnitTimeScale(u, 0.00)
                    endif
                else
                    if (LoadBoolean(pbdsDecayingUnitsHash, GetHandleId(u), 1)) then
                        call SaveBoolean(pbdsDecayingUnitsHash, GetHandleId(u), 1, false)
                    endif
                endif
            endif
        endfunction
    endlibrary
     

  • Note: I recommend taking the folder from the test map instead here since the initializer has to be called manually, and people might forget about that. Bone decay time in the test map is set to 10 seconds instead of the default 88.
    Code (Lua):

    --[[****************************************************************************
    *                                                                              *
    *    Proper Bone Decay Suspension System                                       *
    *                                                                              *
    *    This system allows users to properly suspend the bone decay of units,     *
    *    and resume it at will. More specific info can be found further down.      *
    *                                                                              *
    *    Author:            J2Krauser                                              *
    *                                                                              *
    *    v1.0.0L:           08.09.2020                                             *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] REQUIREMENTS                                                           *
    *                                                                              *
    *        1. As I scripted (and saved) it using the latest version of           *
    *        Warcraft, and editor at the time (1.32.8 - 1.32i), I recommend        *
    *        using the system on at least these. However, it should work on        *
    *        older patches just as well as long as one doesn't go too far back,    *
    *        therefore this is more of an advice than a real requirement.          *
    *        2. Nothing. No unit indexer dependency, or anything of the sort.      *
    *        I found this important to point out.                                  *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] HOW TO IMPLEMENT                                                       *
    *                                                                              *
    *        1. Copy the folder called "PBDS".                                     *
    *        2. Paste it into your map.                                            *
    *        3. Read the HOW TO USE section.                                       *
    *        4. Read the VARIABLES section.                                        *
    *        5. Read the FUNCTIONS section.                                        *
    *        6. Read the NOTES section.                                            *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] HOW TO USE                                                             *
    *                                                                              *
    *        After having copied the system into your map, perform the quick       *
    *        setup below. Only two things are necessary if you have modified       *
    *        the gameplays constants in your map for flesh and/or bone decay:      *
    *        1. Set Setup.FleshDecay to "Decay Time (sec) - Flesh" in your map.    *
    *        2. Set Setup.BoneDecay to "Decay Time (sec) - Bones" in your map.     *
    *                                                                              *
    *        Additionally, you could fiddle with Setup.Peroid, but I recommend     *
    *        not to touch it. At this point everything should just work, the       *
    *        system should just do its thing.                                      *
    *                                                                              *
    *        There is only one public function you have to use called              *
    *        PBDSUnitSuspendDecay. It's fully explained further down, but the      *
    *        gist of it is:                                                        *
    *        If you want to suspend a unit's bone decay:                           *
    *            PBDSUnitSuspendDecay(unit, true)                                  *
    *        If you want to resume it:                                             *
    *            PBDSUnitSuspendDecay(unit, false)                                 *
    *                                                                              *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] SETUP                                                                  *
    *                                                                              *--]]


    do
       local Setup = {}

       Setup.Period = 0.5

       Setup.FleshDecay = 2.0
       Setup.BoneDecay = 88.0

    --[[****************************************************************************
    *                                                                              *
    *    [] VARIABLES                                                              *
    *                                                                              *
    *        REAL                                                                  *
    *        1. Setup.Period: How often the the periodic trigger runs which        *
    *        does the heavy lifting of this system. Can be changed if someone      *
    *        really wants to, but I recommend it stays at its default 0.5 set      *
    *        by me.                                                                *
    *                                                                              *
    *        REAL                                                                  *
    *        2. Setup.FleshDecay: In the editor: Advanced -> Gameplay              *
    *        Constants...                                                          *
    *        Here, find "Decay Time (sec) - Flesh", and this variable should       *
    *        hold the same value one finds there.                                  *
    *                                                                              *
    *        REAL                                                                  *
    *        3. Setup.BoneDecay: In the editor: Advanced -> Gameplay               *
    *        Constants...                                                          *
    *        Here, find "Decay Time (sec) - Bones", and this variable should       *
    *        hold the same value one finds there.                                  *
    *                                                                              *
    *                                   <<[**]>>                                   *
    *        Starting here, the rest are set up by my script, and do not have      *
    *        to be touched manually.                                               *
    *                                   <<[**]>>                                   *
    *                                                                              *
    *                                                                              *
    *        HASHTABLE                                                             *
    *        4. PBDS.DecayingUnitsHash: The main data structure the system uses    *
    *        to function.                                                          *
    *        It's structured in the following way:                                 *
    *            [KEY]: UNIT HANDLE                     INTEGER                    *
    *            [0]:   BONE DECAY                      REAL                       *
    *            [1]:   SUSPENDED                       BOOLEAN                    *
    *                                                                              *
    *        In other words, the key is a unit.                                    *
    *        0 holds its current bone decay timer, counting down. Basically the    *
    *        amount of seconds left till it's removed from the game, and           *
    *        disappears. 1 is a boolean that's true if the bone decay is           *
    *        suspended at the moment, which means the bone decay timer at 0 is     *
    *        not progressing; it's false if the unit is decaying, and will         *
    *        eventually get removed.                                               *
    *                                                                              *
    *        UNIT GROUP                                                            *
    *        5. PBDS.DecayingUnitsUG: Contains every unit that's in the            *
    *        hashtable. The bottom line is, these are units which are in the       *
    *        process of bone decay. They are eligible to be suspended.             *
    *                                                                              *
    *        TRIGGER                                                               *
    *        6. PBDS.TrgUnitDeath: The trigger that fires for every unit death.    *
    *        If the unit is decayable, it'll be eligible for bone decay            *
    *        suspension, therefore it gets stored in PBDS.DecayingUnitsHash and    *
    *        PBDS.DecayingUnitsUG.                                                 *
    *                                                                              *
    *        TRIGGER                                                               *
    *        7. PBDS.TrgPeriodic: Periodically executed as long as the unit        *
    *        group is not empty. Counts down the bone decay timers, removes        *
    *        decayed units.                                                        *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] FUNCTIONS                                                              *
    *                                                                              *
    *        function PBDSUnitSuspendDecay(u, suspend)                             *
    *            UNIT                                                              *
    *            u:       The unit to suspend or resume bone decay for.            *
    *            BOOLEAN                                                           *
    *            suspend: If true, suspends the decay. If false, resumes it.       *
    *            HOW IT WORKS:                                                     *
    *            1. Check if the unit is in PBDS.DecayingUnitsUG. If not, do       *
    *            nothing.                                                          *
    *            2. If "suspend" is true:                                          *
    *                1. If the boolean at index 1 for this unit in                 *
    *                PBDS.DecayingUnitsHash is false, set it to true, then call    *
    *                native UnitSuspendDecay.                                      *
    *                2. Call native SetUnitTimeScale. This native is used to       *
    *                to set the unit's animation speed to 0% (0.00) which          *
    *                prevents its corpse from disappearing over time.              *
    *            3. If "suspend" is false:                                         *
    *                1. If the boolean at index 1 for this unit in                 *
    *                PBDS.DecayingUnitsHash is true, set it to false.              *
    *                2. native UnitSuspendDecay isn't necessary to be called       *
    *                again here with false to resume the decay, as the system      *
    *                will just remove the unit anyway once it decayed.             *
    *                                                                              *
    ********************************************************************************
    *                                                                              *
    *    [] NOTES                                                                  *
    *                                                                              *
    *        1. I tried using SetUnitTimeScale to speed up the bone decay          *
    *        animation instead of manually removing the corpse once the timer      *
    *        is up. This doesn't work. I don't know the exact functionality of     *
    *        the game in the background, but it seems to me as if it's fully       *
    *        hardcoded once the decay is resumed how long it will take for the     *
    *        corpse to disappear, and changing animation speed has no effect on    *
    *        it whatsoever. Why is this important to mention? Because corpses      *
    *        will immediately disappear instead of quickly fading away. This is    *
    *        normal with this system.                                              *
    *                                                                              *
    *        2. Why is the system necessary? Wouldn't it work to just call         *
    *        the natives UnitSuspendDecay, and SetUnitTimeScale appropriately,     *
    *        then call it a day? No, it would not work. Let me explain how         *
    *        UnitSuspendDecay works to show why this system is needed.             *
    *        Basically, once the decay is suspended, the corpse will still         *
    *        disappear over time. This is why SetUnitTimeScale to 0.00 is used     *
    *        so that the animation is halted. Bone decay animations also have      *
    *        a fixed length much like every other, therefore just suspending       *
    *        the decay would keep the unit around (I know this is a bit hard       *
    *        to grasp for people who aren't that into these things.), but its      *
    *        corpse would be gone. You could still raise the unit, or resurrect    *
    *        it, the corpse would just not be there. Halting the animation         *
    *        using SetUnitTimeScale is what keeps the corpse around.               *
    *                                                                              *
    *        So? What's the issue with just calling UnitSuspendDecay again with    *
    *        false, then SetUnitTimeScale with 1.00? Well, this is where the       *
    *        way it works comes into the picture. You see, if you do this, it      *
    *        does NOT resume anything. You might think that would be the           *
    *        expected behaviour, but it actually starts the unit's bone decay      *
    *        all over again. You notice this once you have a few units which       *
    *        didn't all die exactly at the same moment, then you suspend their     *
    *        bone decay just to resume it again. Their corpses will all fade       *
    *        away at the exact same moment, exactly after the amount of seconds    *
    *        you have "Decay Time (sec) - Bones" set to in gameplay constants.     *
    *                                                                              *
    *        That's why this system is needed if you want to actually resume       *
    *        bone decay instead of restarting it at its full duration.             *
    *                                                                              *
    ********************************************************************************--]]


       local PBDS = {}

       PBDS.DecayingUnitsHash = InitHashtable()
       PBDS.DecayingUnitsUG = CreateGroup()
       PBDS.TrgUnitDeath = CreateTrigger()
       PBDS.TrgPeriodic = CreateTrigger()

       local function TrgUnitDeathActions()  -- On any unit's death.
          local thisUnit = GetTriggerUnit()

          if (BlzGetUnitBooleanField(thisUnit, ConvertUnitBooleanField(FourCC("udec")))) then                                   -- If the unit's death type has "Does decay" in there,
             TriggerSleepAction(BlzGetUnitRealField(thisUnit, ConvertUnitRealField(FourCC("udtm"))) + Setup.FleshDecay + 0.03)  -- wait out its "Art - Death Time (seconds)" + Setup.FleshDecay + a tiny amount to make sure it's in bone decay,
             SaveReal(PBDS.DecayingUnitsHash, GetHandleId(thisUnit), 0, Setup.BoneDecay)                                        -- save it in the hashtable
             SaveBoolean(PBDS.DecayingUnitsHash, GetHandleId(thisUnit), 1, false)                                               -- with the boolean at index 1 set to false by default as its decay is not suspended automatically,
             GroupAddUnit(PBDS.DecayingUnitsUG, thisUnit)                                                                       -- then add it to PBDS.DecayingUnitsUG.
             EnableTrigger(PBDS.TrgPeriodic)                                                                                    -- Finally, start the periodic trigger in case the unit group was empty beforehand.
          end                                                                                                                   -- Note: Death Time is the amount of seconds from the unit's death till it starts decaying.
       end                                                                                                                      -- Note: Decaying starts with flesh decay, after which bone decay sets in.

       local function TrgPeriodicActions()  -- Runs every Setup.Period seconds if PBDS.DecayingUnitsUG is not empty.
          local tempUG = CreateGroup()  -- A temporary group used to get rid of phantom units in PBDS.DecayingUnitsUG.

          BlzGroupAddGroupFast(PBDS.DecayingUnitsUG, tempUG)  -- Copy the contents of the original group into the temporary one,
          GroupClear(PBDS.DecayingUnitsUG)                    -- then clear out the original. The temporary will be worked with.

          for i = 0, BlzGroupGetSize(tempUG) - 1, 1 do
             local enumUnit = BlzGroupUnitAt(tempUG, i)
             if (not(LoadBoolean(PBDS.DecayingUnitsHash, GetHandleId(enumUnit), 1))) then                    -- If the boolean at index 1 for this unit is false, its decay is not suspended,
                local remaining = LoadReal(PBDS.DecayingUnitsHash, GetHandleId(enumUnit), 0) - Setup.Period  -- therefore keep the countdown going.
                if (remaining > 0.00) then                                                                   -- If the the timer hasn't expired yet,
                   SaveReal(PBDS.DecayingUnitsHash, GetHandleId(enumUnit), 0, remaining)                     -- keep going.
                else
                   FlushChildHashtable(PBDS.DecayingUnitsHash, GetHandleId(enumUnit))                        -- Otherwise, remove the unit from the hashtable, then remove the unit itself.
                   RemoveUnit(enumUnit)                                                                      -- This is where the corpse disappears.
                end
             end

             if (GetUnitTypeId(enumUnit) ~= 0) then           -- If the unit is not removed yet, its unit-type is not zero. If it's removed, it's a phantom unit.
                GroupAddUnit(PBDS.DecayingUnitsUG, enumUnit)  -- Only existing units (technically, corpses) are copied back into the original group.
             end
          end
          DestroyGroup(tempUG)
          tempUG = nil

          if (BlzGroupGetSize(PBDS.DecayingUnitsUG) < 1) then  -- Disable this trigger if there are no decaying corpses currently.
             DisableTrigger(GetTriggeringTrigger())
             return
          end
       end

    --[[****************************************************************************
    *                                                                              *
    *    [] PUBLIC                                                                 *
    *                                                                              *
    ********************************************************************************--]]


       function PBDSInit()
          for i = 0, bj_MAX_PLAYER_SLOTS - 1, 1 do
             TriggerRegisterPlayerUnitEvent(PBDS.TrgUnitDeath, Player(i), ConvertPlayerUnitEvent(20), nil)
          end
          TriggerAddAction(PBDS.TrgUnitDeath, TrgUnitDeathActions)

          DisableTrigger(PBDS.TrgPeriodic)
          TriggerRegisterTimerEvent(PBDS.TrgPeriodic, Setup.Period, true)
          TriggerAddAction(PBDS.TrgPeriodic, TrgPeriodicActions)
       end

       function PBDSUnitSuspendDecay(u, suspend)  -- Suspends bone decay of "u" if "suspend" is true, resumes it if false.
          if (IsUnitInGroup(u, PBDS.DecayingUnitsUG)) then
             if (suspend) then
                if (not(LoadBoolean(PBDS.DecayingUnitsHash, GetHandleId(u), 1))) then
                   SaveBoolean(PBDS.DecayingUnitsHash, GetHandleId(u), 1, true)
                   UnitSuspendDecay(u, true)
                   SetUnitTimeScale(u, 0.00)
                end
             else
                if (LoadBoolean(PBDS.DecayingUnitsHash, GetHandleId(u), 1)) then
                   SaveBoolean(PBDS.DecayingUnitsHash, GetHandleId(u), 1, false)
                end
             end
          end
       end
    end
     

  • JASS:
    v1.0.0

    • Initial release.
    Lua:
    v1.0.0L

    • Initial release.
Contents

Proper Bone Decay Suspension System (Map)

Proper Bone Decay Suspension System (Map)

  1. J2Krauser

    J2Krauser

    Joined:
    Jul 31, 2020
    Messages:
    56
    Resources:
    0
    Resources:
    0
    Hi.

    I added an additional Lua version of this system just now. However, I have to point out I quickly checked on Lua syntax yesterday night in about an hour, then converted the script into Lua, therefore I'm not exactly clear with proper practices. I tested it thoroughly, it seems to function properly, but if any Lua enthusiast can take a look at it, and tell me if there's something I shouldn't do the way I did, that would be helpful. Thank you.