1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. 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.

Main indexers: a snippet for basic cross-compatibility ?

Discussion in 'World Editor Help Zone' started by Ricola3D, Oct 3, 2019.

  1. Ricola3D

    Ricola3D

    Joined:
    Feb 27, 2019
    Messages:
    225
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Hello,

    I am trying to make a Armor system with a unit indexer as requirement. I need to store the "code armor" value of units whose armor is modified by BlzSetUnitArmor native. Then I use it as input in other pieces of code.

    I can't use hashtable no ? Because if the handle ID of a removed unit is reused, I'll see the previous value (not cleaned..). However there are many indexers, mainly:
    Do you know a JASS snippet of code to have a basic read-compatibility with all those indexers ?
    Just a getter for unit index, and a way to "register" a callback on index/deindex events.

    I hope so, otherwise I'll have to write it myself :S
     
  2. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    3,056
    Resources:
    1
    Spells:
    1
    Resources:
    1
    You can hook RemoveUnit and use a trigger to catch unit death. Flush the entry when either happens to the unit.
     
  3. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,162
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Have a look at TriggerHappy's thread at "Compatibility With Other Indexers".
     
  4. Ricola3D

    Ricola3D

    Joined:
    Feb 27, 2019
    Messages:
    225
    Resources:
    1
    Maps:
    1
    Resources:
    1
    You mean:
    - On remove: flush
    - On Death: get unit boolean field "decayable". If it is not, then flush.
    - On decay: flush
    ?
    Does it handle the case of heroes ? Heroes are revivable even after they decay no ? And a dead hero may be removed 'automatically' when the last reference on it is nulled no ? I wonder how the indexers handle the case. Maybe a periodic check if GetUnitUserData returns 0 or not ?

    Thanks for the tip, I'll check it ASAP! I hope the code is not too complicated for a newbie like me^^
     
  5. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,596
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    No he means using a unit event system (like those in the spell section and script sections) which uses the footman defend ability to detect when a unit is removed from the game as it generates an undefend order when the unit is removed.

    These are the systems behind unit indexers to know when to recycle unit index values.
     
    Last edited: Oct 4, 2019
  6. Ricola3D

    Ricola3D

    Joined:
    Feb 27, 2019
    Messages:
    225
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Thanks for clearing the missunderstanding! Existing unit event systems are:
    - GUI Unit Event (by Bribe, includes GUI Unit Indexer, not-compatible with other indexers)
    - Unit Event (by Nestharus, uses Unit Indexer, is it compatible with other indexers that uses GetUnitUserData ?)
    - AutoEvents (by grim001, requires AutoIndex, not-compatible with other indexers)
    Aren't they ? Did I miss one ?

    With that I'm back to my cross-compatibility issue no ?


    Edit:
    I just realized that my system has a function that temporary adds then removes an ability. Instead of removing it, I could disabling it. So I can differentiate:
    - Unit without the ability > new, reset the hasthable value to 0 for this handle id
    - Unit with ability > do not reset
    No ? The only default would be my hasthable will grow in size (because I never clean the parentKey nor childkey..
     
    Last edited: Oct 4, 2019
  7. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,596
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    That can be considered a leak.

    As I said, use a unit event system which detects unit removal using the undefend order from the human defend ability. One can use this event for clean up.

    Of course one could just use Lua tables with weak keys instead as those automatically garbage collect the mappings when the key is garbage collected. However this requires the map be running in Lua mode.
     
  8. Ricola3D

    Ricola3D

    Joined:
    Feb 27, 2019
    Messages:
    225
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Right about the leak.
    Mhhh I don't need all the event system, so maybe just recoding myself the part I need to avoid having many dependencies ?
    Do you have any idea of how the Defend ability test works ? When do they check if it is still here or gone ?^^
     
  9. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,596
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    One can look at the source code of the event systems. From what I understand a undefend order is issued (which an immediate order event can detect) when the unit is being removed but before it is actually removed.
     
  10. Ricola3D

    Ricola3D

    Joined:
    Feb 27, 2019
    Messages:
    225
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Thanks a lot, with that I can design an armor system with many usefull functions:
    - Armor_GetWhite > get white armor
    - Armor_GetGreen > get armor bonus
    - Armor_GetTotal > get total armor
    - ToDamageFactor > transform armor to damage factor
    - FromDamageFactor > transform damage factor to armor
    - Armor_AdjustToWhite
    - Armor_AdjustToTotal

    And many others less usefull but that can serve some custom spells :)

    Exemple of system code below:
    Code (vJASS):

    library Armor initializer onInit requires Logarithm

    globals
        // Values shall be taken from gameplay constants
        public constant real AGI_DEFENSE_BASE = 5.00 // "Hero Attributes - Defense Base Value (before Agility Bonus)"
        public constant real AGI_DEFENSE_BONUS = 0.10 // "Hero Attributes - Defense Bonus per Agility Point"
        public constant real DEFENSE_ARMOR = 0.06 // "Combat - Armor Damage Reduction Multiplier"

        // An ability derivated from Devotion Aura that nullifies what Blizzard considers "base" armor.
        public constant integer NULLIFY_BASE_ARMOR_ABILITY = 'A05K'
        public constant integer NULLIFY_BASE_ARMOR_BUFF = 'B01J'

        // System cleaning - Ability to detect unit removal from game
        // If you already have a Unit Event system (GUI Unit Event by Bribe, Unit Event by Nestharus, or AutoEvents by grim001) you can reuse the same ability.
        public constant integer DETECT_REMOVE_ABILITY = 'A06D'

        // Set to true if you want the getter/setters to work for hidden units.
        // Notes:
        // - Works fine except in one case: during "[ANef] Storm, Earth, And Fire" ability cast.
        public constant boolean ENABLE_UNHIDE = true

        // Set to true to enable last ressort action (if nothing else worked to compute armors).
        // Notes:
        // - No known case for the moment, it's just a safe fallback measure.
        // - Will temporary remove items, and remove all buffs (only aura/passive buffs will be re-activate after, not active ones)
        public constant boolean ENABLE_BONUS_REMOVAL = true

        private hashtable unitData
    endglobals

        // ----------------------------------------------------------------------------
        // Concepts:
        // - Total armor = white armor + green armor.
        //    > White armor = base armor + agi (bonuses excluded) armor + code armor (an internal variable).
        //        > Base armor: from unit fields "[def] Combat - Defense base" + "[defUp] Defense Upgrade Bonus" * number of defense upgrades.
        //        > Agi armor (bonus excluded): AGI_DEFENSE_BASE + AGI_DEFENSE_BONUS * agility (bonus excluded).
        //        > Code armor: an internal value, not available with JASS natives...
        //    > Green armor = agi bonuses armor + abilities armor.
        //        > Agility bonuses: AGI_DEFENSE_BASE + AGI_DEFENSE_BONUS * agility bonus.
        //        > Abilities armor: sum of all bonuses provided by item abilities, passive abilities & buffs.
        //
        // Modification:
        // - Code armor is the only one you can edit directly by code.
        // - Base armor can only be modified in Object editor or by adding upgrades.
        // - Agi armor can only be modified by changing hero's agility.
        // - Green armor can only be modified by abilities.
        //
        // Other notes:
        // - Non-hero units do not have agility, and thus agility armor neither.
        // - Dead units have green armor = 0.
        // - Base armor is not a constant!! It can be modified when the unit morphs into another type, or when player learns an upgrade.
        // - Devotion aura with "Data - Percent bonus" = true ONLY considers base armor, and totally ignores agi, code & green armors.




        // ----------------------------------------------------------------------------
        // SaveCode.
        // Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
        // ----------------------------------------------------------------------------
        private function SaveCode takes unit whichUnit, real codeArmor returns nothing
            if ( null != whichUnit ) then
                // In case there is no Unit Event system, add the remove detect ability manually.
                if ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) ) then
                    call UnitAddAbility(whichUnit, DETECT_REMOVE_ABILITY)
                    call UnitMakeAbilityPermanent(whichUnit, true, DETECT_REMOVE_ABILITY)
                endif
                call SaveReal( unitData, GetHandleId(whichUnit), StringHash("code_armor"), codeArmor )
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // ToDamageFactor: get damage modifier corresponding to a armor value.
        // Notes:
        // - "0.02" means damages are reduced by 98%.
        // - "1.25" means damages are amplified by 25%.
        // - For the moment Blizzard displays -71% as min reduction
        // ----------------------------------------------------------------------------
        public function ToDamageFactor takes real whichArmor returns real
            local real factor = 0.0
            if (whichArmor >= 0) then
                // Damage reduction - factor is inferior or equal to 1
                set factor = 1 - ( whichArmor * DEFENSE_ARMOR ) / ( 1 + whichArmor * DEFENSE_ARMOR )
            else
                // DamageAmplification - factor is superior to 1
                set factor = 2 - Pow( 1 - DEFENSE_ARMOR , -1 * whichArmor )
            endif
            return factor
        endfunction

        // ----------------------------------------------------------------------------
        // FromDamageFactor: computes the armor necessary to reach this damage factor.
        // Notes:
        // - Does not work for 0 walue (= dodge, nullify damage).
        // - Does not work for negative values (=heal).
        // ----------------------------------------------------------------------------
        public function FromDamageFactor takes real whichDamageFactor returns real
            local real armor = 0.0
            if (whichDamageFactor <= 0.0) then
                // Does not work - infinite armor or healing
                call BJDebugMsg("Armor_FromDamageFactor - Negative or null factor:" + R2S(whichDamageFactor) + ".")
            elseif (whichDamageFactor <= 1.0) then
                // Damage reduction - armor is positive or null
                set armor = ( 1 - whichDamageFactor ) / ( whichDamageFactor * DEFENSE_ARMOR )
            else
                // Damage amplification - armor is negative
                set armor = -1.0 * Logarithm_Logarithm( 1.0 - DEFENSE_ARMOR, 2.0 - whichDamageFactor )
            endif
            return armor
        endfunction

        // ----------------------------------------------------------------------------
        // GetAgi: get armor bonus from agility. Returns 0 for non-hero units.
        // ----------------------------------------------------------------------------
        public function GetAgi takes unit whichUnit, boolean includeBonuses returns real
            local real agiArmor = 0.0
            local integer agi = 0
            local boolean isHero = false
            if ( null != whichUnit ) then
                set isHero = IsUnitIdType(GetUnitTypeId(whichUnit), UNIT_TYPE_HERO)
                if (isHero) then
                    // Unit is hero, illusion of hero, ...
                    set agi = GetHeroAgi(whichUnit, includeBonuses)
                    set agiArmor = AGI_DEFENSE_BASE + AGI_DEFENSE_BONUS * I2R(agi)
                endif
            endif
            return agiArmor
        endfunction

        // ----------------------------------------------------------------------------
        // GetCode: get code armor. This is the only armor you can modify directly (with BlzSetUnitArmor primitive).
        // ----------------------------------------------------------------------------
        public function GetCode takes unit whichUnit returns real
            local real codeArmor = 0.0
            if ( null != whichUnit ) then
                set codeArmor = LoadReal( unitData, GetHandleId(whichUnit), StringHash("code_armor") )
            endif
            return codeArmor
        endfunction

        // ----------------------------------------------------------------------------
        // GetBase: get what Blizzard considers "base" armor.
        // If no upgrades, equal to the number in World Editor > Object Editor > Unit > "[def] Combat - Defense base".
        // Otherwise also includes upgrades count x "[defUp] Defense Upgrade Bonus"
        // Notes:
        // - Meaningless in 99% of cases. Equal to white armor ONLY for non-hero units whose armor have not been modified by code...)
        // - This is the value taken as base by Devotion Aura with "[DataB1] Data - Percent Bonus" set to true.
        // ----------------------------------------------------------------------------
        public function GetBase takes unit whichUnit returns real
            local real totalArmorBefore = 0.0
            local real totalArmorAfter = 0.0
            local real baseArmor = 0.0
    static if ENABLE_BONUS_REMOVAL then
            local integer inventorySize = 0
            local integer tempInventorySlot = 0
            local item array unitItems
    endif
            local boolean abilityAdded = false
            local boolean buffAdded = false
            local boolean isDead = false
    static if ENABLE_UNHIDE then
            local boolean isHidden = false
    endif
            if ( null != whichUnit ) then
    static if ENABLE_UNHIDE then
                set isHidden = IsUnitHidden(whichUnit)
                if (isHidden) then
                    call ShowUnit(whichUnit, true)
                endif
    endif
                // Try to compute base armor using the test ability (that nullifies base armor).
                set totalArmorBefore = BlzGetUnitArmor(whichUnit)
                call UnitAddAbility(whichUnit, NULLIFY_BASE_ARMOR_ABILITY)
                set abilityAdded = ( GetUnitAbilityLevel(whichUnit, NULLIFY_BASE_ARMOR_ABILITY) > 0 )
                set buffAdded = UnitHasBuffBJ(whichUnit, NULLIFY_BASE_ARMOR_BUFF)
                if (abilityAdded and buffAdded) then
                    // Test worked, base armor has been nullified properly.
                    set totalArmorAfter = BlzGetUnitArmor(whichUnit)
                    set baseArmor = totalArmorBefore - totalArmorAfter
                else
                    // Test failed
                    set isDead = IsUnitDeadBJ(whichUnit)
                    if (isDead) then
                        // Case A: unit is dead. Thus its green armor is 0, and base = total - agi - code
                        set baseArmor = BlzGetUnitArmor(whichUnit) - GetAgi(whichUnit, false) - GetCode(whichUnit)
    static if ENABLE_BONUS_REMOVAL then
                    else
                        // Case Z: last resort is to remove all armor bonuses
                        call BJDebugMsg( "Last resort - Unit " + GetUnitName(whichUnit) )
                        // Temporary remove all items
                        set inventorySize = UnitInventorySizeBJ(whichUnit)
                        set tempInventorySlot = 0
                        loop
                            set tempInventorySlot = tempInventorySlot + 1
                            exitwhen tempInventorySlot > inventorySize
                            set unitItems[tempInventorySlot] = UnitItemInSlotBJ(whichUnit, tempInventorySlot)
                            if ( null != unitItems[tempInventorySlot] ) then
                                call UnitRemoveItemFromSlot(whichUnit, tempInventorySlot)
                            endif
                        endlop
                        // Remove all buffs (unfortunately there is no way to restore the active buffs)
                        call UnitRemoveBuffsEx(whichUnit, true, true, true, true, false, true, true) // Remove all buffs but timed life
                        set armor = BlzGetUnitArmor(whichUnit) - GetAgi(whichUnit, false) - GetCode(whichUnit)
                        // Restore all items
                        set tempInventorySlot = 0
                        loop
                            set tempInventorySlot = tempInventorySlot + 1
                            exitwhen tempInventorySlot > inventorySize
                            if ( null != unitItems[tempInventorySlot] ) then
                                call UnitDropItemSlotBJ( whichUnit, unitItems[tempInventorySlot], tempInventorySlot )
                            endif
                        endlop
    endif
                    endif
                endif
                if (abilityAdded) then
                    call UnitRemoveAbility(whichUnit, NULLIFY_BASE_ARMOR_ABILITY)
                endif
                if (buffAdded) then
                    call UnitRemoveBuffBJ(NULLIFY_BASE_ARMOR_BUFF, whichUnit)
                endif
    static if ENABLE_UNHIDE then
                if (isHidden) then
                    call ShowUnit(whichUnit, false)
                endif
    endif
            endif
            return baseArmor
        endfunction

        // ----------------------------------------------------------------------------
        // GetWhite: get armor before bonus (= number displayed in white in unit status).
        // ----------------------------------------------------------------------------
        public function GetWhite takes unit whichUnit returns real
            local real whiteArmor = 0.0
            local integer baseAgi = 0
            if ( null != whichUnit ) then
                set whiteArmor = GetBase(whichUnit) + GetAgi(whichUnit, false) + GetCode(whichUnit)
            endif
            return whiteArmor
        endfunction

        // ----------------------------------------------------------------------------
        // GetTotal: get total armor. Same as BlzGetUnitArmor.
        // ----------------------------------------------------------------------------
        public function GetTotal takes unit whichUnit returns real
            local real armor = 0.0
            if ( null != whichUnit ) then
                set armor = BlzGetUnitArmor(whichUnit)
            endif
            return armor
        endfunction

        // ----------------------------------------------------------------------------
        // GetGreen: get current armor bonus (= number displayed in green in unit status.
        // ----------------------------------------------------------------------------
        public function GetGreen takes unit whichUnit returns real
            return ( GetTotal(whichUnit) - GetWhite(whichUnit) )
        endfunction

        // ----------------------------------------------------------------------------
        // Modify: apply difference to unit code armor (and thus to white armor). Difference can be positive or negative.
        // Also modifies white armor and total armor of the same difference.
        // ----------------------------------------------------------------------------
        public function Modify takes unit whichUnit, real armorDiff returns nothing
            local real currentTotalArmor = 0.0
            if ( null != whichUnit ) then
                set currentTotalArmor = BlzGetUnitArmor(whichUnit)
                call BlzSetUnitArmor(whichUnit, currentTotalArmor + armorDiff )
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // SetCode: set unit code armor to desired value.
        // ----------------------------------------------------------------------------
        public function SetCode takes unit whichUnit, real desiredCodeArmor returns nothing
            local real currentCodeArmor = 0.0
            local real armorDiff = 0.0
            if ( null != whichUnit ) then
                set currentCodeArmor = GetCode(whichUnit)
                set armorDiff = desiredCodeArmor - currentCodeArmor
                call Modify(whichUnit, armorDiff)
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // AdjustToWhite: set unit base armor such as white armor reaches the desired amount.
        // ----------------------------------------------------------------------------
        public function AdjustToWhite takes unit whichUnit, real desiredWhiteArmor returns nothing
            local real currentWhiteArmor = 0.0
            local real armorDiff = 0.0
            if ( null != whichUnit ) then
                set currentWhiteArmor = GetWhite(whichUnit)
                set armorDiff = desiredWhiteArmor - currentWhiteArmor
                call Modify(whichUnit, armorDiff)
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // AdjustToWhitePercent: set unit base armor such as white armor changes of the desirated percent.
        // Ex: 1.50 will increase white armor of 50%
        // ----------------------------------------------------------------------------
        public function AdjustToWhitePercent takes unit whichUnit, real desiredWhiteArmorPercent returns nothing
            local real currentWhiteArmor = 0.0
            local real armorDiff = 0.0
            if ( null != whichUnit ) then
                set currentWhiteArmor = GetWhite(whichUnit)
                set armorDiff = currentWhiteArmor * (desiredWhiteArmorPercent - 1.0)
                call Modify(whichUnit, armorDiff)
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // AdjustToTotal: set unit base armor such as total reaches the desired amount.
        // Same as BlzSetUnitArmor.
        // ----------------------------------------------------------------------------
        public function AdjustToTotal takes unit whichUnit, real desiredTotalArmor returns nothing
            if ( null != whichUnit ) then
                call BlzSetUnitArmor(whichUnit, desiredTotalArmor)
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // AdjustToTotalPercent: set unit base armor such as total armor changes of the desirated percent.
        // Ex: 1.50 will increase total armor of 50%
        // ----------------------------------------------------------------------------
        public function AdjustToTotalPercent takes unit whichUnit, real desiredTotalArmorPercent returns nothing
            local real currentTotalArmor = 0.0
            if ( null != whichUnit ) then
                set currentTotalArmor = BlzGetUnitArmor(whichUnit)
                call BlzSetUnitArmor( whichUnit, currentTotalArmor * desiredTotalArmorPercent )
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // onBlzSetUnitArmor: called just before everytime code sets unit armor with "BlzSetUnitArmor" native.
        // Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
        // ----------------------------------------------------------------------------
        private function onBlzSetUnitArmor takes unit whichUnit, real armorAmount returns nothing
            local real previousCodeArmor = 0.0
            local real nextCodeArmor = 0.0
            local real previousArmorAmount = 0.0
            local real armorDiff = 0.0
            if ( null != whichUnit ) then
                // Detect armor difference
                set previousArmorAmount = BlzGetUnitArmor(whichUnit)
                set armorDiff = armorAmount - previousArmorAmount

                // Get previous code armor (0 if not existing), update it, and save-it back
                set previousCodeArmor = GetCode(whichUnit)
                set nextCodeArmor = previousCodeArmor + armorDiff
                call SaveCode(whichUnit, nextCodeArmor)
            endif
        endfunction
        hook BlzSetUnitArmor onBlzSetUnitArmor

        // ----------------------------------------------------------------------------
        // onBlzSetUnitRealField: called just before everytime code sets unit armor with "BlzSetUnitRealField" native for UNIT_RF_DEFENSE field.
        // Notes: intenally just calls onBlzSetUnitArmor function.
        // ----------------------------------------------------------------------------
        private function onBlzSetUnitRealField takes unit whichUnit, unitrealfield whichField, real value returns nothing
            if ( UNIT_RF_DEFENSE == whichField ) then
                call onBlzSetUnitArmor(whichUnit, value)
            endif
        endfunction
        hook BlzSetUnitRealField onBlzSetUnitRealField

        // ----------------------------------------------------------------------------
        // onUnitRemoved: called just before a unit is removed from the game. Cleans internal storage.
        // ----------------------------------------------------------------------------
        public function onUnitRemoved takes nothing returns nothing
            local unit whichUnit = GetTriggerUnit()
            local boolean unitBeingRemoved
            // Because unit has DETECT_REMOVE_ABILITY, it will fire a "undefend" event just before being removed from game.
            if ( OrderId2StringBJ(GetIssuedOrderIdBJ()) == "undefend" ) then
                set unitBeingRemoved = ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) )
                if (unitBeingRemoved) then
                    call FlushChildHashtable( unitData, GetHandleId(whichUnit) )
                endif
            endif
        endfunction

        // ----------------------------------------------------------------------------
        // onInit: system initializer
        // ----------------------------------------------------------------------------
        private function onInit takes nothing returns nothing
            local trigger cleanTrigger

            // Init hashtable
            set unitData = InitHashtable()

            // Init a trigger to detect unit removal (and clean hashtable)
            set cleanTrigger = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ( cleanTrigger, EVENT_PLAYER_UNIT_ISSUED_ORDER )
            call TriggerAddAction( cleanTrigger, function onUnitRemoved )
        endfunction

    endlibrary
     
    Last edited: Oct 7, 2019