1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. 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
  3. 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
  4. We have recently started the 16th edition of the Mini Mapping Contest. The theme is mini RPG. Do check it out and have fun.
    Dismiss Notice
  5. Dismiss Notice
  6. The Highway to Hell has been laid open. Come along and participate in the 5th Special Effect Contest.
    Dismiss Notice
  7. 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.

Trigger Viewer

UserInterface v1.0.6.w3x
Variables
Initialization
StartGame
Item Database
---------------------------
Basic UI Example
Required Libraries
UserInterface
---------------------------
UnitDex
PlayerUtils
Camera
Math
Optional
BoundSentinel
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.
//
Name Type Is Array Initial Value
library StartGame initializer Init requires UserInterface

    globals
        Camera array PlayerCamera
        unit array PlayerHero
    endglobals
   
    private function Init takes nothing returns nothing
        local integer i = 0
        local integer array urace
        local User user
       
        set urace[1] = 'Hpal'
        set urace[2] = 'Obla'
        set urace[3] = 'Ulic'
        set urace[4] = 'Edem'

        call FogEnable(false)
        call FogMaskEnable(false)
       
        loop
            exitwhen i == User.AmountPlaying
           
            set user = User.fromPlaying(i)
            set PlayerCamera[user.id] = Camera.create()
           
            // create hero
            set PlayerHero[user.id] = CreateUnitAtLoc(user.handle, urace[GetHandleId(GetPlayerRace(user.handle))], GetStartLocationLoc(GetPlayerStartLocation(user.handle)), 180)
           
            //call ShowUnit(PlayerHero[user.id], false)

            // get vision from dummy player
            call SetPlayerAllianceStateBJ(CAMERA_DUMMY_PLAYER, user.handle, bj_ALLIANCE_ALLIED_VISION)
            call SetPlayerAllianceStateBJ(user.handle, CAMERA_DUMMY_PLAYER, bj_ALLIANCE_ALLIED_VISION)
       
            if (User.Local == user.handle) then
                call ClearSelection()
                call SelectUnit(PlayerHero[user.id], true)
            endif
           
            set i = i + 1
        endloop
    endfunction

endlibrary
library ItemDatabase initializer InventoryInitDB

globals
    private hashtable gcbag
    private integer TypeTextureBlank = 'dbnk'
endglobals


function GetItemTexture takes integer typeitem returns integer
    //local string hi = I2S(typeitem)
    local integer hi = typeitem
    local string tex
    if HaveSavedString(gcbag, StringHash("Texture"), hi) then
        set tex = LoadStr(gcbag, StringHash("Texture"), hi)
        if HaveSavedInteger(gcbag, StringHash("IconDest"), StringHash(tex)) then
            return LoadInteger(gcbag, StringHash("IconDest"), StringHash(tex))
        endif
    endif
    return TypeTextureBlank
endfunction

function GetItemCost takes integer typeitem returns integer
    //local string hi = I2S(typeitem)
    local integer hi = typeitem
    if HaveSavedInteger(gcbag, StringHash("Cost"), hi) then
        return  LoadInteger(gcbag, StringHash("Cost"), hi)
    endif
    return -1
endfunction

function GetItemDescription takes integer typeitem returns string
    //local string hi = I2S(typeitem)
    local integer hi = typeitem
    if HaveSavedString(gcbag, StringHash("Description"), hi) then
        return LoadStr(gcbag, StringHash("Description"), hi)
    endif
    return null
endfunction

function GetItemHasCharges takes integer typeitem returns boolean
    //local string hi = I2S(typeitem)
    local integer hi = typeitem
    if HaveSavedBoolean(gcbag, StringHash("Charge"), hi) then
        return LoadBoolean(gcbag, StringHash("Charge"), hi)
    endif
    return false
endfunction

function InventoryInitItemDB_Func takes integer ItemType, string Texture, string Description, integer Cost, boolean Charge returns nothing
    //local string ItemTypeStr = I2S(ItemType)
    local integer ItemTypeStr = ItemType
    call SaveStr(gcbag, StringHash("Texture"), ItemTypeStr, Texture)
    call SaveStr(gcbag, StringHash("Description"), ItemTypeStr, Description)
    call SaveInteger(gcbag, StringHash("Cost"), ItemTypeStr, Cost)
    call SaveBoolean(gcbag, StringHash("Charge"), ItemTypeStr, Charge)
endfunction

function InventoryInitDestDB_Func takes integer TypeDest, string Texture returns nothing
    call SaveInteger(gcbag, StringHash("IconDest"), StringHash(Texture), TypeDest)
endfunction

function InventoryInitIconDB_Func takes integer TypeUnit, string Texture returns nothing
    call SaveStr(gcbag, StringHash("Texture"), TypeUnit, Texture)
endfunction


private function InventoryInitDB takes nothing returns nothing
    set gcbag = InitHashtable()
    //---------------------------------------------------------------------------------------------------+
    //                                                                                                   |
    //                                               Item                                                |
    //                                                                                                   |
    //---------------------------------------------------------------------------------------------------+
    //__________
    call InventoryInitItemDB_Func('afac', "BTNAlleriaFlute.blp"     , "Increases nearby ranged units' damage by <AIar,DataA1,%>%. |nDoes not stack with Trueshot Aura.", 400, false)
    call InventoryInitItemDB_Func('spsh', "BTNSpellShieldAmulet.blp", "Blocks a negative spell that an enemy casts on the Hero once every <ANss,Cool1> seconds.", 400, false)
    call InventoryInitItemDB_Func('ajen', "BTNJanggo.blp"           , "Grants the Hero and friendly nearby units increased attack rate and movement speed. |nDoes not stack with Endurance Aura.", 500, false)
    call InventoryInitItemDB_Func('bgst', "BTNBelt.blp"             , "Increases the Strength of the Hero by 6 when worn.", 500, false)
    call InventoryInitItemDB_Func('belv', "BTNBoots.blp"            , "Increases the Agility of the Hero by 6 when worn.", 500, false)
    call InventoryInitItemDB_Func('bspd', "BTNBootsOfSpeed.blp"     , "Increases the movement speed of the Hero when worn.", 150, false)
    call InventoryInitItemDB_Func('cnob', "BTNCirclet.blp"          , "Increases the Strength, Agility and Intelligence of the Hero by 2 when worn.", 175, false)
    call InventoryInitItemDB_Func('ratc', "BTNClawsOfAttack.blp"    , "Increases the attack damage of the Hero by 12 when worn.", 500, false)
    call InventoryInitItemDB_Func('rat6', "BTNClawsOfAttack.blp"    , "Increases the attack damage of the Hero by 6 when worn.", 100, false)
    call InventoryInitItemDB_Func('rat9', "BTNClawsOfAttack.blp"    , "Increases the attack damage of the Hero by 9 when worn.", 400, false)
    call InventoryInitItemDB_Func('clfm', "BTNCloakOfFlames.blp"    , "Engulfs the Hero in fire which deals <AIcf,DataA1> damage per second to nearby enemy land units. |nDoes not stack with Immolation.", 600, false)
    call InventoryInitItemDB_Func('clsd', "BTNCloak.blp"            , "Provides the Hero with invisibility at night when worn. An invisible Hero is untargetable by the enemy unless detected. If the Hero moves, attacks, uses an ability, or casts a spell, the invisibility effect is lost.", 100, false)
    call InventoryInitItemDB_Func('crys', "BTNCrystalBall.blp"      , "Reveals a targeted area. Invisible units are also revealed by the Crystal Ball's effect. |nLasts <AIta,Dur1> seconds.", 500, false)
    call InventoryInitItemDB_Func('dsum', "BTNDarkSummoning.blp"    , "Teleports <AUds,DataA1> of the player's units within the targeted area to the location of the Hero when used.", 400, false)
    call InventoryInitItemDB_Func('lgdh', "BTNHornOfDoom.blp"       , "Grants the Hero and friendly nearby units increased life regeneration and movement speed. |nDoes not stack with Unholy Aura.", 400, false)
    call InventoryInitItemDB_Func('hval', "BTNHelmOfValor.blp"      , "Increases the Strength and Agility of the Hero by 4 when worn.", 500, false)
    call InventoryInitItemDB_Func('rst1', "BTNGauntletsOfOgrePower.blp", "Increases the Strength of the Hero by 3 when worn.", 100, false)
    call InventoryInitItemDB_Func('gcel', "BTNGlove.blp"            , "Increases the attack speed of the Hero by <AIsx,DataA1,%>% when worn.", 100, false)
    call InventoryInitItemDB_Func('hcun', "BTNHoodOfCunning.blp"    , "Increases the Agility and Intelligence of the Hero by 4 when worn.", 500, false)
    call InventoryInitItemDB_Func('rhth', "BTNPeriapt1.blp"         , "Increases the hit points of the Hero by <AIl2,DataA1> when worn.", 500, false)
    call InventoryInitItemDB_Func('kpin', "BTNPipeOfInsight.blp"    , "Grants the Hero and friendly nearby units a bonus to mana regeneration. |nDoes not stack with Brilliance Aura.", 500, false)
    call InventoryInitItemDB_Func('rin1', "BTNMantleOfIntelligence.blp", "Increases the Intelligence of the Hero by 3 when worn.", 100, false)
    call InventoryInitItemDB_Func('mcou', "BTNMedalionOfCourage.blp", "Increases the Strength and Intelligence of the Hero by 4 when worn.", 500, false)
    call InventoryInitItemDB_Func('odef', "BTNOrbOfDarkness.blp"    , "Adds <AIdf,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attack also becomes ranged when attacking air and will create a Dark Minion when it is the killing blow on an enemy unit. The Dark Minion lasts <ANbs,DataC1> seconds.", 500, false)
    call InventoryInitItemDB_Func('penr', "BTNPendantOfEnergy.blp"  , "Increases the mana capacity of the Hero by <AImb,DataA1> when worn.", 400 , false)
    call InventoryInitItemDB_Func('pmna', "BTNPendantOfMana.blp"    , "Increases the mana capacity of the Hero by <AIbm,DataA1> when worn.", 500, false)
    call InventoryInitItemDB_Func('prvt', "BTNPeriapt.blp"          , "Increases the hit points of the Hero by <AIlf,DataA1> when worn.", 350, false)
    call InventoryInitItemDB_Func('rde1', "BTNRingGreen.blp"        , "Increases the armor of the Hero by 2 when worn.", 150, false)
    call InventoryInitItemDB_Func('rde2', "BTNRingGreen.blp"        , "Increases the armor of the Hero by 3 when worn.", 400, false)
    call InventoryInitItemDB_Func('rde3', "BTNRingGreen.blp"        , "Increases the armor of the Hero by 4 when worn.", 500, false)
    call InventoryInitItemDB_Func('rlif', "BTNRingSkull.blp"        , "Increases the Hero's hit point regeneration by <Arel,DataA1> hit points per second.", 200, false)
    call InventoryInitItemDB_Func('ciri', "BTNRobeOfTheMagi.blp"    , "Increases the Intelligence of the Hero by 6 when worn.", 500, false)
    call InventoryInitItemDB_Func('brac', "BTNRunedBracers.blp"     , "Reduces Magic damage dealt to the Hero by <AIsr,DataB1,%>%.", 400, false)
    call InventoryInitItemDB_Func('sbch', "BTNBoneChimes.blp"       , "Grants a melee Hero and friendly nearby melee units life stealing attacks which take <AIav,DataA1,%>% of the damage they deal and convert it into life. |nDoes not stack with Vampiric Aura.", 450, false)
    call InventoryInitItemDB_Func('rag1', "BTNSlippersOfAgility.blp", "Increases the Agility of the Hero by 3 when worn.", 100, false)
    call InventoryInitItemDB_Func('rwiz', "BTNSobiMask.blp"         , "Increases the Hero's rate of mana regeneration by <AIrm,DataA1,%>% when worn.", 400, false)
    call InventoryInitItemDB_Func('ssil', "BTNStaffOfSilence.blp"   , "Stops all enemies in a target area from casting spells.", 500, false)
    call InventoryInitItemDB_Func('stel', "BTNStaffOfTeleportation.blp", "Teleports the Hero to the targeted allied land unit or structure.", 100, false)
    call InventoryInitItemDB_Func('evtl', "BTNTalisman.blp"         , "Causes attacks against the wearer to miss <AIev,DataA1,%>% of the time. |nDoes not stack with Evasion or Drunken Brawler.", 400, false)
    call InventoryInitItemDB_Func('lhst', "BTNLionHorn.blp"         , "Grants the Hero and friendly nearby units <AIad,DataA1> bonus armor. |nDoes not stack with Devotion Aura.", 400, false)
    call InventoryInitItemDB_Func('ward', "BTNDrum.blp"             , "Increases the attack damage of nearby friendly units by <AIcd,DataA1,%>% when worn. |nDoes not stack with Command Aura.", 500, false)
    //_______ ______
    call InventoryInitItemDB_Func('wild', "BTNAmuletOftheWild.blp"  , "Summons a Furbolg Warrior. The Furbolg lasts <AIuw,Dur1> seconds.", 750, true)
    call InventoryInitItemDB_Func('ankh', "BTNAnkh.blp"             , "Automatically brings the Hero back to life with <AIrc,DataB1> hit points when the Hero wearing the Ankh dies.", 800, true)
    call InventoryInitItemDB_Func('fgsk', "BTNBookOfTheDead.blp"    , "Summons <AIfs,DataA1> Skeleton Warriors and <AIfs,DataB1> Skeleton Archers to fight for you. |nLasts <AIfs,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('fgdg', "BTNDoomGuard.blp"        , "Summons a Doom Guard to fight for you. |nLasts <AIfu,Dur1> seconds.", 750, true)
    call InventoryInitItemDB_Func('whwd', "BTNHealingWard.blp"      , "Drops a ward that heals nearby friendly units for <Ahwd,Dur1> seconds. |nContains <whwd,uses> charges.", 600, true)
    call InventoryInitItemDB_Func('hlst', "BTNHealthStone.blp"      , "Increases the life regeneration rate of the Hero by <Arll,DataA1> hit points per second when worn. Can be consumed for <AIh2,DataA1> health.", 450, true)
    call InventoryInitItemDB_Func('shar', "BTNIceShard.blp"         , "Summons an Ice Revenant. The Ice Revenant lasts <AIir,Dur1> seconds.", 750, true)
    call InventoryInitItemDB_Func('infs', "BTNInfernalStone.blp"    , "Calls an Infernal down from the sky, dealing <AIin,DataA1> damage and stunning enemy land units for <AIin,Dur1> seconds in an area. The Infernal lasts <AIin,DataB1> seconds.", 750, true)
    call InventoryInitItemDB_Func('mnst', "BTNManaStone.blp"        , "Increases the mana regeneration rate of the Hero by <AIrn,DataA1,%>% when worn. Can be consumed for <AIm2,DataA1> mana.", 450, true)
    call InventoryInitItemDB_Func('pdiv', "BTNPotionOfDivinity.blp" , "Turns the Hero invulnerable for <AIdv,Dur1> seconds.", 600, true)
    call InventoryInitItemDB_Func('pghe', "BTNPotionGreen.blp"      , "Heals <AIh2,DataA1> hit points when used.", 400, true)
    call InventoryInitItemDB_Func('pgma', "BTNPotionBlueBig.blp"    , "Restores <AIm2,DataA1> mana when used.", 400, true)
    call InventoryInitItemDB_Func('pnvu', "BTNGreaterInvulneralbility.blp", "Makes the Hero invulnerable to damage for <AIvu,Dur1> seconds when used. An invulnerable Hero may not be the target of spells or effects.", 400, true)
    call InventoryInitItemDB_Func('pomn', "BTNPotionOfOmniscience.blp", "Reveals the entire map for <AIrv,Dur1> seconds when used.", 400, true)
    call InventoryInitItemDB_Func('pres', "BTNPotionOfRestoration.blp", "Restores <AIre,DataA1> hit points and <AIre,DataB1> mana of the Hero when used.", 600, true)
    call InventoryInitItemDB_Func('fgrd', "BTNRedDragon.blp"        , "Summons a Red Drake to fight for you. |nLasts <AIfd,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('rej3', "BTNRejuvPotion.blp"      , "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp3,DataA1> hit points and <AIp3,DataB1> mana of the Hero over <AIp3,Dur1> seconds.", 400, true)
    call InventoryInitItemDB_Func('sand', "BTNSnazzyScrollPurple.blp", "Raises <AIan,DataA1> nearby dead units to fight for <AIan,Dur1> seconds.", 750, true)
    call InventoryInitItemDB_Func('sres', "BTNScrollOfHealing.blp"  , "Restores <AIra,DataA1> hit points and <AIra,DataB1> mana of friendly non-mechanical units in an area around your Hero.", 750, true)
    call InventoryInitItemDB_Func('srrc', "BTNSnazzyScroll.blp"     , "Brings <AIrs,DataA1> of your nearby dead units back to life.", 750, true)
    call InventoryInitItemDB_Func('sror', "BTNSnazzyScrollGreen.blp", "Gives friendly nearby units a <AIrr,DataA1,%>% bonus to damage for <AIrr,Dur1> seconds.", 400, true)
    call InventoryInitItemDB_Func('wswd', "BTNSentryWard.blp"       , "Drops a Sentry Ward to spy upon an area for <AIsw,Dur1> seconds. |nContains <wswd,uses> charges.", 150, true)
    call InventoryInitItemDB_Func('fgfh', "BTNFelHound.blp"         , "Summons a Fel Stalker to fight for you. |nLasts <AIfh,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('fgrg', "BTNRockGolem.blp"        , "Summons a Rock Golem to fight for you. |nLasts <AIfr,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('totw', "BTNStone.blp"            , "This mystic stone summons a Furbolg to fight for you. |nContains <totw,uses> charges. |nLasts <AIff,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('will', "BTNWand.blp"             , "Create an illusory double of the targeted unit when used. The illusory double deals no damage to enemy units, takes <AIil,DataB1> times the damage from enemy attacks, and will disappear after <AIil,Dur1> seconds or when its hit points reach zero. |nContains <will,uses> charges.", 150, true)
    call InventoryInitItemDB_Func('wlsd', "BTNStarWand.blp"         , "Allows the Hero to cast Lightning Shield on a target unit. Lightning Shield surrounds a unit with electricity, dealing <AIls,DataA1> damage per second to nearby units. |nContains <wlsd,uses> charges. |nLasts <AIls,Dur1> seconds.", 150, true)
    call InventoryInitItemDB_Func('woms', "BTNWandOfManaSteal.blp"  , "Steals mana from a target unit and gives it to the Hero. |nContains <woms,uses> charges.", 400, true)
    call InventoryInitItemDB_Func('wshs', "BTNWandOfShadowSight.blp", "Gives the player vision of a target unit until that unit is dispelled. |nContains <wshs,uses> charges.", 150, true)
    call InventoryInitItemDB_Func('wcyc', "BTNWandOfCyclone.blp"    , "Allows the Hero to cast Cyclone. Cyclone tosses a target enemy unit into the air, rendering it unable to attack, move or cast spells. |nContains <wcyc,uses> charges. |nLasts <AIcy,Dur1> seconds.", 450, true)
    //___________
    //_________
    call InventoryInitItemDB_Func('ratf', "BTNClawsOfAttack.blp"    , "Increases the attack damage of the Hero by 15 when worn.", 800, false)
    call InventoryInitItemDB_Func('ckng', "BTNHelmutPurple.blp"     , "Increases the Strength, Intelligence, and Agility of the Hero by 5 when worn.", 1000, false)
    call InventoryInitItemDB_Func('desc', "BTNDaggerOfEscape.blp"   , "Allows the Hero to teleport a short distance.", 400, false)
    call InventoryInitItemDB_Func('modt', "BTNMaskOfDeath.blp"      , "While wearing this mask, a Hero will recover hit points equal to <AIva,DataA1,%>% of the attack damage dealt to an enemy unit.", 1000, false)
    call InventoryInitItemDB_Func('ofro', "BTNOrbOfFrost.blp"       , "Adds <AIob,DataA1> bonus cold damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air and slow the movement speed and attack rate of the enemy for <AIob,Dur1> seconds.", 800, false)
    call InventoryInitItemDB_Func('rde4', "BTNRingGreen.blp"        , "Increases the armor of the Hero by 5 when worn.", 800, false)
    call InventoryInitItemDB_Func('tkno', "BTNTomeRed.blp"          , "Increases the level of the Hero by <AIlm,DataA1> when used.", 1200, false)
    //__________ _______
    call InventoryInitItemDB_Func('pclr', "BTNPotionOfClarity.blp"  , "|cff87ceebNon-Combat Consumable|r|nRegenerates the Hero's mana by <AIpr,DataB1> over <AIpr,Dur1> seconds when used.", 160, true)
    call InventoryInitItemDB_Func('hslv', "BTNHealingSalve.blp"     , "|cff87ceebNon-Combat Consumable|r|nRegenerates a target unit's hit points by <AIrl,DataA1> over <AIrl,Dur1> seconds when used. |nContains <hslv,uses> charges.", 100, true)
    call InventoryInitItemDB_Func('tsct', "BTNHumanWatchTower.blp"  , "Creates a Scout Tower at a target location.", 30, true)
    call InventoryInitItemDB_Func('plcl', "BTNLesserClarityPotion.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates the Hero's mana by <AIpl,DataB1> over <AIpl,Dur1> seconds when used.", 70, true)
    call InventoryInitItemDB_Func('mcri', "BTNMechanicalCritter.blp", "Creates a player-controlled critter that can be used to scout enemies.", 50, true)
    call InventoryInitItemDB_Func('moon', "BTNMoonStone.blp"        , "Causes an eclipse that blocks out the sun and creates an artificial night. |nLasts <AIct,Dur1> seconds.", 50, true)
    call InventoryInitItemDB_Func('phea', "BTNPotionGreenSmall.blp" , "Heals <AIh1,DataA1> hit points when used.", 150, true)
    call InventoryInitItemDB_Func('pinv', "BTNLesserInvisibility.blp", "|cff87ceebNon-Combat Consumable|r|nRenders the Hero invisible for <AIv1,Dur1> seconds when used. An invisible Hero is untargetable by the enemy unless detected. If the Hero attacks, uses an ability, or casts a spell, the invisibility effect is lost.", 100, true)
    call InventoryInitItemDB_Func('pnvl', "BTNLesserInvulneralbility.blp", "Makes the Hero invulnerable to damage for <AIvl,Dur1> seconds when used. An invulnerable Hero may not be the target of spells or effects.", 150, true)
    call InventoryInitItemDB_Func('rnec', "BTNRodOfNecromancy.blp"  , "Creates two Skeleton Warriors from a corpse. |nContains <rnec,uses> charges.", 150, true)
    call InventoryInitItemDB_Func('pman', "BTNPotionBlueSmall.blp"  , "Restores <AIm1,DataA1> mana when used.", 200, true)
    call InventoryInitItemDB_Func('skul', "BTNSacrificialSkull.blp" , "Creates an area of Blight at a target location.", 50, true)
    call InventoryInitItemDB_Func('shea', "BTNScrollOfTownPortal.blp", "Heals <AIha,DataA1> hit points to all friendly non-mechanical units around the Hero when used.", 250, true)
    call InventoryInitItemDB_Func('sman', "BTNScrollOfProtection.blp", "Restores <AImr,DataA1> mana to all friendly units in an area around your Hero.", 150, true)
    call InventoryInitItemDB_Func('spro', "BTNScroll.blp"           , "Increases the armor of all friendly units in an area around your Hero by <AIda,DataA1> for <AIda,Dur1> seconds.", 150, true)
    call InventoryInitItemDB_Func('sreg', "BTNScrollOfRegenerationGreen.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates the hit points of all friendly non-mechanical units in an area around your Hero by <AIsl,DataA1> over <AIsl,Dur1> seconds when used.", 100, true)
    call InventoryInitItemDB_Func('shas', "BTNScrollOfHaste.blp"    , "Increases the movement speed of the Hero and nearby allied units to the maximum movement speed. |nLasts <AIsa,Dur1> seconds.", 50, true)
    call InventoryInitItemDB_Func('stwp', "BTNScrollUber.blp"       , "Teleports the Hero and any of its nearby troops to a target friendly town hall.", 350, true)
    call InventoryInitItemDB_Func('silk', "BTNSpiderSilkBroach.blp" , "Binds a target enemy air unit in webbing, forcing the target to the ground. Webbed units can be hit as though they were land units. |nContains <silk,uses> charges.", 50, true)
    call InventoryInitItemDB_Func('sneg', "BTNStaffOfNegation.blp"  , "Dispels all magical effects in a target area. |n|cffffcc00Deals <AIdi,DataB1> damage to summoned units.|r", 200, true)
    call InventoryInitItemDB_Func('ssan', "BTNStaffOfSanctuary.blp" , "Teleports a target unit to your highest level town hall, stunning the unit and regenerating <ANsa,DataE1> hit points per second. Lasts until the unit is fully healed.", 250, true)
    call InventoryInitItemDB_Func('tcas', "BTNTinyCastle.blp"       , "Creates a Castle at a target location.", 800, true)
    call InventoryInitItemDB_Func('tgrh', "BTNGreathall.blp"        , "Creates a Great Hall at a target location. Human, Night Elf, and Undead players will get their racial equivalent town hall.", 600, true)
    call InventoryInitItemDB_Func('tret', "BTNTomeOfRetraining.blp" , "Unlearns all of the Hero's spells, allowing the Hero to learn different skills.", 300, true)
    call InventoryInitItemDB_Func('vamp', "BTNPotionOfVampirism.blp", "Adds <AIpv,DataA1> bonus damage and a life-stealing attack to the Hero. |nLasts <AIpv,Dur1> seconds.", 75, true)
    call InventoryInitItemDB_Func('wneg', "BTNWandSkull.blp"        , "Dispels all magical effects in a target area. |nContains <wneg,uses> charges. |n|cffffcc00Deals <AIdi,DataB1> damage to summoned units.|r", 200, true)
    call InventoryInitItemDB_Func('wneu', "BTNWandOfNeutralization.blp", "Hurls forth a stream of neutralizing magic that bounces up to <AIdc,DataC1> times, dispelling units in its wake. |nContains <wneu,uses> charges.", 150, true)
    //______ ___ ________
    call InventoryInitItemDB_Func('kybl', "BTNBloodKey.blp"         , "This key is covered in blood.", 200, false)
    call InventoryInitItemDB_Func('ches', "BTNCheese.blp"           , "Cheese cheese cheese cheese!", 250, false)
    call InventoryInitItemDB_Func('bzbe', "BTNVialEmpty.blp"        , "A special vial adept at containing the magical healing waters of a Fountain of Life.", 200, false)
    call InventoryInitItemDB_Func('engs', "BTNEnchantedGemstone.blp", "This artifact of the Kelani Magi is said to hold the power to make constructs out of pure energy. When the Kelani fell to ruin, the Razormane Quillboars were quick to scavenge and covet these beautiful and powerful objects.", 200, false)
    call InventoryInitItemDB_Func('bzbf', "BTNVialFull.blp"         , "A special vial adept at containing the magical healing waters of a Fountain of Life.", 200, false)
    call InventoryInitItemDB_Func('gmfr', "BTNGem.blp"              , "A fragment of a gem from a powerful ring.", 200, false)
    call InventoryInitItemDB_Func('ledg', "BTNSorceressMaster.blp"  , "This Ledger looks to be full of boring facts and figures.", 200, false)
    call InventoryInitItemDB_Func('kygh', "BTNGhostKey.blp"         , "This key is rather insubstantial.", 200, false)
    call InventoryInitItemDB_Func('gopr', "BTNGlyph.blp"            , "Created by ancient druids, this glyph has the power to heal the land.", 250, false)
    call InventoryInitItemDB_Func('azhr', "BTNHeartOfAszune.blp"    , "Legends say that the imprisoned spirit of Aszune seeks out her heart to this very day.", 200, false)
    call InventoryInitItemDB_Func('cnhn', "BTNHornOfCenarius.blp"   , "This ancient relic of the Night Elves is said to hold the power to call the spirits of all Night Elves. It imbues its owner with <AIl1,DataA1> hit points, and a <Arel,DataA1> hit point per second regeneration bonus.", 200, false)
    call InventoryInitItemDB_Func('dkfw', "BTNBarrel.blp"           , "A keg filled to the brim with the strongest drink available this side of Khaz Modan!", 200, false)
    call InventoryInitItemDB_Func('k3m3', "BTN3M3.blp"              , "Cut from the sapphire Body of Enulaia, it opens the soul of the Gate Keeper.", 200, false)
    call InventoryInitItemDB_Func('mgtk', "BTNBlood&GhostKey.blp"   , "This magical chain of keys can open many doors.", 200, false)
    call InventoryInitItemDB_Func('mort', "BTNSpy.blp"              , "The letter is magically sealed. On the front, written in large scrawling letters is the word Thrall.", 200, false)
    call InventoryInitItemDB_Func('kymn', "BTNMoonKey.blp"          , "This key glows faintly.", 200, false)
    call InventoryInitItemDB_Func('k3m1', "BTN3M1.blp"              , "Cut from the emerald Eye of Jennala, it opens the mind of the Gate Keeper.", 200, false)
    call InventoryInitItemDB_Func('jpnt', "BTNScrollOfProtection.blp", "A note from Thrall, for Jaina Proudmoore.", 200, false)
    call InventoryInitItemDB_Func('k3m2', "BTN3M2.blp"              , "Cut from the amethyst Stone of Hannalee, it opens the heart of the Gate Keeper.", 200, false)
    call InventoryInitItemDB_Func('phlt', "BTNUndeadShrine.blp"     , "There is no phatter lewt than this.", 500, false)
    call InventoryInitItemDB_Func('sclp', "BTNSelectHeroOn.blp"     , "Unlocks a secret level!", 75, false)
    call InventoryInitItemDB_Func('sorf', "BTNOrbOfDarkness.blp"    , "A fragment of a powerful artifact.", 200, false)
    call InventoryInitItemDB_Func('shwd', "BTNShimmerWeed.blp"      , "Wondrous plant said to have miraculous mind-expanding properties.", 200, false)
    call InventoryInitItemDB_Func('skrt', "BTNOrbOfCorruption.blp"  , "This ancient artifact entraps the souls of those who die violently, forcing them to relive the last moments of their lives for eternity.", 250, false)
    call InventoryInitItemDB_Func('glsk', "BTNGuldanSkull.blp"      , "Once a powerful user of Demonic magics, the Demons answered his calls, and found a greater use for his head.", 200, false)
    call InventoryInitItemDB_Func('kysn', "BTNSunKey.blp"           , "This key glows brightly.", 200, false)
    call InventoryInitItemDB_Func('sehr', "BTNHeartOfSearinox.blp"  , "The still beating heart of Searinox can be used to imbue an Orb with the fiery powers of a Dragon.", 200, false)
    call InventoryInitItemDB_Func('thle', "BTNThunderLizardEgg.blp" , "This massive egg will not hatch without a parent to warm it.", 200, false)
    call InventoryInitItemDB_Func('dphe', "BTNThunderLizardEgg.blp" , "A rare egg of a Thunder Hawk.", 200, false)
    call InventoryInitItemDB_Func('dthb', "BTNManaFlareOff.blp"     , "An exotic plant well known for its unstable and dangerous properties.", 200, false)
    call InventoryInitItemDB_Func('ktrm', "BTNUrnOfKelThuzad.blp"   , "Formerly the container of King Terenas' ashes, this magically enchanted Urn was chosen by Tichondrius to preserve Kel'Thuzad's remains.", 200, false)
    call InventoryInitItemDB_Func('wtlg', "BTNWirtsLeg.blp"         , "Could it be that a portal opened up and expelled the remains of our dearest pal from the world of Diablo to here? If so, was it a player, or a Demon? Just how many worlds have the Burning Legion conquered? Could the Demons of the Burning Legion and those of Sanctuary be one and the same? The mind wobbles.", 200, false)
    call InventoryInitItemDB_Func('wolg', "BTNWirtsOtherLeg.blp"    , "Perhaps the overzealous adventurer pried this off before his journey here thinking it might give him one last opportunity at bovine slaughter. Little did he know where it would lead him.", 200, false)
    //_____
    call InventoryInitItemDB_Func('amrc', "BTNAmulet.blp"           , "Teleports <AIrt,DataA1> of the player's units within the targeted area to the location of the Hero when used.", 250, true)
    call InventoryInitItemDB_Func('axas', "BTNWitchDoctorMaster.blp", "|cffff8c00Artifact|r|nSummons <AIsh,DataB1> Troll Berserkers to fight for you. Also grants the Hero and friendly nearby units increased attack rate and movement speed.|n|cffffcc00History|r|n|cffffdeadNames of generations of Witch Doctors are carved into this staff. The wielder can call upon them for wisdom and guidance in times of peril.|r", 3000, false)
    call InventoryInitItemDB_Func('anfg', "BTNClayFigurine.blp"     , "Increases the Intelligence of the Hero by 1 when carried.", 150, false)
    call InventoryInitItemDB_Func('pams', "BTNSnazzyPotion.blp"     , "Gives the Hero immunity to magical spells for <AIxs,Dur1> seconds.", 100, true)
    call InventoryInitItemDB_Func('arsc', "BTNBansheeAdept.blp"     , "A powerful scroll that restores <AIha,DataA1> hit points, <AImr,DataA1> mana, and grants <AIda,DataA1> bonus armor to nearby friendly units.", 1000, true)
    call InventoryInitItemDB_Func('arsh', "BTNArcaniteArmor.blp"    , "Reduces damage from ranged attacks to <AIdd,DataA1,%>%. Also increases the Hero's armor by <AId5,DataA1> when worn.", 3500, false)
    call InventoryInitItemDB_Func('asbl', "BTNDaggerOfEscape.blp"   , "Adds <AItj,DataA1> bonus damage to the attack of the Hero when carried. The Hero's attacks also deal <AIsz,DataA1> damage per second, and slow the movement speed and attack rate of the enemy.", 2000, false)
    call InventoryInitItemDB_Func('btst', "BTNOrcBattleStandard.blp", "The Battle Standard of Thrall's Orcs, carry it with pride.", 1000, false)
    call InventoryInitItemDB_Func('blba', "BTNArmorGolem.blp"       , "Grants nearby units <AIad,DataA1> bonus defense. Enhances the Hero's armor by <AId7,DataA1>.", 3500, false)
    call InventoryInitItemDB_Func('bfhr', "BTNPhilosophersStone.blp", "|cff8b00ffUnique|r|nIncreases the Hero's agility by <AIaz,DataA1> when worn.", 2500, false)
    call InventoryInitItemDB_Func('brag', "BTNRingPurple.blp"       , "Increases the Agility of the Hero by 1 when worn.", 50, false)
    call InventoryInitItemDB_Func('cosl', "BTNUsedSoulGem.blp"      , "|cffff8c00Artifact|r|nBrings <AIrx,DataA1> of your nearby dead units back to life. |n|cffffcc00History|r|n|cffffdeadCrafted by the Titans as gifts to their favored creations, the Celestial Orb of Souls channels the powers of the light to bring back to life those who have recently fallen.|r", 10000, false)
    call InventoryInitItemDB_Func('rat3', "BTNClawsOfAttack.blp"    , "Increases the attack damage of the Hero by 3 when worn.", 50, false)
    call InventoryInitItemDB_Func('stpg', "BTNPenguin.blp"          , "This penguin squeak-toy was first created by the goblin tinkerer Salzhigh for the centaur. Regarding it with some awe (having never seen a penguin before) the centaur purchased them as idols and worshipped them at altars.", 450,  false)
    call InventoryInitItemDB_Func('crdt', "BTNRevenant.blp"         , "|cffff8c00Artifact|r|nGrants the ability to fire bolts of pain that deal <AIfz,DataC1> damage. Also increases the Hero's hit points by <AIlf,DataA1> and mana by <AImz,DataA1> when worn.|n|cffffcc00History|r|n|cffffdeadThe Deathlords are rumored to have been mighty Paladins once. One of their order turned from the light when he slaughtered his own family, believing they were impure.|r", 6400, false)
    call InventoryInitItemDB_Func('dtsb', "BTNSorceressMaster.blp"  , "|cffff8c00Artifact|r|nGrants the ability to portal to your home town. Also reduces spell damage by <AIsr,DataB1,%>% and increases the Hero's mana by <AImv,DataA1> while equipped.|n|cffffcc00History|r|n|cffffdeadDrek'Thar's old spellbook is filled with pages stolen from Kirin Tor mages that were slain in battle.|r", 3350, false)
    call InventoryInitItemDB_Func('drph', "BTNDust.blp"             , "Increases the Intelligence of the Hero by 1 when carried.", 50, false)
    call InventoryInitItemDB_Func('dust', "BTNDustOfAppearance.blp" , "Reveals enemy invisible units in an area around the Hero. |nContains <dust,uses> charges. |nLasts <AItb,Dur1> seconds.", 750, true)
    call InventoryInitItemDB_Func('shen', "BTNThoriumArmor.blp"     , "Increases the Hero's armor by <AId2,DataA1> and hit points by <AIlz,DataA1> when worn.", 650, false)
    call InventoryInitItemDB_Func('envl', "BTNVialFull.blp"         , "Regenerates <AIp3,DataA1> hit points and <AIp3,DataB1> mana of the Hero over <AIp3,Dur1> seconds. |nContains <envl,uses> charges.", 450, true)
    call InventoryInitItemDB_Func('esaz', "BTNHeartOfAszune.blp"    , "Legends speak of an intelligent Orc who found the Heart of Aszune. This is the essence of her heart, precious to the Night Elves. It has the power to heal the Hero that wields it. This item is permanent.", 600, false)
    call InventoryInitItemDB_Func('frhg', "BTNAdvancedUnholyStrength.blp", "Increases armor by <AId5,DataA1> and attack rate by <AIs2,DataA1,%>% when worn.", 3500, false)
    call InventoryInitItemDB_Func('fgun', "BTNFlare.blp"            , "Reveals a target area on the map. |nContains <fgun,uses> charges.", 125, false)
    call InventoryInitItemDB_Func('fwss', "BTNGrimWard.blp"         , "This ancient Frost Wyrm skull has been equipped with handles, turning it into a powerful shield. Increases armor by 2 when worn and reduces Magic damage dealt to the Hero by <AIsr,DataB1,%>%.", 750, false)
    call InventoryInitItemDB_Func('frgd', "BTNThoriumMelee.blp"     , "Adds <AIft,DataA1> bonus cold damage to the attack of a Hero and <AId5,DataA1> bonus armor when carried. The Hero's attacks also slow the movement speed and attack rate of the enemy.", 1400, false)
    call InventoryInitItemDB_Func('gemt', "BTNGem.blp"              , "Allows the Hero to detect hidden or invisible units in the Hero's line of sight when carried.", 200, false)
    call InventoryInitItemDB_Func('gvsm', "BTNSpellSteal.blp"       , "|cff8b00ffUnique|r|nGrants the ability to control summoned units. Also increases the intelligence of the Hero by <AIa6,DataA1> when worn.", 1400, false)
    call InventoryInitItemDB_Func('gobm', "BTNGoblinLandMine.blp"   , "Places a hidden land mine at a target point. Enemy units that move near the land mine will activate the mine, destroying the mine and causing area of effect damage to nearby units. |nContains <gobm,uses> charges.", 225, true)
    call InventoryInitItemDB_Func('tels', "BTNTelescope.blp"        , "Provides an increase to the Hero's line of sight radius at night when carried.", 200, false)
    call InventoryInitItemDB_Func('rej4', "BTNGreaterRejuvPotion.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp4,DataA1> hit points and <AIp4,DataB1> mana of the Hero over <AIp4,Dur1> seconds.", 450, true)
    call InventoryInitItemDB_Func('rej6', "BTNGreaterRejuvScroll.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp6,DataA1> hit points and <AIp6,DataB1> mana of the Hero and nearby friendly units over <AIp6,Dur1> seconds.", 500, true)
    call InventoryInitItemDB_Func('grsl', "BTNNecromancerMaster.blp", "|cff87ceebUnique Consumable|r|nThis powerful book permanently increases the hit points of the Hero by <AIpx,DataA1> each time it is used. |nContains <grsl,uses> charges.", 1350, true)
    call InventoryInitItemDB_Func('hbth', "BTNUnholyAura.blp"       , "|cff8b00ffUnique|r|nGrants the ability to go Berserk, causing the Hero to attack <AIxk,DataB1,%>% faster but take <AIxk,DataC1,%>% more damage. Also increases strength and agility by 4 when worn.", 4200, false)
    call InventoryInitItemDB_Func('sfog', "BTNHornOfFog.blp"        , "Allows the Hero to channel the Cloud ability, which stops an area of enemy towers from attacking for <AIfg,Dur1> seconds.", 200, false)
    call InventoryInitItemDB_Func('flag', "BTNHumanCaptureFlag.blp" , "An object that is often captured in special scenarios as a win condition.", 1000, false)
    call InventoryInitItemDB_Func('iwbr', "BTNNatureTouchGrow.blp"  , "Increases the Strength of the Hero by 1 when carried.", 50, false)
    call InventoryInitItemDB_Func('jdrn', "BTNRingJadeFalcon.blp"   , "Increases the Agility of the Hero by 1 when worn.", 50, false)
    call InventoryInitItemDB_Func('kgal', "BTNBarrel.blp"           , "Increases hit point and mana regeneration.", 850, false)
    call InventoryInitItemDB_Func('klmm', "BTNSpiritWalkerAdeptTraining.blp", "|cffff8c00Artifact|r|nIncreases the attack damage of the Hero by <AItx,DataA1> when carried. Also causes the Hero's attacks to steal life.|n|cffffcc00History|r|n|cffffdeadWhen Dethorin found his lady, Allurana, in the arms of another, he went to the Barrens and cried out. An axe burst forth from the sands as if in answer. Dethorin slew Allurana and her lover, then hurled the axe with all his might into the deep dark sea.|r", 7500, false)
    call InventoryInitItemDB_Func('rej2', "BTNLesserRejuvPotion.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp2,DataA1> hit points and <AIp2,DataB1> mana of the Hero over <AIp2,Dur1> seconds.", 150, true)
    call InventoryInitItemDB_Func('rej5', "BTNLesserRejuvScroll.blp", "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp5,DataA1> hit points and <AIp5,DataB1> mana of the Hero and nearby friendly units over <AIp5,Dur1> seconds.", 400, true)
    call InventoryInitItemDB_Func('lnrn', "BTNRingLionHead.blp"     , "Increases the Agility of the Hero by 1 when worn.", 50, false)
    call InventoryInitItemDB_Func('mlst', "BTNHammer.blp"           , "Increases the Strength of the Hero by 1 when carried.", 50, false)
    call InventoryInitItemDB_Func('mnsf', "BTNBrilliance.blp"       , "Increases the mana of the Hero by <AI2m,DataA1>. Also grants the Hero and friendly nearby units a bonus to mana regeneration.", 1800, false)
    call InventoryInitItemDB_Func('rej1', "BTNMinorRejuvPotion.blp" , "|cff87ceebNon-Combat Consumable|r|nRegenerates <AIp1,DataA1> hit points and <AIp1,DataB1> mana of the Hero over <AIp1,Dur1> seconds.", 100, true)
    call InventoryInitItemDB_Func('lure', "BTNMonsterLure.blp"      , "Creates a ward that draws nearby creeps to it.", 200, true)
    call InventoryInitItemDB_Func('nspi', "BTNNecklace.blp"         , "Renders the Hero invulnerable to magic.", 1000, false)
    call InventoryInitItemDB_Func('nflg', "BTNNightElfCaptureFlag.blp", "An object that is often captured in special scenarios as a win condition.", 1000, false)
    call InventoryInitItemDB_Func('ocor', "BTNOrbOfCorruption.blp"  , "Adds <AIcb,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air and reduce the armor of enemy units for <AIcb,Dur1> seconds.", 400, false)
    call InventoryInitItemDB_Func('ofir', "BTNOrbOfFire.blp"        , "Adds <AIfb,DataA1> bonus fire damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air and do splash damage to nearby enemy units.", 400, false)
    call InventoryInitItemDB_Func('gldo', "BTNUsedSoulGem.blp"      , "Adds <AIgd,DataA1> bonus fire damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air and do splash damage to nearby enemy units.", 450, false)
    call InventoryInitItemDB_Func('olig', "BTNOrbOfLightning.blp"   , "Adds <AIlb,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air, dispel magic and slow the movement speed of the enemy for <AIlp,Dur1> seconds. |n|cffffcc00Deals <AIlp,DataC1> bonus damage to summoned units.", 450, false)
    call InventoryInitItemDB_Func('oli2', "BTNOrbOfLightning.blp"   , "Adds <AIll,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air, and have a chance to dispel magic and slow the movement speed of the enemy for <AIlp,Dur1> seconds. |n|cffffcc00Deals <AIpg,DataC1> bonus damage to summoned units.", 400, false)
    call InventoryInitItemDB_Func('oslo', "BTNOrbofSlowness.blp"    , "Adds <AIsb,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air, and have a chance to slow a target enemy unit's movement speed by <AIos,DataA1,%>% and attack rate by <AIos,DataB1,%>% for <AIos,Dur1> seconds.", 550, false)
    call InventoryInitItemDB_Func('oven', "BTNOrbOfVenom.blp"       , "Adds <AIpb,DataA1> bonus damage to the attack of a Hero when carried. The Hero's attacks also become ranged when attacking air and poison enemy units for <Apo2,Dur1> seconds.", 400, false)
    call InventoryInitItemDB_Func('oflg', "BTNOrcCaptureFlag.blp"   , "An object that is often captured in special scenarios as a win condition.", 1000, false)
    call InventoryInitItemDB_Func('pgin', "BTNGreaterInvisibility.blp", "|cff87ceebNon-Combat Consumable|r|nRenders the Hero invisible for <AIv2,Dur1> seconds when used. An invisible Hero is untargetable by the enemy unless detected. If the Hero attacks, uses an ability, or casts a spell, the invisibility effect is lost.", 200, true)
    call InventoryInitItemDB_Func('pspd', "BTNPotionRed.blp"        , "Increases the movement speed of the Hero by <AIsp,DataA1,%>% for <AIsp,Dur1> seconds.", 75, true)
    call InventoryInitItemDB_Func('rde0', "BTNRingGreen.blp"        , "Increases the armor of the Hero by 1 when worn.", 50, false)
    call InventoryInitItemDB_Func('rnsp', "BTNGoldRing.blp"         , "Increases the Strength, Agility and Intelligence of the Hero by 1 when worn.", 100, false)
    call InventoryInitItemDB_Func('ram1', "BTNRingJadeFalcon.blp"   , "A powerful artifact with a sliver of a fragmented gem inset. Increases the Strength, Agility and Intelligence of the Hero by 1.", 125, false)
    call InventoryInitItemDB_Func('ram4', "BTNRingJadeFalcon.blp"   , "A powerful artifact with a wondrous gem inset. Increases the Strength, Agility and Intelligence of the Hero by 3 and gives nearby friendly units a bonus to mana regeneration.", 750, false)
    call InventoryInitItemDB_Func('ram2', "BTNRingJadeFalcon.blp"   , "A powerful artifact with a part of a fragmented gem inset. Increases the Strength, Agility and Intelligence of the Hero by 2.", 300, false)
    call InventoryInitItemDB_Func('ram3', "BTNRingJadeFalcon.blp"   , "A powerful artifact with most of a fragmented gem inset. Increases the Strength, Agility and Intelligence of the Hero by 3.", 550, false)
    call InventoryInitItemDB_Func('rugt', "BTNImprovedUnholyStrength.blp", "Increases the strength and armor of the Hero by 3 when worn.", 725, false)
    call InventoryInitItemDB_Func('rump', "BTNGatherGold.blp"       , "This heavy pick can be swung with force. Increases the Hero's attack damage by <AItg,DataA1> and gives a <AIbx,DataA1>% chance to stun the enemy.", 100, false)
    call InventoryInitItemDB_Func('horl', "BTNGlyph.blp"            , "A powerful artifact, sacred to the orc shaman. |nGrants the Hero and friendly nearby units increased attack rate and movement speed. |nDoes not stack with Endurance Aura.", 950, false)
    call InventoryInitItemDB_Func('schl', "BTNPriestAdept.blp"      , "Grants the ability to heal a friendly unit. Also grants the Hero and friendly nearby units <AIgx,DataA1,%>% increased hit point regeneration.", 4200, false)
    call InventoryInitItemDB_Func('ccmd', "BTNScepterOfMastery.blp" , "Transfers control of the targeted non-Hero unit to the player who uses the Scepter. The transfer of control is permanent. |nCannot be used on Heroes or on creeps higher than level <AIco,DataA1>. |nContains <ccmd,uses> charges.", 1000, true)
    call InventoryInitItemDB_Func('rots', "BTNWitchDoctorAdept.blp" , "|cff87ceebUnique Consumable|r|nSummons <AIwm,DataA1> Murlocs to fight for you. Also increases the Hero's strength, agility, and intelligence by 2. |nContains <rots,uses> charges.", 1000, true)
    call InventoryInitItemDB_Func('scul', "BTNBansheeMaster.blp"    , "Animates <AIan,DataA1> nearby corpses to fight for you. Lasts <AIan,Dur1> seconds.", 950, true)
    call InventoryInitItemDB_Func('srbd', "BTNArcaniteMelee.blp"    , "Adds <AIfw,DataA1> bonus fire damage to the attack of a Hero when carried. The Hero's attacks also do splash damage to nearby enemy units, and have a <AIcs,DataA1>% chance to deal <AIcs,DataB1> times their normal damage.", 1650, false)
    call InventoryInitItemDB_Func('srtl', "BTNOrcMeleeUpThree.blp"  , "|cffff8c00Artifact|r|nIncreases the attack rate of the Hero by <AIsx,DataA1,%>% and attack damage by <AItf,DataA1>.|n|cffffcc00History|r|n|cffffdeadThis weapon was crafted on Draenor for Kash'drakor and used in the Blood River war that ended with the annihilation of the Dark Scar clan. Nazgrel is the last living relative of Kash'drakor.|r", 5500, false)
    call InventoryInitItemDB_Func('sor1', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 1.", 50, false)
    call InventoryInitItemDB_Func('sora', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 10, armor by 3 and grants enhanced hit point regeneration.", 1250, false)
    call InventoryInitItemDB_Func('sor2', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 2.", 100, false)
    call InventoryInitItemDB_Func('sor3', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 3.", 200, false)
    call InventoryInitItemDB_Func('sor4', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 4 and armor by 1.", 300, false)
    call InventoryInitItemDB_Func('sor5', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 5 and armor by 1.", 350, false)
    call InventoryInitItemDB_Func('sor6', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 6 and armor by 1.", 400, false)
    call InventoryInitItemDB_Func('sor7', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 7 and armor by 2.", 550, false)
    call InventoryInitItemDB_Func('sor8', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 8 and armor by 2.", 700, false)
    call InventoryInitItemDB_Func('sor9', "BTNOrbOfDarkness.blp"    , "This artifact was imbued with special powers by the Orc Shadow Council. It increases your attack damage by 9, armor by 2 and grants enhanced hit point regeneration.", 900, false)
    call InventoryInitItemDB_Func('shcw', "BTNShamanMaster.blp"     , "|cff8b00ffUnique|r|nThese are given to shaman upon the completion of their training. Increases attack damage by <AIlx,DataA1>. The Hero's attacks also have a chance to dispel magic and slow the movement speed of the enemy for <AIpg,Dur1> seconds.", 950, false)
    call InventoryInitItemDB_Func('shtm', "BTNEntrapmentWard.blp"   , "This powerful Orc artifact channels Shamanic powers through its user, allowing them to cast Purge.", 600, false)
    call InventoryInitItemDB_Func('shhn', "BTNHumanArmorUpThree.blp", "|cff8b00ffUnique|r|nGrants nearby friendly units a <AIcd,DataA1,%>% bonus to attack damage. Also increases the armor of the Hero by <AId8,DataA1> when worn.", 3350, false)
    call InventoryInitItemDB_Func('shdt', "BTNLightningShield.blp"  , "|cffff8c00Artifact|r|nEngulfs the Hero in fire which deals <AIcf,DataA1> damage per second to nearby enemy land units. Also increases the Hero's armor by <AId0,DataA1>, hit points by <AIlf,DataA1>, and mana by <AImz,DataA1> when worn. |n|cffffcc00History|r|n|cffffdeadWhen Arthas took up the sword against his own people in Stratholme, the Deathlords committed the same heinous act in many other cities across Lordaeron.|r", 9000, false)
    call InventoryInitItemDB_Func('shrs', "BTNMonsterLure.blp"      , "A tasty roast with a shimmerweed base. Heals <AIhx,DataA1> hit points when eaten. |nContains <shrs,uses> charges.", 150, true)
    call InventoryInitItemDB_Func('sksh', "BTNGrimWard.blp"         , "Increases the Strength of the Hero by 1 when carried.", 200, false)
    call InventoryInitItemDB_Func('soul', "BTNUsedSoulGem.blp"      , "A soul, trapped by the enchantments of a Soul Gem.", 1000, false)
    call InventoryInitItemDB_Func('gsou', "BTNSoulGem.blp"          , "Traps the targeted enemy Hero inside the Soul Gem when used. The enemy Hero is returned to play when the bearer of the Soul Gem is killed. While an enemy Hero is trapped, the bearer of the Soul Gem is revealed to the enemy through the Fog of War.", 1000, true)
    call InventoryInitItemDB_Func('sbok', "BTNSpellBookBLS.blp"     , "A book full of random spells.", 325, false)
    call InventoryInitItemDB_Func('sprn', "BTNRingVioletSpider.blp" , "Increases the Agility of the Hero by 1 when worn.", 50, false)
    call InventoryInitItemDB_Func('spre', "BTNStaffOfPreservation.blp", "Teleports a target friendly unit to its highest level town hall.", 200, false)
    call InventoryInitItemDB_Func('stre', "BTNWandSkull.blp"        , "Animates a nearby corpse to fight your enemies. Lasts <AInd,Dur1> seconds.", 200, false)
    call InventoryInitItemDB_Func('stwa', "BTNOrcMeleeUpOne.blp"    , "Increases the attack damage of the Hero by <AItj,DataA1> when carried.", 600, false)
    call InventoryInitItemDB_Func('thdm', "BTNEnchantedGemstone.blp", "|cff8b00ffUnique|r|nCasts bolts of lightning that deal damage to multiple targets.", 1190, false)
    call InventoryInitItemDB_Func('tbak', "BTNAltarOfKings.blp"     , "Creates a Altar of Kings at a target location.", 180, true)
    call InventoryInitItemDB_Func('tbar', "BTNHumanBarracks.blp"    , "Creates a Barracks at a target location.", 160, true)
    call InventoryInitItemDB_Func('tbsm', "BTNBlacksmith.blp"       , "Creates a Blacksmith at a target location.", 200, true)
    call InventoryInitItemDB_Func('tfar', "BTNFarm.blp"             , "Creates a Farm at a target location.", 75, true)
    call InventoryInitItemDB_Func('tlum', "BTNHumanLumberMill.blp"  , "Creates a Lumber Mill at a target location.", 150, true)
    call InventoryInitItemDB_Func('tgxp', "BTNManual3.blp"          , "Gives the Hero <AIe2,DataA1> bonus experience points when used.", 1000, true)
    call InventoryInitItemDB_Func('tmsc', "BTNNecromancerAdept.blp" , "|cff8b00ffUnique|r|nGrants the ability to sacrifice a friendly non-Hero unit to restore hit points. Also increases the Hero's mana by <AImz,DataA1> while equipped.", 1250, false)
    call InventoryInitItemDB_Func('tmmt', "BTNEntrapmentWard.blp"   , "Increases the Strength of the Hero by 1 when carried.", 50, false)
    call InventoryInitItemDB_Func('uflg', "BTNUndeadCaptureFlag.blp", "An object that is often captured in special scenarios as a win condition.", 1000, false)
    call InventoryInitItemDB_Func('vddl', "BTNShadowPact.blp"       , "Increases the Intelligence of the Hero by 1 when carried.", 50, false)
    //---------------------------------------------------------------------------------------------------+
    //                                                                                                   |
    //                                               Dest                                                |
    //                                                                                                   |
    //---------------------------------------------------------------------------------------------------+
    //________
    call InventoryInitDestDB_Func('D000', "BTNSpellShieldAmulet.blp")
    call InventoryInitDestDB_Func('D001', "BTNAmuletOftheWild.blp")
    call InventoryInitDestDB_Func('D002', "BTNPendantOfMana.blp")
    call InventoryInitDestDB_Func('D003', "BTNAmulet.blp")
    call InventoryInitDestDB_Func('D004', "BTNPendantOfEnergy.blp")
    call InventoryInitDestDB_Func('D005', "BTNSnazzyPotion.blp")
    call InventoryInitDestDB_Func('D006', "BTNArcaniteArmor.blp")
    call InventoryInitDestDB_Func('D007', "BTNOrcMeleeUpOne.blp")
    call InventoryInitDestDB_Func('D008', "BTNDrum.blp")
    call InventoryInitDestDB_Func('D009', "BTNClawsOfAttack.blp")
    call InventoryInitDestDB_Func('D00A', "BTNBarrel.blp")
    call InventoryInitDestDB_Func('D00B', "BTNRingPurple.blp")
    call InventoryInitDestDB_Func('D00C', "BTNCirclet.blp")
    call InventoryInitDestDB_Func('D00D', "BTNNatureTouchGrow.blp")
    call InventoryInitDestDB_Func('D00E', "BTNBlood&GhostKey.blp")
    call InventoryInitDestDB_Func('D00F', "BTNWirtsOtherLeg.blp")
    call InventoryInitDestDB_Func('D00G', "BTNBundleOfLumber.blp")
    call InventoryInitDestDB_Func('D00H', "BTNGoblinLandMine.blp")
    call InventoryInitDestDB_Func('D00I', "BTNTelescope.blp")
    call InventoryInitDestDB_Func('D00J', "BTNOrcMeleeUpThree.blp")
    call InventoryInitDestDB_Func('D00K', "BTNSpy.blp")
    call InventoryInitDestDB_Func('D00L', "BTNArmorGolem.blp")
    call InventoryInitDestDB_Func('D00M', "BTNClayFigurine.blp")
    call InventoryInitDestDB_Func('D00N', "BTNHealingWard.blp")
    call InventoryInitDestDB_Func('D00O', "BTNUsedSoulGem.blp")
    call InventoryInitDestDB_Func('D00P', "BTNWandOfCyclone.blp")
    call InventoryInitDestDB_Func('D00Q', "BTNWand.blp")
    call InventoryInitDestDB_Func('D00R', "BTNPriestAdept.blp")
    call InventoryInitDestDB_Func('D00S', "BTNStarWand.blp")
    call InventoryInitDestDB_Func('D00T', "BTNWitchDoctorAdept.blp")
    call InventoryInitDestDB_Func('D00U', "BTNWandSkull.blp")
    call InventoryInitDestDB_Func('D00V', "BTNRodOfNecromancy.blp")
    call InventoryInitDestDB_Func('D00W', "BTNWandOfManaSteal.blp")
    call InventoryInitDestDB_Func('D00X', "BTNWandOfNeutralization.blp")
    call InventoryInitDestDB_Func('D00Y', "BTNWandOfShadowSight.blp")
    call InventoryInitDestDB_Func('D00Z', "BTNMechanicalCritter.blp")
    call InventoryInitDestDB_Func('D010', "BTNPenguin.blp")
    call InventoryInitDestDB_Func('D011', "BTNSacrificialSkull.blp")
    call InventoryInitDestDB_Func('D012', "BTNEnchantedGemstone.blp")
    call InventoryInitDestDB_Func('D013', "BTNThoriumArmor.blp")
    call InventoryInitDestDB_Func('D014', "BTNShimmerWeed.blp")
    call InventoryInitDestDB_Func('D015', "BTNPotionOfVampirism.blp")
    call InventoryInitDestDB_Func('D016', "BTNPotionOfOmniscience.blp")
    call InventoryInitDestDB_Func('D017', "BTNPotionBlueSmall.blp")
    call InventoryInitDestDB_Func('D018', "BTNLesserInvisibility.blp")
    call InventoryInitDestDB_Func('D019', "BTNGreaterInvulneralbility.blp")
    call InventoryInitDestDB_Func('D01A', "BTNRejuvPotion.blp")
    call InventoryInitDestDB_Func('D01B', "BTNPotionOfClarity.blp")
    call InventoryInitDestDB_Func('D01C', "BTNPotionRed.blp")
    call InventoryInitDestDB_Func('D01D', "BTNGrimWard.blp")
    call InventoryInitDestDB_Func('D01E', "BTNGlyph.blp")
    call InventoryInitDestDB_Func('D01F', "BTNHumanCaptureFlag.blp")
    call InventoryInitDestDB_Func('D01G', "BTNUndeadCaptureFlag.blp")
    call InventoryInitDestDB_Func('D01H', "BTNNightElfCaptureFlag.blp")
    call InventoryInitDestDB_Func('D01I', "BTNOrcBattleStandard.blp")
    call InventoryInitDestDB_Func('D01J', "BTNOrcCaptureFlag.blp")
    call InventoryInitDestDB_Func('D01K', "BTNHumanWatchTower.blp")
    call InventoryInitDestDB_Func('D01L', "BTNBlacksmith.blp")
    call InventoryInitDestDB_Func('D01M', "BTNHumanLumberMill.blp")
    call InventoryInitDestDB_Func('D01N', "BTNFarm.blp")
    call InventoryInitDestDB_Func('D01O', "BTNHumanBarracks.blp")
    call InventoryInitDestDB_Func('D01P', "BTNAltarOfKings.blp")
    call InventoryInitDestDB_Func('D01Q', "BTNTinyCastle.blp")
    call InventoryInitDestDB_Func('D01R', "BTNRockGolem.blp")
    call InventoryInitDestDB_Func('D01S', "BTNSoulGem.blp")
    call InventoryInitDestDB_Func('D01T', "BTNPeriapt1.blp")
    call InventoryInitDestDB_Func('D01U', "BTNMoonStone.blp")
    call InventoryInitDestDB_Func('D01V', "BTNHealthStone.blp")
    call InventoryInitDestDB_Func('D01W', "BTNDarkSummoning.blp")
    call InventoryInitDestDB_Func('D01X', "BTNManaStone.blp")
    call InventoryInitDestDB_Func('D01Y', "BTNInfernalStone.blp")
    call InventoryInitDestDB_Func('D01Z', "BTNGem.blp")
    call InventoryInitDestDB_Func('D020', "BTNHoodOfCunning.blp")
    call InventoryInitDestDB_Func('D021', "BTNBoots.blp")
    call InventoryInitDestDB_Func('D022', "BTNDaggerOfEscape.blp")
    call InventoryInitDestDB_Func('D023', "BTN3M3.blp")
    call InventoryInitDestDB_Func('D024', "BTNGhostKey.blp")
    call InventoryInitDestDB_Func('D025', "BTNNecromancerAdept.blp")
    call InventoryInitDestDB_Func('D026', "BTNTomeOfRetraining.blp")
    call InventoryInitDestDB_Func('D027', "BTNNecromancerMaster.blp")
    call InventoryInitDestDB_Func('D028', "BTNTomeRed.blp")
    call InventoryInitDestDB_Func('D029', "BTNTome.blp")
    call InventoryInitDestDB_Func('D02A', "BTNBookOfTheDead.blp")
    call InventoryInitDestDB_Func('D02B', "BTNTomeBrown.blp")
    call InventoryInitDestDB_Func('D02C', "BTNSpellBookBLS.blp")
    call InventoryInitDestDB_Func('D02D', "BTNShamanMaster.blp")
    call InventoryInitDestDB_Func('D02E', "BTNRingJadeFalcon.blp")
    call InventoryInitDestDB_Func('D02F', "BTNRingGreen.blp")
    call InventoryInitDestDB_Func('D02G', "BTNRingLionHead.blp")
    call InventoryInitDestDB_Func('D02H', "BTNRingVioletSpider.blp")
    call InventoryInitDestDB_Func('D02I', "BTNGoldRing.blp")
    call InventoryInitDestDB_Func('D02J', "BTNRingSkull.blp")
    call InventoryInitDestDB_Func('D02K', "BTNManaFlareOff.blp")
    call InventoryInitDestDB_Func('D02L', "BTNHelmutPurple.blp")
    call InventoryInitDestDB_Func('D02M', "BTNRevenant.blp")
    call InventoryInitDestDB_Func('D02N', "BTNBoneChimes.blp")
    call InventoryInitDestDB_Func('D02O', "BTNAnkh.blp")
    call InventoryInitDestDB_Func('D02P', "BTNSpiritWalkerAdeptTraining.blp")
    call InventoryInitDestDB_Func('D02Q', "BTNBloodKey.blp")
    call InventoryInitDestDB_Func('D02R', "BTNShadowPact.blp")
    call InventoryInitDestDB_Func('D02S', "BTNThoriumMelee.blp")
    call InventoryInitDestDB_Func('D02T', "BTNPotionGreenSmall.blp")
    call InventoryInitDestDB_Func('D02U', "BTNHealingSalve.blp")
    call InventoryInitDestDB_Func('D02V', "BTNMoonKey.blp")
    call InventoryInitDestDB_Func('D02W', "BTN3M1.blp")
    call InventoryInitDestDB_Func('D02X', "BTNMantleOfIntelligence.blp")
    call InventoryInitDestDB_Func('D02Y', "BTNSobiMask.blp")
    call InventoryInitDestDB_Func('D02Z', "BTNMaskOfDeath.blp")
    call InventoryInitDestDB_Func('D030', "BTNMedalionOfCourage.blp")
    call InventoryInitDestDB_Func('D031', "BTNManual.blp")
    call InventoryInitDestDB_Func('D032', "BTNDust.blp")
    call InventoryInitDestDB_Func('D033', "BTNHammer.blp")
    call InventoryInitDestDB_Func('D034', "BTNSpiderSilkBroach.blp")
    call InventoryInitDestDB_Func('D035', "BTNMinorRejuvPotion.blp")
    call InventoryInitDestDB_Func('D036', "BTNSentryWard.blp")
    call InventoryInitDestDB_Func('D037', "BTNWirtsLeg.blp")
    call InventoryInitDestDB_Func('D038', "BTNArcaniteMelee.blp")
    call InventoryInitDestDB_Func('D039', "BTNCloakOfFlames.blp")
    call InventoryInitDestDB_Func('D03A', "BTNRobeOfTheMagi.blp")
    call InventoryInitDestDB_Func('D03B', "BTNNecklace.blp")
    call InventoryInitDestDB_Func('D03C', "BTNUndeadShrine.blp")
    call InventoryInitDestDB_Func('D03D', "BTNFlare.blp")
    call InventoryInitDestDB_Func('D03E', "BTNIceShard.blp")
    call InventoryInitDestDB_Func('D03F', "BTNLesserInvulneralbility.blp")
    call InventoryInitDestDB_Func('D03G', "BTNLesserRejuvPotion.blp")
    call InventoryInitDestDB_Func('D03H', "BTNLesserClarityPotion.blp")
    call InventoryInitDestDB_Func('D03I', "BTNFelHound.blp")
    call InventoryInitDestDB_Func('D03J', "BTNAdvancedUnholyStrength.blp")
    call InventoryInitDestDB_Func('D03K', "BTNGlove.blp")
    call InventoryInitDestDB_Func('D03L', "BTNSpellSteal.blp")
    call InventoryInitDestDB_Func('D03M', "BTNCloak.blp")
    call InventoryInitDestDB_Func('D03N', "BTNVialFull.blp")
    call InventoryInitDestDB_Func('D03O', "BTNDustOfAppearance.blp")
    call InventoryInitDestDB_Func('D03P', "BTNStaffOfSilence.blp")
    call InventoryInitDestDB_Func('D03Q', "BTNStaffOfPreservation.blp")
    call InventoryInitDestDB_Func('D03R', "BTNBrilliance.blp")
    call InventoryInitDestDB_Func('D03S', "BTNStaffOfNegation.blp")
    call InventoryInitDestDB_Func('D03T', "BTNWitchDoctorMaster.blp")
    call InventoryInitDestDB_Func('D03U', "BTNStaffOfSanctuary.blp")
    call InventoryInitDestDB_Func('D03V', "BTNStaffOfTeleportation.blp")
    call InventoryInitDestDB_Func('D03W', "BTNBelt.blp")
    call InventoryInitDestDB_Func('D03X', "BTNMonsterLure.blp")
    call InventoryInitDestDB_Func('D03Y', "BTNVialEmpty.blp")
    call InventoryInitDestDB_Func('D03Z', "BTNGatherGold.blp")
    call InventoryInitDestDB_Func('D040', "BTNLionHorn.blp")
    call InventoryInitDestDB_Func('D041', "BTNHornOfCenarius.blp")
    call InventoryInitDestDB_Func('D042', "BTNHornOfFog.blp")
    call InventoryInitDestDB_Func('D043', "BTNGauntletsOfOgrePower.blp")
    call InventoryInitDestDB_Func('D044', "BTNRune.blp")
    call InventoryInitDestDB_Func('D045', "BTNRunedBracers.blp")
    call InventoryInitDestDB_Func('D046', "BTNImprovedUnholyStrength.blp")
    call InventoryInitDestDB_Func('D047', "BTNBootsOfSpeed.blp")
    call InventoryInitDestDB_Func('D048', "BTNScrollUber.blp")
    call InventoryInitDestDB_Func('D049', "BTNScrollOfHealing.blp")
    call InventoryInitDestDB_Func('D04A', "BTNSnazzyScroll.blp")
    call InventoryInitDestDB_Func('D04B', "BTNSnazzyScrollPurple.blp")
    call InventoryInitDestDB_Func('D04C', "BTNScroll.blp")
    call InventoryInitDestDB_Func('D04D', "BTNSnazzyScrollGreen.blp")
    call InventoryInitDestDB_Func('D04E', "BTNScrollOfTownPortal.blp")
    call InventoryInitDestDB_Func('D04F', "BTNScrollOfProtection.blp")
    call InventoryInitDestDB_Func('D04G', "BTNGreaterRejuvScroll.blp")
    call InventoryInitDestDB_Func('D04H', "BTNLesserRejuvScroll.blp")
    call InventoryInitDestDB_Func('D04I', "BTNBansheeMaster.blp")
    call InventoryInitDestDB_Func('D04J', "BTNScrollOfRegenerationGreen.blp")
    call InventoryInitDestDB_Func('D04K', "BTNBansheeAdept.blp")
    call InventoryInitDestDB_Func('D04L', "BTNScrollOfHaste.blp")
    call InventoryInitDestDB_Func('D04M', "BTNSelectHeroOn.blp")
    call InventoryInitDestDB_Func('D04N', "BTNHeartOfAszune.blp")
    call InventoryInitDestDB_Func('D04O', "BTNHeartOfSearinox.blp")
    call InventoryInitDestDB_Func('D04P', "BTNPhilosophersStone.blp")
    call InventoryInitDestDB_Func('D04Q', "BTNScepterOfMastery.blp")
    call InventoryInitDestDB_Func('D04R', "BTNSunKey.blp")
    call InventoryInitDestDB_Func('D04S', "BTNDoomGuard.blp")
    call InventoryInitDestDB_Func('D04T', "BTNOrbofSlowness.blp")
    call InventoryInitDestDB_Func('D04U', "BTNOrbOfFrost.blp")
    call InventoryInitDestDB_Func('D04V', "BTNOrbOfLightning.blp")
    call InventoryInitDestDB_Func('D04W', "BTNOrbOfFire.blp")
    call InventoryInitDestDB_Func('D04X', "BTNOrbOfCorruption.blp")
    call InventoryInitDestDB_Func('D04Y', "BTNOrbOfDarkness.blp")
    call InventoryInitDestDB_Func('D04Z', "BTNOrbOfVenom.blp")
    call InventoryInitDestDB_Func('D050', "BTNChestOfGold.blp")
    call InventoryInitDestDB_Func('D051', "BTNCheese.blp")
    call InventoryInitDestDB_Func('D052', "BTNTalisman.blp")
    call InventoryInitDestDB_Func('D053', "BTNPeriapt.blp")
    call InventoryInitDestDB_Func('D054', "BTNStone.blp")
    call InventoryInitDestDB_Func('D055', "BTNEntrapmentWard.blp")
    call InventoryInitDestDB_Func('D056', "BTNHornOfDoom.blp")
    call InventoryInitDestDB_Func('D057', "BTNSlippersOfAgility.blp")
    call InventoryInitDestDB_Func('D058', "BTNUrnOfKelThuzad.blp")
    call InventoryInitDestDB_Func('D059', "BTNSorceressMaster.blp")
    call InventoryInitDestDB_Func('D05A', "BTNAlleriaFlute.blp")
    call InventoryInitDestDB_Func('D05B', "BTNPipeOfInsight.blp")
    call InventoryInitDestDB_Func('D05C', "BTNCrystalBall.blp")
    call InventoryInitDestDB_Func('D05D', "BTNPotionGreen.blp")
    call InventoryInitDestDB_Func('D05E', "BTN3M2.blp")
    call InventoryInitDestDB_Func('D05F', "BTNJanggo.blp")
    call InventoryInitDestDB_Func('D05G', "BTNGuldanSkull.blp")
    call InventoryInitDestDB_Func('D05H', "BTNGreathall.blp")
    call InventoryInitDestDB_Func('D05I', "BTNHelmOfValor.blp")
    call InventoryInitDestDB_Func('D05J', "BTNUnholyAura.blp")
    call InventoryInitDestDB_Func('D05K', "BTNHumanArmorUpThree.blp")
    call InventoryInitDestDB_Func('D05L', "BTNLightningShield.blp")
    call InventoryInitDestDB_Func('D05M', "BTNPotionOfRestoration.blp")
    call InventoryInitDestDB_Func('D05N', "BTNPotionBlueBig.blp")
    call InventoryInitDestDB_Func('D05O', "BTNGreaterInvisibility.blp")
    call InventoryInitDestDB_Func('D05P', "BTNPotionOfDivinity.blp")
    call InventoryInitDestDB_Func('D05Q', "BTNGreaterRejuvPotion.blp")
    call InventoryInitDestDB_Func('D05R', "BTNManual3.blp")
    call InventoryInitDestDB_Func('D05S', "BTNThunderLizardEgg.blp")
    call InventoryInitDestDB_Func('D05T', "BTNRedDragon.blp")
endfunction

endlibrary
library Example initializer CreateInterfaces requires StartGame
   
    globals
        // configuration
        private constant real BACKGROUND_X = 0.32
        private constant real BACKGROUND_Y = 0.32
        private constant real BACKGROUND_W = 140.
        private constant real BACKGROUND_H = 70.
        private constant real BACKGROUND_SIZE = 0.26
        // end config
       
        BasicWindow array PlayerWindow
       
        private UIPicture array Background
        private UIButton array Buttons[15][20]
        private UIButton array DoubleClickButton
        private UIText array Text
       
        private constant real BUTTON_W = .1
        private constant real BUTTON_H = .1 * SCREEN_ASPECT_RATIO
       
        private boolean array Displayed
        private UIButton array LastButton
    endglobals
   
    private function OnLeftClick takes nothing returns boolean
        local UIButton btn = GetTriggerButton()
        local integer pid = btn.customValue
        local player p = GetClickingPlayer()
       
        if (User.Local == p) then
            call SelectUnit(PlayerHero[pid], true)
        endif
       
        if (btn == DoubleClickButton[pid] and LastButton[pid] != 0) then
            call BJDebugMsg("[" + User[p].nameColored + "] Double-Click: Button #" + I2S(LastButton[pid]))
            set LastButton[pid] = 0
        else
            call BJDebugMsg("[" + User[p].nameColored + "] Left-Click: Button #" + I2S(btn))
        endif
       
        set LastButton[pid] = btn
       
        return false
    endfunction
   
    private function OnRightClick takes nothing returns boolean
        local UIButton btn = GetTriggerButton()
        local integer pid = btn.customValue
        local player p = GetClickingPlayer()

        call BJDebugMsg("[" + User[p].nameColored + "] Right-Click: Button #" + I2S(btn))
       
        return false
    endfunction
   
    struct BasicWindow
       
        // we use an array instead of struct members
        // to create for each player only once
        static UIPicture array Background
        static UIButton array Buttons[15][20] // size for 15 players, 20 buttons
        static UIText array Text
       
        readonly boolean displayed
        readonly User user
        readonly Camera camera
        readonly thistype next
        readonly thistype prev
        readonly integer buttons

        method addButton takes real x, real y, integer texture returns UIButton
            set Buttons[user.id][.buttons]              = UIButton.create(x, y, BUTTON_W, BUTTON_H, 0.5, texture)
            //set Buttons[user.id][.buttons].onLeftClick  = Filter(function OnLeftClick)
            set Buttons[user.id][.buttons].onRightClick = Filter(function OnRightClick)
            set Buttons[user.id][.buttons].customValue  = .user.id
            set Buttons[user.id][.buttons].selectUnit   = PlayerHero[.user.id] // unit to re-select when selecting dummy
           
            set .buttons = .buttons + 1
           
            return Buttons[user.id][.buttons-1]
        endmethod

        static method create takes User user, Camera cam returns thistype
            local thistype this = thistype.allocate()
            local integer i = 0
           
            set this.camera = cam
            set this.user = user
            set this.buttons = 0

            // create only once per player
            if (Background[user.id] == 0) then
           
                // create background
                set Background[user.id] = CreateWindow(BACKGROUND_X, BACKGROUND_Y, BACKGROUND_SIZE, BACKGROUND_W, BACKGROUND_H, GetPlayerRace(user.handle))

                // create title
                set Text[user.id] = UIText.createEx(user.handle, BACKGROUND_X + 0.15, BACKGROUND_Y, 1)
                call SetTextTagText(Text[user.id].text, "Custom User Interface v1.0", 12 * 0.0023)
               
                // create double click detector (not officially supported)
                set DoubleClickButton[user.id] = this.addButton(BACKGROUND_X + 0.28, 0.24, 'B002')
               
                // create top button
                call this.addButton(BACKGROUND_X + 0.28, 0.06, 'B000')
               
                // create bottom button
                call this.addButton(BACKGROUND_X + 0.28, -0.12, 'B001')
   
            endif
           
            return this
        endmethod
       
        method show takes boolean flag returns nothing
            local integer i = this.user.id
            local integer n = 0
            local player p = this.user.handle
           
            // show only to one player for performance
            call Background[i].showPlayer(p, flag, PlayerCamera[i])
            call Text[i].show(flag, PlayerCamera[i])
           
            loop
                exitwhen n == .buttons
                call Buttons[i][n].showPlayer(p, flag, PlayerCamera[i])
                set n = n + 1
            endloop
        endmethod
       
        method update takes nothing returns nothing
            local integer i = this.user.id
            local integer n = 0
           
            // update only the elements we need
            call Background[i].update()
            call Text[i].update()
           
            loop
                exitwhen n == .buttons
                call Buttons[i][n].update()
                set n = n + 1
            endloop
        endmethod
       
    endstruct

    private function OnUpdate takes nothing returns nothing
        local User user = User(User.LocalId)
        local integer id = user.id
        local Camera cam = PlayerCamera[id]
        local real x = GetUnitX(PlayerHero[id])
        local real y = GetUnitY(PlayerHero[id])
        local BasicWindow window = PlayerWindow[id]

        call cam.setPosition(x, y, UserInterface_GetTerrainZ(x, y))
       
        if (cam.applyCameraForPlayer(user.handle, false)) then
            //call Interface.updateAll(true, true, true)
            call window.update()
        endif
    endfunction
   
    private function CreateInterfaces takes nothing returns nothing
        local User user = User.first
        local BasicWindow window
       
        loop
            exitwhen user == User.NULL

            // create a basic window for all playing players
            set window = BasicWindow.create(user, PlayerCamera[user.id])
           
            set PlayerWindow[user.id] = window
           
            call window.show(true)

            set user = user.next
        endloop

        call TimerStart(CreateTimer(), 0.01, true, function OnUpdate)
    endfunction

endlibrary
library UserInterface initializer Init requires optional UnitDex/*or any unit indexer*/, Camera
/***************************************************************
*
*   v1.0.6, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
*   - Create UI elements based on dummy units.
*
*   - Elements are overlay onto the current camera keeping you in gameplay.
*
*   - The camera can be modified to any view including third person, however
*     it must be locked.
*
*   - Dynamic textures are applied to dummies through an ability based off
*     off War Clib, using destructables with their replaceable texture field
*     set to the desired texture. (more: http://www.wc3c.net/showthread.php?p=1043980)
*
*   - Detect left and right clicks.
*
*   - Works in multiplayer (and single) without lag.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Copy the script to your map and save it (requires JassHelper *or* JNGP)
*       2. Copy the object editor data and paste the correct raw codes into the configuration.
*   _________________________________________________________________________
*   2. API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       struct UIButton
*
*           static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
*           static method updateAll takes nothing returns nothing
*           static method click takes unit u, boolean isLeft returns boolean
*           static method clickEx takes real x, real y, boolean isLeft returns boolean
*           static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
*
*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method show takes boolean flag, Camera Cam returns nothing
*           method showPlayer takes player p, boolean flag, Camera cam returns nothing
*           method isClicked takes real x, real y returns boolean
*           method clickL takes nothing returns nothing
*           method clickR takes nothing returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method setTexture takes integer texture returns nothing
*
*           -----------------------
*
*           integer customValue
*           integer animIndex
*           trigger triggerL
*           trigger triggerR
*           filterfunc onLeftClick
*           filterfunc onRightClick
*
*
*       struct UIPicture
*
*           static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
*           static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
*           static method updateAll takes nothing returns nothing

*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method show takes boolean show, Camera Cam returns nothing
*           method showPlayer takes player p, boolean flag, Camera cam returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method setTexture takes integer texture returns nothing
*
*           -----------------------
*
*           integer customValue
*           integer animIndex
*
*       struct UIText
*  
*           static method create takes real minx, real maxy, real z returns thistype
*           static method updateAll takes nothing returns nothing
*
*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method show takes boolean show, Camera Cam returns nothing
*
*           -----------------------
*
*           integer customValue
*
*       function GetTriggerButton takes nothing returns UIButton
*       function GetClickingPlayer takes nothing returns player
*       function GetCameraDeltaZ takes nothing returns real
*       function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
*
*       public function ApplySkin takes unit u, integer texture returns boolean
*
***************************************************************/


    // configuration
    private module InterfaceConfig
   
        // dummy unit with pitch angle animations
        static constant integer DUMMY_TYPE          = 'uidm'
       
        // model used in CreateWindow function
        static constant integer WINDOW_DUMMY        = 'uiwn'
       
        // destructable ID of the human border texture. the
        // rest of the races should have consecutive ids.
        static constant integer RACE_BORDERS_START  = 'D201'
       
        // run a timer to check for unit selections.
        // faster but more cpu intensive.
        static constant boolean LEFT_CLICK_TIMER    = true

            // really low timer intervals remove the flicker
            // effect, most of the time.
            static constant real LEFT_CLICK_TIMER_RATE  = 0.01

            // in multiplayer, IsUnitSelected is synchronous
            // so it doesn't instantly return false when you use
            // SelectUnit(u, false). So we must disable the button
            // for the clicking player for a brief period or else
            // the event will be spammed.
            static constant real LEFT_CLICK_DELAY   = 0.6
       
        // ability id's
        static constant integer DESTROYER_FORM_ID   = 'Aave'
        static constant integer LOCUST_ID           = 'Aloc'
       
        // ability used to change dummy unit textures
        static constant integer SKIN_CHANGE_ABILITY = 'Skin'
       
        static constant integer GHOST_ABILITY       = 'Aeth'

        // dummy player
        static constant player PLAYER               = CAMERA_DUMMY_PLAYER // from Camera library
       
    endmodule
    // end configuration

    globals
        public destructable DummyTree = null
        private trigger Trig = CreateTrigger()
        private UIButton ClickedButton = 0
        private player ClickingPlayer = null
        private location DummyLoc = Location(0,0)
    endglobals

    function GetTriggerButton takes nothing returns UIButton
        return ClickedButton
    endfunction
   
    function GetClickingPlayer takes nothing returns player
        return ClickingPlayer
    endfunction

    public function Eval takes filterfunc func returns boolean
        call TriggerClearConditions(Trig)
        call TriggerAddCondition(Trig, func)
        return TriggerEvaluate(Trig)
    endfunction

    public function GetTerrainZ takes real x, real y returns real
        call MoveLocation(DummyLoc, x, y)
        return GetLocationZ(DummyLoc)
    endfunction
   
    // adjusts
    public function FindModelAnimIndex takes real w, real h, real z returns integer
        local real W = w*SCREEN_WIDTH*z
        local real H = h*SCREEN_HEIGHT*z
        local real anim
        if (H<W) then
            set anim = 10*(W/H-1)
            return UIMath_R2I_N(anim)
        else
            set anim = 10*(H/W-1)
            if anim >= 0.5 then
                return 100+UIMath_R2I_N(anim)
            endif
        endif
        return 0
    endfunction

    public function FindModelSize takes real w, real h, real z returns real
        local real W = w*SCREEN_WIDTH*z
        local real H = h*SCREEN_HEIGHT*z
        if (H<W) then
            return 0.5*H
        endif
        return 0.5*W
    endfunction

    private function LocalReal takes player p, real forPlayer, real default returns real
        if (User.Local == p) then
            return forPlayer
        endif
        return default
    endfunction
   
    public function ApplySkin takes unit u, integer texture returns boolean
        local boolean out = false
       
        if (texture != 0) then
           
            call UnitAddAbility(u, Interface.SKIN_CHANGE_ABILITY)
            set DummyTree = CreateDestructable(texture,GetUnitX(u),GetUnitY(u),0,0,1)
            set out = IssueTargetOrder(u, "grabtree", DummyTree)
            call RemoveDestructable(DummyTree)
            set DummyTree = null
            call UnitRemoveAbility(u, Interface.SKIN_CHANGE_ABILITY)
        endif
       
        return out
    endfunction

    struct UIButton
        integer customValue
        integer animIndex
        trigger triggerL
        trigger triggerR
        filterfunc onLeftClick
        filterfunc onRightClick
        boolean disabled
        unit selectUnit

        static boolean useDelay = false

        readonly static thistype array AllShow
        readonly static integer CountShow = 0

        readonly integer index
        readonly Camera camera
       
        readonly real scale
        readonly real centerx
        readonly real centery
        readonly real minx
        readonly real maxx
        readonly real miny
        readonly real maxy
        readonly real width
        readonly real height
        readonly real z
        readonly unit picture
        readonly effect model
       
        readonly boolean displayed
        readonly integer texture
       
        private integer unitId
        private static thistype array Unit2Button
       
        static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
            local thistype this = thistype.allocate()

            set .texture        = texture
            set .customValue    = 0
            set .camera         = 0
            set .width          = W
            set .height         = H
            set .minx           = minx
            set .maxx           = minx+W
            set .maxy           = maxy
            set .miny           = maxy-H
            set .z              = 100.2+z
            set .centerx        = minx+W/2.0
            set .centery        = maxy-H/2.0
            set .displayed      = false
            set .scale          = FindModelSize(W, H, .z)
            set .triggerL       = null
            set .triggerR       = null
            set .onLeftClick    = null
            set .onRightClick   = null
            set .selectUnit     = null
            set .picture        = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
           
            // apply texture
            call ApplySkin(.picture, texture)
           
            set .animIndex = FindModelAnimIndex(W, H, .z)
            call SetUnitAnimationByIndex(.picture, .animIndex)
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.SKIN_CHANGE_ABILITY)
            call UnitAddAbility(.picture, Interface.GHOST_ABILITY)
           
            set this.unitId = GetUnitUserData(.picture)
            set Unit2Button[this.unitId] = this
           
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call RemoveUnit(.picture)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod

        method update takes nothing returns nothing
            local VECTOR3 Pos
           
            if (.displayed) then
                set Pos = .camera.win2World(.centerx, .centery, .z)
                call SetUnitX(.picture, Pos.x)
                call SetUnitY(.picture, Pos.y)
                call SetUnitFlyHeight(.picture, Pos.z-GetTerrainZ(Pos.x, Pos.y), 0)
                call Pos.destroy()
            endif
        endmethod
       
        method show takes boolean flag, Camera Cam returns nothing
            if Cam != -1 then
                set .camera = Cam
            endif
           
            if flag != .displayed then
                if flag then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                    call SetUnitAnimationByIndex(.picture, .animIndex)
                   
                    call SetUnitScale(.picture, .scale, 0, 0)
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                   
                    call SetUnitX(.picture, 0)
                    call SetUnitY(.picture, 0)
                   
                    call SetUnitScale(.picture, 0, 0, 0)
                endif
            endif
           
            set .displayed = flag
        endmethod
       
        method showPlayer takes player p, boolean flag, Camera cam returns nothing
            call .show(flag, cam)
           
            if (flag) then
                call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
            endif
        endmethod

        method isClicked takes real x, real y returns boolean
            return .minx < x and x < .maxx and .miny < y  and y < .maxy
        endmethod
       
        method clickL takes nothing returns nothing
            set ClickedButton = this

            if (.triggerL != null and TriggerEvaluate(.triggerL)) then
                call TriggerExecute(.triggerL)
            endif

            if (.onLeftClick != null) then
                 call Eval(.onLeftClick)
            endif
        endmethod
       
        method clickR takes nothing returns nothing
            set ClickedButton = this

            if (.triggerR != null and TriggerEvaluate(.triggerR)) then
                call TriggerExecute(.triggerR)
            endif
           
            if (.onRightClick != null) then
                 call Eval(.onRightClick)
            endif
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .minx = minx
            set .maxx = minx+.width
            set .maxy = maxy
            set .miny = maxy-.height
            set .centerx = minx+.width/2.0
            set .centery = maxy-.height/2.0
           
            if .displayed then
                call .update()
            endif
        endmethod
       
        method setTexture takes integer texture returns nothing
            call SetUnitX(.picture, 0)
            call SetUnitY(.picture, 0)
            call ApplySkin(.picture, texture)
           
            if .displayed then
                call .update()
            endif
           
            set .texture = texture
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow
            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod
       
        static method click takes unit u, boolean isLeft returns boolean
            local thistype b
           
            if GetUnitTypeId(u) == Interface.DUMMY_TYPE then
                set b = Unit2Button[GetUnitUserData(u)]

                if b > 0 and b.displayed then
                    call SelectUnit(u, false)

                    if isLeft then
                        call b.clickL()
                    else
                        call b.clickR()
                    endif
                   
                    return true
                endif
            endif
           
            return false
        endmethod
       
        static method clickEx takes real x, real y, boolean isLeft returns boolean
            local integer i = .CountShow
           
            loop
                exitwhen i < 0

                if .AllShow[i].isClicked(x, y) then
                    if isLeft then
                        call .AllShow[i].clickL()
                    else
                        call .AllShow[i].clickR()
                    endif
                   
                    return true
                endif
               
                set i = i - 1
            endloop
           
            return false
        endmethod
       
        private static method enableButtonCallback takes nothing returns nothing
            local timer t = GetExpiredTimer()

            static if (LIBRARY_TimerUtils) then
                local thistype btn = thistype(GetTimerData(t))
            else
                local thistype btn = thistype(R2I(TimerGetRemaining(t) + 0.5))
            endif

            set btn.disabled = false
            set t = null
        endmethod

        static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
            local integer i = .CountShow
            local timer t
           
            loop
                exitwhen i < 0

                if (IsUnitSelected(.AllShow[i].picture, p)) then
                   
                    if (not .AllShow[i].disabled) then

                        if isLeft then
                            call .AllShow[i].clickL()
                        else
                            call .AllShow[i].clickR()
                        endif

                       
                        if (thistype.useDelay) then // static if doesn't work..
                            set .AllShow[i].disabled = true

                            static if (LIBRARY_TimerUtils) then
                                call TimerStart(NewTimerEx(.AllShow[i]), Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
                            else
                                set t = CreateTimer()
                                call TimerStart(t, .AllShow[i], false, null)
                                call PauseTimer(t)
                                call TimerStart(t, Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
                                set t = null
                            endif
                        endif
                       
                        return true
                    endif

                    call SelectUnit(.AllShow[i].picture, false)

                    if (.AllShow[i].selectUnit != null and User.Local == p) then
                        call SelectUnit(.AllShow[i].selectUnit, true)
                    endif

                endif
               
                set i = i - 1
            endloop
           
            return false
        endmethod

    endstruct

    struct UIPicture
        integer customValue
        integer animIndex
       
        readonly static thistype array AllShow
        readonly static integer CountShow = 0
       
        readonly integer index
        readonly Camera camera
       
        readonly real scale
        readonly real centerx
        readonly real centery
        readonly real width
        readonly real height
        readonly real z
        readonly unit picture
        readonly boolean displayed
        readonly integer texture

        static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
            local thistype this = thistype.allocate()

            set .texture        = texture
            set .customValue    = 0
            set .camera         = 0
            set .width          = w
            set .height         = h
            set .centerx        = minx+w/2.0
            set .centery        = maxy-h/2.0
            set .z              = 100.2+z
            set .displayed      = false
            set .picture        = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
            set .animIndex      = FindModelAnimIndex(w, h, .z)
            set .scale          = FindModelSize(w, h, .z)
           
            call ApplySkin(.picture, texture)
           
            call SetUnitAnimationByIndex(.picture, .animIndex)
           
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.LOCUST_ID)
            call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
           
            // hide unit
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
            local thistype this = thistype.allocate()

            set .customValue    = 0
            set .camera         = 0
            set .z              = 100.2 + z
            set .width          = (modelWidth*scale) / (SCREEN_WIDTH*.z)
            set .height         = (modelHeight*scale) / (SCREEN_HEIGHT*.z)
            set .scale          = scale
            set .centerx        = minx+.width/2.0
            set .centery        = maxy-.height/2.0
            set .displayed      = false
            set .picture        = CreateUnit(Interface.PLAYER, unitId, 0, 0, 270)
           
            call ApplySkin(.picture, texture)
           
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.LOCUST_ID)
            call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
           
            // hide unit
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call RemoveUnit(.picture)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod

        method update takes nothing returns nothing
            local VECTOR3 Pos

            if (this.displayed) then
                set Pos = this.camera.win2World(.centerx, .centery, .z)
                call SetUnitX(.picture, Pos.x)
                call SetUnitY(.picture, Pos.y)
                call SetUnitFlyHeight(.picture, Pos.z - GetTerrainZ(Pos.x, Pos.y), 0)
                call Pos.destroy()
            endif
        endmethod
       
        method show takes boolean show, Camera Cam returns nothing
            if Cam != -1 then
                set this.camera = Cam
            endif

            if show != .displayed then
                if show then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                    call SetUnitAnimationByIndex(.picture, .animIndex)
                    call SetUnitScale(.picture, .scale, 0, 0)
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                    call SetUnitX(.picture, 0)
                    call SetUnitY(.picture, 0)
                    call SetUnitScale(.picture, 0, 0, 0)
                endif
            endif
           
            set .displayed = show
        endmethod
       
        method showPlayer takes player p, boolean flag, Camera cam returns nothing
            call .show(flag, cam)
           
            if (flag) then
                call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
            endif
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .centerx = minx+.width/2.0
            set .centery = maxy-.height/2.0
            if .displayed then
                call .update()
            endif
        endmethod
       
        method setTexture takes integer texture returns nothing
            call SetUnitX(.picture, 0)
            call SetUnitY(.picture, 0)
            call ApplySkin(.picture, texture)
           
            if .displayed then
                call .update()
            endif
           
            set .texture = texture
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow

            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod

    endstruct

    struct UIText
        integer customValue
       
        readonly unit dummy
        readonly static thistype array AllShow
        readonly static integer CountShow = 0
        readonly integer index
        readonly Camera camera
        readonly real minx
        readonly real maxy
        readonly real z
        readonly texttag text
        readonly boolean displayed
       
        static method create takes real minx, real maxy, real z returns thistype
            local thistype this = thistype.allocate()
           
            set .customValue    = 0
            set .camera         = 0
            set .minx           = minx
            set .maxy           = maxy
            set .z              = 100 + z
            set .displayed      = false
            set .text           = CreateTextTag()
            set .dummy          = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)

            call SetUnitScale(.dummy, 0, 0, 0)

            call UnitAddAbility(.dummy, Interface.LOCUST_ID)
            call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
           
            call SetTextTagVisibility(.text, false)
            call SetTextTagPosUnit(.text, .dummy, 0)
           
            return this
        endmethod
       
        static method createEx takes player p, real minx, real maxy, real z returns thistype
            local thistype this = thistype.allocate()
           
            set .customValue    = 0
            set .camera         = 0
            set .minx           = minx
            set .maxy           = maxy
            set .z              = 100 + z
            set .displayed      = false
            set .dummy          = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)

            if (User.Local == p) then
                set .text = CreateTextTag()
            endif
           
            call SetUnitScale(.dummy, 0, 0, 0)

            call UnitAddAbility(.dummy, Interface.LOCUST_ID)
            call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
           
            call SetTextTagVisibility(.text, false)
            call SetTextTagPosUnit(.text, .dummy, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call DestroyTextTag(.text)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod
       
        method update takes nothing returns nothing
            local VECTOR3 Pos = .camera.win2World(.minx, .maxy, .z)
            call SetUnitX(.dummy, Pos.x)
            call SetUnitY(.dummy, Pos.y)
            call SetTextTagPosUnit(.text, .dummy, (Pos.z-GetTerrainZ(Pos.x,Pos.y))-14.8)
            call Pos.destroy()
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .minx = minx
            set .maxy = maxy
           
            if .displayed then
                call .update()
            endif
        endmethod
       
        method show takes boolean show, Camera Cam returns nothing
            if Cam != -1 then
                set .camera = Cam
            endif
           
            call SetTextTagVisibility(.text, show)
           
            if show != .displayed then
                if show then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                endif
            endif
           
            set .displayed = show
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow
            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod

    endstruct

    struct Interface extends array
       
        implement InterfaceConfig
       
       static method getRaceBorders takes race r returns integer
            return RACE_BORDERS_START + (GetHandleId(r)-1)
        endmethod
       
        static method updateAll takes boolean but, boolean pic, boolean tex returns nothing
            if but then
                call UIButton.updateAll()
            endif
            if pic then
                call UIPicture.updateAll()
            endif
            if tex then
                call UIText.updateAll()
            endif
        endmethod

    endstruct

    function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
        return UIPicture.createEx(x, y, /*z=*/2, size, Interface.WINDOW_DUMMY, width, height, Interface.getRaceBorders(playerRace))
    endfunction
   
    private function InterfaceClickR takes nothing returns nothing
        local unit clicker = GetTriggerUnit()
        local unit btnUnit = GetOrderTargetUnit()
       
        set ClickingPlayer = GetOwningPlayer(clicker)
       
        if (UIButton.click(btnUnit, false)) then
            call PauseUnit(clicker, true)
            call IssueImmediateOrderById(clicker, 851973)
            call PauseUnit(clicker, false)
        endif
       
        set clicker = null
    endfunction
   
    private function InterfaceClickL takes nothing returns nothing
        set ClickingPlayer = GetTriggerPlayer()
        call UIButton.click(GetTriggerUnit(), true)
    endfunction

    private function InterfaceClickL_Timer takes nothing returns nothing
        local integer i = 0
        local integer c = UIButton.CountShow
        local User user = User.first
       
        loop
            exitwhen user == User.NULL
 
            set ClickingPlayer = user.handle
           
            call UIButton.clickPeriodicSelect(user.handle, true)

            set user = user.next
        endloop
    endfunction
   
    private function Init takes nothing returns nothing
         local trigger t = CreateTrigger()
         local trigger t2 = CreateTrigger()
         local integer i = 0
         local User user = User.first
       
         loop
            exitwhen user == User.NULL
           
            call TriggerRegisterPlayerUnitEvent(t, user.handle, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, null)
           
            static if (not Interface.LEFT_CLICK_TIMER) then
                call TriggerRegisterPlayerUnitEvent(t2, user.handle, EVENT_PLAYER_UNIT_SELECTED, null)
            endif
           
            set user = user.next
        endloop

        call TriggerAddAction(t, function InterfaceClickR)
       
        static if (Interface.LEFT_CLICK_TIMER) then
            call TimerStart(CreateTimer(), Interface.LEFT_CLICK_TIMER_RATE, true, function InterfaceClickL_Timer)
        else
            call TriggerAddAction(t2, function InterfaceClickL)
        endif

        set UIButton.useDelay = Interface.LEFT_CLICK_TIMER
    endfunction
   
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
*   v1.2.1, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   UnitDex assigns every unit an unique integer. It attempts to make that number within the
*   maximum array limit so you can associate it with one.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Copy the script to your map, save it, then restart the editor and comment out the line below.
*/

    ///! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/*  ________________________________________________________________________
*   2. Configuration
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
 
    private module UnitDexConfig
   
        // The raw code of the leave detection ability.
        static constant integer DETECT_LEAVE_ABILITY = 'uDex'
       
        // Allow debug messages (debug mode must also be on)
        static constant boolean ALLOW_DEBUGGING      = true
       
        // Uncomment the lines below to define a filter for units being indexed
       
        /*static method onFilter takes unit u returns boolean
            return true
        endmethod*/

       
    endmodule
/*  _________________________________________________________________________
*   3. Function API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Every function inlines except for UnitDexRemove
*
*       function GetUnitId takes unit whichUnit returns integer
*       function GetUnitById takes integer index returns unit
*    
*       function UnitDexEnable takes boolean flag returns nothing
*       function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
*       function IsUnitIndexed takes unit u returns boolean
*       function IsIndexingEnabled takes nothing returns boolean
*
*       function GetIndexedUnit takes nothing returns unit
*       function GetIndexedUnitId takes nothing returns integer
*      
*       function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
*       function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
*       function OnUnitIndex takes code func returns triggercondition
*       function OnUnitDeidex takes code func returns triggercondition
*   _________________________________________________________________________
*   4. Struct API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       UnitDex.Enabled = false // toggle the indexer
*       UnitDex.Initialized     // returns true if the preload timer has finished
*       UnitDex.Count           // returns the amount of units indexed
*       UnitDex.Unit[0]         // access the UnitDex array directly
*       UnitDex.Group           // a unit group containing every indexed unit (for enumeration)
*       UnitDex.LastIndex       // returns the last indexed unit's id
*   _________________________________________________________________________
*   5. Public Variables
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       These are to be used with the "eventtype" argument of the event API:
*
*           constant integer EVENT_UNIT_INDEX     = 0
*           constant integer EVENT_UNIT_DEINDEX   = 1
*   _________________________________________________________________________
*   6. Examples
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Associate a unit with a variable
*
*           set UnitFlag[GetUnitId(yourUnit)] = true
*
*       2. Allocate a struct instance using a units assigned ID
*
*           local somestruct data = GetUnitId(yourUnit)
*  
*       3. Detect when a unit leaves the map
*
*           function Exit takes nothing returns nothing
*               call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
*           endfunction
*
*           call OnUnitDeindex(function Exit)
*           // or
*           call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
*   _________________________________________________________________________
*   7. How it works
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Enumerate all preplaced units
*       2. TriggerRegisterEnterRegion native to detect when a unit enters the map
*       3. Assign each unit that enters the map a unique integer
*       4. Give every unit an ability based off of defend. There is no native to accurately
*          detect when a unit leaves the map but when a unit dies or is removed from the game
*          they are issued the "undefend" order
*       5. Catch the "undefend" order and remove unit from the queue
*   _________________________________________________________________________
*   8. Notes
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
*       - The object merger line should be commented out after saving and restarting.
*       - All public functions are inlined except UnitDexRemove.
*
*   -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/

   
    globals
        // Event types
        constant integer EVENT_UNIT_INDEX     = 0
        constant integer EVENT_UNIT_DEINDEX   = 1
       
        // System variables
        private trigger array IndexTrig
        private integer Index = 0
        private real E=-1
        private boolexpr FilterEnter
    endglobals
   
    function GetUnitId takes unit whichUnit returns integer
        return GetUnitUserData(whichUnit)
    endfunction
   
    function GetUnitById takes integer index returns unit
        return UnitDex.Unit[index]
    endfunction
   
    function GetIndexedUnit takes nothing returns unit
        return UnitDex.Unit[UnitDex.LastIndex]
    endfunction
   
    function GetIndexedUnitId takes nothing returns integer
        return UnitDex.LastIndex
    endfunction
   
    function IsUnitIndexed takes unit u returns boolean
        return (GetUnitById(GetUnitId(u)) != null)
    endfunction
   
    function UnitDexEnable takes boolean flag returns nothing
        set UnitDex.Enabled = flag
    endfunction
   
    function IsIndexingEnabled takes nothing returns boolean
        return UnitDex.Enabled
    endfunction
   
    function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
        return TriggerAddCondition(IndexTrig[eventtype], func)
    endfunction
   
    function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
        call TriggerRemoveCondition(IndexTrig[eventtype], c)
    endfunction
   
    function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
        call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
    endfunction
   
    function OnUnitIndex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
    endfunction

    function OnUnitDeindex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
    endfunction
   
    function UnitDexRemove takes unit u, boolean runEvents returns boolean
        return UnitDex.Remove(u, runEvents)
    endfunction
   
    /****************************************************************/
   
    private keyword UnitDexCore
   
    struct UnitDex extends array
        static boolean Enabled = true
       
        readonly static integer LastIndex
        readonly static boolean Initialized=false
        readonly static group Group=CreateGroup()
        readonly static unit array Unit
        readonly static integer Count = 0
        readonly static integer array List
       
        implement UnitDexConfig
       
        private static integer Counter = 0
       
        implement UnitDexCore
    endstruct
   
    /****************************************************************/
     
    private module UnitDexCore
   
        static method Remove takes unit u, boolean runEvents returns boolean
            local integer i
           
            if (IsUnitIndexed(u)) then
                set i = GetUnitId(u)
                set UnitDex.List[i] = Index
                set Index = i
               
                call GroupRemoveUnit(UnitDex.Group, u)
                call SetUnitUserData(u, 0)
           
                if (runEvents) then
                    set UnitDex.LastIndex = i
                    set E = EVENT_UNIT_DEINDEX
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                    set E = -1
                endif
               
                set UnitDex.Unit[i] = null
                set UnitDex.Count = UnitDex.Count - 1
               
                return true
            endif
           
            return false
        endmethod
       
        private static method onGameStart takes nothing returns nothing
            local integer i = 0
            static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
                local group ENUM_GROUP = CreateGroup() // If not, create the group.
            endif
           
            // Index preplaced units
            loop
                call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
               
                set i = i + 1
               
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            static if (not LIBRARY_GroupUtils) then
                call DestroyGroup(ENUM_GROUP)
                set ENUM_GROUP = null
            endif
           
            // run init triggers
            set i = 1
            loop
                exitwhen i > Counter
               
                set LastIndex = i
               
                call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                set E = EVENT_UNIT_INDEX
                set E = -1
               
                set i = i + 1
            endloop

            set LastIndex   = Counter
            set Initialized = true
            set FilterEnter = null
           
            call DestroyTimer(GetExpiredTimer())
        endmethod
       
        private static method onEnter takes nothing returns boolean
            local unit    u = GetFilterUnit()
            local integer i = GetUnitId(u)
            local integer t = Index
           
            if (i == 0 and thistype.Enabled) then
               
                // If a filter was defined pass the unit through it.
                static if (thistype.onFilter.exists) then
                    if (not thistype.onFilter(u)) then
                        set u = null
                        return false // check failed
                    endif
                endif
               
                // Handle debugging
                static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
                    if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
                        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
                        set u = null
                        return false
                    endif
                endif
               
                // Add to group of indexed units
                call GroupAddUnit(thistype.Group, u)
               
                // Give unit the leave detection ability
                call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
                call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
               
                // Allocate index
                if (Index != 0) then
                    set Index = List[t]
                else
                    set Counter = Counter + 1
                    set t = Counter
                endif
               
                set List[t] = -1
                set LastIndex = t
                set thistype.Unit[t] = u
                set Count = Count + 1
               
                // Store the index
                call SetUnitUserData(u, t)
               
                if (thistype.Initialized) then
                    // Execute custom events registered with RegisterUnitIndexEvent
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_INDEX

                    // Reset so the event can occur again
                    set E = -1
                endif
            endif
           
            set u = null
           
            return false
        endmethod

        private static method onLeave takes nothing returns boolean
            local unit    u
            local integer i
           
            // Check if order is undefend.
            if (thistype.Enabled and GetIssuedOrderId() == 852056) then
               
                set u = GetTriggerUnit()
               
                // If unit was killed (not removed) then don't continue
                if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
                    set u = null
                    return false
                endif
               
                set i = GetUnitId(u)

                // If unit has been indexed then deindex it
                if (i > 0 and i <= Counter and u == GetUnitById(i)) then
                   
                    // Recycle the index
                    set List[i]   = Index
                    set Index     = i
                    set LastIndex = i
                   
                    // Remove to group of indexed units
                    call GroupRemoveUnit(thistype.Group, u)
               
                    // Execute custom events without any associated triggers
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_DEINDEX
                   
                    // Remove entry
                    call SetUnitUserData(u, 0)
                    set thistype.Unit[i] = null
                   
                    // Decrement unit count
                    set Count = Count - 1
               
                    // Reset so the event can occur again
                    set E = -1
                endif
               
                set u = null
            endif
           
            return false
        endmethod
       
        private static method onInit takes nothing returns nothing
            local trigger t         = CreateTrigger()
            local integer i         = 0
            local player p
            local unit u
           
            static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
                local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
                local rect world = GetWorldBounds()
            endif
           
            set FilterEnter = Filter(function thistype.onEnter)
           
            // Begin to index units when they enter the map
            static if (LIBRARY_WorldBounds) then
                call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
            else
                call RegionAddRect(reg, world)
                call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
                call RemoveRect(world)
                set world = null
            endif
           
            call TriggerAddCondition(t, Filter(function thistype.onLeave))
           
            set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
            set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
           
            loop
                set p = Player(i)
               
                // Detect "undefend"
                call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
               
                // Hide the detect ability from players
                call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
               
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
        endmethod
   
    endmodule
   
endlibrary
library PlayerUtils
/**************************************************************
*
*   v1.2.8 by TriggerHappy
*
*   This library provides a struct which caches data about players
*   as well as provides functionality for manipulating player colors.
*
*   Constants
*   ------------------
*
*       force FORCE_PLAYING - Player group of everyone who is playing.
*
*   Struct API
*   -------------------
*     struct User
*
*       static method fromIndex takes integer i returns User
*       static method fromLocal takes nothing returns User
*       static method fromPlaying takes integer id returns User
*
*       static method operator []    takes integer id returns User
*       static method operator count takes nothing returns integer
*
*       method operator name         takes nothing returns string
*       method operator name=        takes string name returns nothing
*       method operator color        takes nothing returns playercolor
*       method operator color=       takes playercolor c returns nothing
*       method operator defaultColor takes nothing returns playercolor
*       method operator hex          takes nothing returns string
*       method operator nameColored  takes nothing returns string
*
*       method toPlayer takes nothing returns player
*       method colorUnits takes playercolor c returns nothing
*
*       readonly string originalName
*       readonly boolean isPlaying
*       readonly static player Local
*       readonly static integer LocalId
*       readonly static integer AmountPlaying
*       readonly static playercolor array Color
*       readonly static player array PlayingPlayer
*
**************************************************************/


    globals
        // automatically change unit colors when changing player color
        private constant boolean AUTO_COLOR_UNITS = true
   
        // use an array for name / color lookups (instead of function calls)
        private constant boolean ARRAY_LOOKUP     = false
   
        // this only applies if ARRAY_LOOKUP is true
        private constant boolean HOOK_SAFETY      = false // disable for speed, but only use the struct to change name/color safely
   
        constant force FORCE_PLAYING = CreateForce()
   
        private string array Name
        private string array Hex
        private string array OriginalHex
        private playercolor array CurrentColor
    endglobals

    private keyword PlayerUtilsInit

    struct User extends array
     
        static constant integer NULL = 15
       
        readonly player handle
        readonly integer id
        readonly thistype next
        readonly thistype prev

        readonly string originalName
        readonly boolean isPlaying
   
        readonly static thistype first
        readonly static thistype last
        readonly static player Local
        readonly static integer LocalId
        readonly static integer AmountPlaying = 0
        readonly static playercolor array Color

        static if not (LIBRARY_GroupUtils) then
            readonly static group ENUM_GROUP = CreateGroup()
        endif

        private static thistype array PlayingPlayer
        private static integer array PlayingPlayerIndex
   
        // similar to Player(#)
        static method fromIndex takes integer i returns thistype
            return thistype(i)
        endmethod
   
        // similar to GetLocalPlayer
        static method fromLocal takes nothing returns thistype
            return thistype(thistype.LocalId)
        endmethod
   
        // access active players array
        static method fromPlaying takes integer index returns thistype
            return PlayingPlayer[index]
        endmethod
   
        static method operator [] takes player p returns thistype
            return thistype(GetPlayerId(p))
        endmethod
   
        method toPlayer takes nothing returns player
            return this.handle
        endmethod
     
        method operator name takes nothing returns string
            static if (ARRAY_LOOKUP) then
                return Name[this]
            else
                return GetPlayerName(this.handle)
            endif
        endmethod
   
        method operator name= takes string newName returns nothing
            call SetPlayerName(this.handle, newName)
            static if (ARRAY_LOOKUP) then
                static if not (HOOK_SAFETY) then
                    set Name[this] = newName
                endif
            endif
        endmethod
   
        method operator color takes nothing returns playercolor
            static if (ARRAY_LOOKUP) then
                return CurrentColor[this]
            else
                return GetPlayerColor(this.handle)
            endif
        endmethod
   
        method operator hex takes nothing returns string
            return OriginalHex[GetHandleId(this.color)]
        endmethod
   
        method operator color= takes playercolor c returns nothing
            call SetPlayerColor(this.handle, c)
       
            static if (ARRAY_LOOKUP) then
                set CurrentColor[this] = c
                static if not (HOOK_SAFETY) then
                    static if (AUTO_COLOR_UNITS) then
                        call this.colorUnits(color)
                    endif
                endif
            endif
        endmethod
   
        method operator defaultColor takes nothing returns playercolor
            return Color[this]
        endmethod
   
        method operator nameColored takes nothing returns string
            return hex + this.name + "|r"
        endmethod
   
        method colorUnits takes playercolor c returns nothing
            local unit u
       
            call GroupEnumUnitsOfPlayer(ENUM_GROUP, this.handle, null)
       
            loop
                set u = FirstOfGroup(ENUM_GROUP)
                exitwhen u == null
                call SetUnitColor(u, c)
                call GroupRemoveUnit(ENUM_GROUP, u)
            endloop
        endmethod
   
        static method onLeave takes nothing returns boolean
            local thistype p  = thistype[GetTriggerPlayer()]
            local integer i   = .PlayingPlayerIndex[p.id]
       
            // clean up
            call ForceRemovePlayer(FORCE_PLAYING, p.toPlayer())
       
            // recycle index
            set .AmountPlaying = .AmountPlaying - 1
            set .PlayingPlayerIndex[i] = .PlayingPlayerIndex[.AmountPlaying]
            set .PlayingPlayer[i] = .PlayingPlayer[.AmountPlaying]
           
            if (.AmountPlaying == 1) then
                set p.prev.next = User.NULL
                set p.next.prev = User.NULL
            else
                set p.prev.next = p.next
                set p.next.prev = p.prev
            endif

            set .last = .PlayingPlayer[.AmountPlaying]
           
            set p.isPlaying = false
       
            return false
        endmethod
   
        implement PlayerUtilsInit
   
    endstruct

    private module PlayerUtilsInit
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            local thistype p
       
            set thistype.Local   = GetLocalPlayer()
            set thistype.LocalId = GetPlayerId(thistype.Local)
       
            set OriginalHex[0]  = "|cffff0303"
            set OriginalHex[1]  = "|cff0042ff"
            set OriginalHex[2]  = "|cff1ce6b9"
            set OriginalHex[3]  = "|cff540081"
            set OriginalHex[4]  = "|cfffffc01"
            set OriginalHex[5]  = "|cfffe8a0e"
            set OriginalHex[6]  = "|cff20c000"
            set OriginalHex[7]  = "|cffe55bb0"
            set OriginalHex[8]  = "|cff959697"
            set OriginalHex[9]  = "|cff7ebff1"
            set OriginalHex[10] = "|cff106246"
            set OriginalHex[11] = "|cff4e2a04"
         
            set thistype.first = User.NULL

            loop
                exitwhen i == 12

                set p         = User(i)
                set p.handle  = Player(i)
                set p.id      = i
           
                set thistype.Color[i] = GetPlayerColor(p.handle)
                set CurrentColor[i] = thistype.Color[i]
             
                if (GetPlayerController(p.handle) == MAP_CONTROL_USER and GetPlayerSlotState(p.handle) == PLAYER_SLOT_STATE_PLAYING) then

                    set .PlayingPlayer[AmountPlaying] = p
                    set .PlayingPlayerIndex[i] = .AmountPlaying
                   
                   set .last = i
                   
                    if (.first == User.NULL) then
                        set .first = i
                        set User(i).next = User.NULL
                        set User(i).prev = User.NULL
                    else
                        set User(i).prev = PlayingPlayer[AmountPlaying-1].id
                        set PlayingPlayer[AmountPlaying-1].next = User(i)
                        set User(i).next = User.NULL
                    endif

                    set p.isPlaying = true
               
                    call TriggerRegisterPlayerEvent(t, p.handle, EVENT_PLAYER_LEAVE)
                    call ForceAddPlayer(FORCE_PLAYING, p.handle)
               
                    set Hex[p] = OriginalHex[GetHandleId(thistype.Color[i])]
               
                    set .AmountPlaying = .AmountPlaying + 1

                endif
           
                set Name[p] = GetPlayerName(p.handle)
                set p.originalName=Name[p]
           
                set i = i + 1
            endloop
       
            call TriggerAddCondition(t, Filter(function thistype.onLeave))
        endmethod
    endmodule

    //===========================================================================


    static if (ARRAY_LOOKUP) then
        static if (HOOK_SAFETY) then
            private function SetPlayerNameHook takes player whichPlayer, string name returns nothing
                set Name[GetPlayerId(whichPlayer)] = name
            endfunction
       
            private function SetPlayerColorHook takes player whichPlayer, playercolor color returns nothing
                local User p = User[whichPlayer]
           
                set Hex[p] = OriginalHex[GetHandleId(color)]
                set CurrentColor[p] = color
           
                static if (AUTO_COLOR_UNITS) then
                    call p.colorUnits(color)
                endif
            endfunction
       
            hook SetPlayerName SetPlayerNameHook
            hook SetPlayerColor SetPlayerColorHook
        endif
    endif

endlibrary
library Camera requires UIMath, PlayerUtils

    globals
        // configuration  
       
        // location on the map where the terrain is the default level
        constant real CHECK_DELTAZ_X = 584
        constant real CHECK_DELTAZ_Y = -768
       
        constant real SCREEN_WIDTH  = 0.544
        constant real SCREEN_HEIGHT = 0.302
       
        constant real SCREEN_ASPECT_RATIO = SCREEN_WIDTH/SCREEN_HEIGHT
       
        constant integer CAMERA_DUMMY_TYPE  = 'uidm'
        constant player CAMERA_DUMMY_PLAYER = Player(bj_PLAYER_NEUTRAL_EXTRA)
        // endconfig
       
        public unit array DummyUnit
        private real DeltaZ = 0
    endglobals

    function GetCameraDeltaZ takes nothing returns real
        return DeltaZ
    endfunction
   
    private function Matrix4Perspective1 takes MATRIX4 Output, real fovy, real Aspect, real zn, real zf returns MATRIX4
        return Output.SetValues(2*zn/fovy,0,0,0,0,2*zn/Aspect,0,0,0,0,zf/(zf-zn),1,0,0,zn*zf/(zn-zf),0)
    endfunction

    private function Matrix4Perspective2 takes MATRIX4 Output, real n, real f, real r, real l, real t, real b returns MATRIX4
        return Output.SetValues(2*n/(r-l), 0, (r+l)/(r-l), 0, 0, 2*n/(t-b), (t+b)/(t-b), 0, 0, 0, -(f+n)/(f-n), -2*f*n/(f-n), 0, 0, -1, 0)
    endfunction

    private function Matrix4Look takes MATRIX4 Output, VECTOR3 PosCamera, VECTOR3 AxisX, VECTOR3 AxisY, VECTOR3 AxisZ returns MATRIX4
        return Output.SetValues(AxisX.x,AxisY.x,AxisZ.x,0,AxisX.y,AxisY.y,AxisZ.y,0,AxisX.z,AxisY.z,AxisZ.z,0,-Vec3Dot(AxisX, PosCamera),-Vec3Dot(AxisY, PosCamera),-Vec3Dot(AxisZ, PosCamera),1)
    endfunction

    struct Camera
        VECTOR3 eye
        VECTOR3 at
        unit target
        real distance
        real yaw
        real pitch
        real roll
        VECTOR3 axisX
        VECTOR3 axisY
        VECTOR3 axisZ
        private MATRIX4 view
        private MATRIX4 projection
        private boolean change
        integer customValue
       
        method win2World takes real X, real Y, real Range returns VECTOR3
            local VECTOR3 Output = VECTOR3.create()
            set Output.x = .eye.x+.axisZ.x*Range+X*.axisX.x*SCREEN_WIDTH*Range+Y*.axisY.x*SCREEN_HEIGHT*Range
            set Output.y = .eye.y+.axisZ.y*Range+X*.axisX.y*SCREEN_WIDTH*Range+Y*.axisY.y*SCREEN_HEIGHT*Range
            set Output.z = .eye.z+.axisZ.z*Range+X*.axisX.z*SCREEN_WIDTH*Range+Y*.axisY.z*SCREEN_HEIGHT*Range
            return Output
        endmethod

        method world2Win takes real X, real Y, real Z returns VECTOR3
            local VECTOR3 Pos = VECTOR3.New_1(X, Y, Z)
            local boolean b
            call Vec3Transform_2(Pos, Pos, .view)
            set b = Pos.z < 0
            call Vec3Transform_2(Pos, Pos, .projection)
            if b then
                set Pos.z = -Pos.z
            endif
            return Pos
        endmethod
       
        private method updateDistanceYawPitch takes nothing returns nothing
            local real dx = .at.x-.eye.x
            local real dy = .at.y-.eye.y
            local real dz = .at.z-.eye.z
            local real len2d
            set .distance = SquareRoot(dx*dx+dy*dy+dz*dz)
            set .yaw = Atan2(dy, dx)
            set len2d = SquareRoot(dx*dx+dy*dy)
            set .pitch = Atan2(dz, len2d)
        endmethod
       
        private method updateAxisMatrix takes nothing returns nothing
            local MATRIX3 mat
            call Vec3Normalize(.axisZ, Vec3Subtract(.axisZ, .at, .eye))
            set mat = Matrix3RotationAxis(MATRIX3.create(), .axisZ, -.roll)
            call Vec3Normalize(.axisX, Vec3Cross(.axisX, .axisZ, VECTOR3.oneZ))
            call Vec3Transform_1(.axisY, Vec3Cross(.axisY, .axisX, .axisZ), mat)
            call Vec3Transform_1(.axisX, .axisX, mat)
            call Matrix4Look(.view, .eye, .axisX, .axisY, .axisZ)
            call mat.destroy()
        endmethod

        method applyCameraForPlayer takes player p, boolean ignoreChange returns boolean
            if GetLocalPlayer() == p then
                call SetCameraField(CAMERA_FIELD_ROTATION, .yaw*bj_RADTODEG, 0)
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, .pitch*bj_RADTODEG, 0)
                call SetCameraField(CAMERA_FIELD_ROLL, .roll*bj_RADTODEG, 0)
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, .distance, 0)
                call SetCameraTargetController(DummyUnit[GetPlayerId(p)], .at.x, .at.y, false)
                call SetCameraField(CAMERA_FIELD_ZOFFSET, .at.z-DeltaZ, 0)
            endif
            if .change or ignoreChange then
                set .change = false
                return true
            endif
            return false
        endmethod

        method setPosition takes real x, real y, real z returns nothing
            local real dx = x-.at.x
            local real dy = y-.at.y
            local real dz = z-.at.z
            set .eye.x = .eye.x+dx
            set .eye.y = .eye.y+dy
            set .eye.z = .eye.z+dz
            set .at.x = x
            set .at.y = y
            set .at.z = z
            set .change = true
        endmethod
       
        method setEyeAndAt takes real ex, real ey, real ez, real tx, real ty, real tz returns nothing
            set .eye.x = ex
            set .eye.y = ey
            set .eye.z = ez
            set .at.x = tx
            set .at.y = ty
            set .at.z = tz
            call .updateDistanceYawPitch()
            call .updateAxisMatrix()
            set .change = true
        endmethod
       
        method setYawPitchRoll takes real yaw, real pitch, real roll, boolean EyeLock returns nothing
            local real Z = .distance*Sin(pitch)
            local real XY = .distance*Cos(pitch)
            local real X = XY*Cos(yaw)
            local real Y = XY*Sin(yaw)
            set .yaw = yaw
            set .pitch = pitch
            set .roll = roll
            if EyeLock then
                set .at.x = .eye.x+X
                set .at.y = .eye.y+Y
                set .at.z = .eye.z+Z
            else
                set .eye.x = .at.x-X
                set .eye.y = .at.y-Y
                set .eye.z = .at.z-Z
            endif
            call .updateAxisMatrix()
            set .change = true
        endmethod
       
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
           
            set .customValue = 0
            set .change = true
            set .eye = VECTOR3.New_1(0.0,-922.668,DeltaZ+1367.912)
            set .at = VECTOR3.New_1(0, 0, DeltaZ)
            set .distance = 0
            set .yaw = 0
            set .pitch = 0
            set .roll = 0
            set .axisX = VECTOR3.create()
            set .axisY = VECTOR3.create()
            set .axisZ = VECTOR3.create()
            set .view  = MATRIX4.create()
            set .projection = Matrix4Perspective2(MATRIX4.create(), 0.5, 10000, -SCREEN_WIDTH/2, SCREEN_WIDTH/2, -SCREEN_HEIGHT/2, SCREEN_HEIGHT/2)
            call .updateDistanceYawPitch()
            call .updateAxisMatrix()
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call .eye.destroy()
            call .at.destroy()
            call .axisX.destroy()
            call .axisY.destroy()
            call .axisZ.destroy()
            call .view.destroy()
            call .projection.destroy()
            call this.destroy()
        endmethod
       
    endstruct

    globals
        private real TempX = 0
        private real TempY = 0
    endglobals

    private function InitDeltaZ takes nothing returns nothing
        local timer t = GetExpiredTimer()
       
        set TempX = GetCameraTargetPositionX()
        set TempY = GetCameraTargetPositionY()
        call SetCameraPosition(CHECK_DELTAZ_X, CHECK_DELTAZ_Y)
        set DeltaZ = GetCameraTargetPositionZ()
        call SetCameraPosition(TempX, TempY)

        static if (LIBRARY_TimerUtils) then
            call ReleaseTimer(t)
        else
            call DestroyTimer(t)
        endif
           
        set t = null
    endfunction
   
    private module CamInitModule
        private static method onInit takes nothing returns nothing
            local integer i = 0
            local User user
           
            loop
                exitwhen i == User.AmountPlaying
               
                set user = User.fromPlaying(i)
               
                set DummyUnit[user.id] = CreateUnit(CAMERA_DUMMY_PLAYER, CAMERA_DUMMY_TYPE, 0, 0, 0)
                call ShowUnit(DummyUnit[user.id], false)
                call PauseUnit(DummyUnit[user.id], true)
               
                set i = i + 1
            endloop
           
            // init delta z
            static if (LIBRARY_TimerUtils) then
                call TimerStart(NewTimer(), 0., false, function InitDeltaZ)
            else
                call TimerStart(CreateTimer(), 0., false, function InitDeltaZ)
            endif
        endmethod
    endmodule
   
    private struct CamInit
        implement CamInitModule
    endstruct
   
endlibrary
library UIMath initializer Init

    public function R2I_N takes real r returns integer
        local integer i = R2I(r)
        if (r < I2R(i)+0.5) then
            return i
        else
            return i+1
        endif
    endfunction

    struct VECTOR3
        static VECTOR3 Zero
        static VECTOR3 oneX
        static VECTOR3 oneY
        static VECTOR3 oneZ
        real x
        real y
        real z
       
        static method New_0 takes nothing returns VECTOR3
            local VECTOR3 this = VECTOR3.create()
            set .x = 0
            set .y = 0
            set .z = 0
            return this
        endmethod
       
        static method New_1 takes real x, real y, real z returns VECTOR3
            local VECTOR3 this = VECTOR3.create()
            set .x = x
            set .y = y
            set .z = z
            return this
        endmethod
       
        static method New_2 takes VECTOR3 v returns VECTOR3
            local VECTOR3 this = VECTOR3.create()
            set .x = v.x
            set .y = v.y
            set .z = v.z
            return this
        endmethod
       
        method SetValues takes real x, real y, real z returns VECTOR3
            set .x = x
            set .y = y
            set .z = z
            return this
        endmethod
       
        method Length takes nothing returns real
            return SquareRoot(.x*.x+.y*.y+.z*.z)
        endmethod
       
        method LengthSq takes nothing returns real
            return .x*.x+.y*.y+.z*.z
        endmethod
       
        method ToString takes nothing returns string
            return "Vector3 id "+I2S(this) + "\nx = "+R2S(.x)+"   y = "+R2S(.y)+"   z = "+R2S(.z)
        endmethod
       
    endstruct

    function Vec3Add takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
        set Output.x = v1.x + v2.x
        set Output.y = v1.y + v2.y
        set Output.z = v1.z + v2.z
        return Output
    endfunction

    function Vec3Subtract takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
        set Output.x = v1.x - v2.x
        set Output.y = v1.y - v2.y
        set Output.z = v1.z - v2.z
        return Output
    endfunction
       
    function Vec3Scale takes VECTOR3 Output, VECTOR3 v, real r returns VECTOR3
        set Output.x = v.x * r
        set Output.y = v.y * r
        set Output.z = v.z * r
        return Output
    endfunction
       
    function Vec3Division takes VECTOR3 Output, VECTOR3 v, real r returns VECTOR3
        set Output.x = v.x / r
        set Output.y = v.y / r
        set Output.z = v.z / r
        return Output
    endfunction

    function Vec3Length takes VECTOR3 v returns real
        return SquareRoot(v.x*v.x+v.y*v.y+v.z*v.z)
    endfunction

    function Vec3LengthSq takes VECTOR3 v returns real
        return v.x*v.x+v.y*v.y+v.z*v.z
    endfunction

    function Vec3Normalize takes VECTOR3 Output, VECTOR3 v returns VECTOR3
        local real len = SquareRoot(v.x*v.x+v.y*v.y+v.z*v.z)
        set Output.x = v.x/len
        set Output.y = v.y/len
        set Output.z = v.z/len
        return Output
    endfunction

    function Vec3Dot takes VECTOR3 v1, VECTOR3 v2 returns real
        return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
    endfunction

    function Vec3Cross takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
        local real y  = v1.z * v2.x - v1.x * v2.z
        local real z = v1.x * v2.y - v1.y * v2.x
        set Output.x = v1.y * v2.z - v1.z * v2.y
        set Output.y = y
        set Output.z = z
        return Output
    endfunction

    function Vec3Transform_1 takes VECTOR3 Output, VECTOR3 v, MATRIX3 m returns VECTOR3
        local real y = v.x*m.m12+v.y*m.m22+v.z*m.m32
        local real z = v.x*m.m13+v.y*m.m23+v.z*m.m33
        set Output.x = v.x*m.m11+v.y*m.m21+v.z*m.m31
        set Output.y = y
        set Output.z = z
        return Output
    endfunction

    function Vec3Transform_2 takes VECTOR3 Output, VECTOR3 v, MATRIX4 m returns VECTOR3
        local real y = v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42
        local real z = v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43
        local real w = v.x*m.m14+v.y*m.m24+v.z*m.m34+m.m44
        set Output.x = (v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41)/w
        set Output.y = y/w
        set Output.z = z/w
        return Output
    endfunction


    struct MATRIX3
        static MATRIX3 Zero
        static MATRIX3 E
        real m11
        real m12
        real m13
        real m21
        real m22
        real m23
        real m31
        real m32
        real m33
       
        static method New_0 takes nothing returns MATRIX3
            local MATRIX3 this = MATRIX3.create()
            set .m11 = 0
            set .m12 = 0
            set .m13 = 0
            set .m21 = 0
            set .m22 = 0
            set .m23 = 0
            set .m31 = 0
            set .m32 = 0
            set .m33 = 0
            return this
        endmethod
       
        static method New_1 takes real r11, real r12, real r13, real r21, real r22, real r23, real r31, real r32, real r33 returns MATRIX3
            local MATRIX3 this = MATRIX3.create()
            set .m11 = r11
            set .m12 = r12
            set .m13 = r13
            set .m21 = r21
            set .m22 = r22
            set .m23 = r23
            set .m31 = r31
            set .m32 = r32
            set .m33 = r33
            return this
        endmethod
       
        static method New_2 takes MATRIX3 m returns MATRIX3
            local MATRIX3 this = MATRIX3.create()
            set .m11 = m.m11
            set .m12 = m.m12
            set .m13 = m.m13
            set .m21 = m.m21
            set .m22 = m.m22
            set .m23 = m.m23
            set .m31 = m.m31
            set .m32 = m.m32
            set .m33 = m.m33
            return this
        endmethod
       
        method SetValues takes real r11, real r12, real r13, real r21, real r22, real r23, real r31, real r32, real r33 returns MATRIX3
            set .m11 = r11
            set .m12 = r12
            set .m13 = r13
            set .m21 = r21
            set .m22 = r22
            set .m23 = r23
            set .m31 = r31
            set .m32 = r32
            set .m33 = r33
            return this
        endmethod
     
        method ToString takes nothing returns string
            return "Matrux3 id "+I2S(this)+"\n"+R2S(.m11)+"  "+R2S(.m12)+"  "+R2S(.m13)+"\n"+R2S(.m21)+"  "+R2S(.m22)+"  "+R2S(.m23)+"\n"+R2S(.m31)+"  "+R2S(.m32)+"  "+R2S(.m33)
        endmethod
       
    endstruct

    function Matrix3Multiply takes MATRIX3 Output, MATRIX3 M1, MATRIX3 M2 returns MATRIX3  
        return Output.SetValues(M1.m11*M2.m11+M1.m21*M2.m12+M1.m31*M2.m13,M1.m12*M2.m11+M1.m22*M2.m12+M1.m32*M2.m13,M1.m13*M2.m11+M1.m23*M2.m12+M1.m33*M2.m13,M1.m11*M2.m21+M1.m21*M2.m22+M1.m31*M2.m23,M1.m12*M2.m21+M1.m22*M2.m22+M1.m32*M2.m23,M1.m13*M2.m21+M1.m23*M2.m22+M1.m33*M2.m23,M1.m11*M2.m31+M1.m21*M2.m32+M1.m31*M2.m33,M1.m12*M2.m31+M1.m22*M2.m32+M1.m32*M2.m33,M1.m13*M2.m31+M1.m23*M2.m32+M1.m33*M2.m33)
    endfunction

    function Matrix3Scaling takes MATRIX3 Output, real x, real y, real z returns MATRIX3
        return Output.SetValues(x,0,0,0,y,0,0,0,z)
    endfunction

    function Matrix3RotationX takes MATRIX3 Output, real a returns MATRIX3
        return Output.SetValues(1,0,0,0,Cos(a),-Sin(a),0,Sin(a),Cos(a))
    endfunction

    function Matrix3RotationY takes MATRIX3 Output, real a returns MATRIX3
        return Output.SetValues(Cos(a),0,Sin(a),0,1,0,-Sin(a),0,Cos(a))
    endfunction

    function Matrix3RotationZ takes MATRIX3 Output, real a returns MATRIX3
        return Output.SetValues(Cos(a),-Sin(a),0,Sin(a),Cos(a),0,0,0,1)
    endfunction

    function Matrix3RotationAxis takes MATRIX3 Output, VECTOR3 v, real a returns MATRIX3
        local real cosa = Cos(a)
        local real sina = Sin(a)
        return Output.SetValues(cosa+(1-cosa)*v.x*v.x,(1-cosa)*v.x*v.y-sina*v.z,(1-cosa)*v.x*v.z+sina*v.y,(1-cosa)*v.y*v.x+sina*v.z,cosa+(1-cosa)*v.y*v.y,(1-cosa)*v.y*v.z-sina*v.x,(1-cosa)*v.z*v.x-sina*v.y,(1-cosa)*v.z*v.y+sina*v.x,cosa+(1-cosa)*v.z*v.z)
    endfunction

    function Matrix3RotationYawPitchRoll takes MATRIX3 Output, real Yaw, real Pitch, real Roll returns MATRIX3
        local real cosa = Cos(Yaw)
        local real sina = Sin(Yaw)
        local real cosb = Cos(Pitch)
        local real sinb = Sin(Pitch)
        local real cosy = Cos(Roll)
        local real siny = Sin(Roll)
        return Output.SetValues(cosa*cosb,cosa*sinb*siny-sina*cosy,cosa*sinb*cosy+sina*siny,sina*cosb,sina*sinb*siny+cosa*cosy,sina*sinb*cosy-cosa*siny,-sinb,cosb*siny,cosb*cosy)
    endfunction

    struct MATRIX4
        static MATRIX4 Zero
        static MATRIX4 E
        real m11
        real m12
        real m13
        real m14
        real m21
        real m22
        real m23
        real m24
        real m31
        real m32
        real m33
        real m34
        real m41
        real m42
        real m43
        real m44
       
        static method New_0 takes nothing returns MATRIX4
            local MATRIX4 this = MATRIX4.create()
            set .m11 = 0
            set .m12 = 0
            set .m13 = 0
            set .m14 = 0
            set .m21 = 0
            set .m22 = 0
            set .m23 = 0
            set .m24 = 0
            set .m31 = 0
            set .m32 = 0
            set .m33 = 0
            set .m34 = 0
            set .m41 = 0
            set .m42 = 0
            set .m43 = 0
            set .m44 = 0
            return this
        endmethod
       
        static method New_1 takes real r11, real r12, real r13, real r14, real r21, real r22, real r23, real r24, real r31, real r32, real r33, real r34, real r41, real r42, real r43, real r44 returns MATRIX4
            local MATRIX4 this = MATRIX4.create()
            set .m11 = r11
            set .m12 = r12
            set .m13 = r13
            set .m14 = r14
            set .m21 = r21
            set .m22 = r22
            set .m23 = r23
            set .m24 = r24
            set .m31 = r31
            set .m32 = r32
            set .m33 = r33
            set .m34 = r34
            set .m41 = r41
            set .m42 = r42
            set .m43 = r43
            set .m44 = r44
            return this
        endmethod
       
        static method New_2 takes MATRIX4 m returns MATRIX4
            local MATRIX4 this = MATRIX4.create()
            set .m11 = m.m11
            set .m12 = m.m12
            set .m13 = m.m13
            set .m14 = m.m14
            set .m21 = m.m21
            set .m22 = m.m22
            set .m23 = m.m23
            set .m24 = m.m24
            set .m31 = m.m31
            set .m32 = m.m32
            set .m33 = m.m33
            set .m34 = m.m34
            set .m41 = m.m41
            set .m42 = m.m42
            set .m43 = m.m43
            set .m44 = m.m44
            return this
        endmethod
       
        static method New_3 takes MATRIX3 m returns MATRIX4
            local MATRIX4 this = MATRIX4.create()
            set .m11 = m.m11
            set .m12 = m.m12
            set .m13 = m.m13
            set .m14 = 0
            set .m21 = m.m21
            set .m22 = m.m22
            set .m23 = m.m23
            set .m24 = 0
            set .m31 = m.m31
            set .m32 = m.m32
            set .m33 = m.m33
            set .m34 = 0
            set .m41 = 0
            set .m42 = 0
            set .m43 = 0
            set .m44 = 1
            return this
        endmethod
       
        method SetValues takes real r11, real r12, real r13, real r14, real r21, real r22, real r23, real r24, real r31, real r32, real r33, real r34, real r41, real r42, real r43, real r44 returns MATRIX4
            set .m11 = r11
            set .m12 = r12
            set .m13 = r13
            set .m14 = r14
            set .m21 = r21
            set .m22 = r22
            set .m23 = r23
            set .m24 = r24
            set .m31 = r31
            set .m32 = r32
            set .m33 = r33
            set .m34 = r34
            set .m41 = r41
            set .m42 = r42
            set .m43 = r43
            set .m44 = r44
            return this
        endmethod
     
        method ToString takes nothing returns string
            return "Matrux4 id "+I2S(this)+"\n"+R2S(.m11)+"  "+R2S(.m12)+"  "+R2S(.m13)+"  "+R2S(.m14)+"\n"+R2S(.m21)+"  "+R2S(.m22)+"  "+R2S(.m23)+"  "+R2S(.m24)+"\n"+R2S(.m31)+"  "+R2S(.m32)+"  "+R2S(.m33)+"  "+R2S(.m34)+"\n"+R2S(.m41)+"  "+R2S(.m42)+"  "+R2S(.m43)+"  "+R2S(.m44)
        endmethod
       
    endstruct

    function Matrix4Multiply takes MATRIX4 Output, MATRIX4 M1, MATRIX4 M2 returns MATRIX4
        return Output.SetValues(M1.m11*M2.m11+M1.m21*M2.m12+M1.m31*M2.m13+M1.m41*M2.m14,M1.m12*M2.m11+M1.m22*M2.m12+M1.m32*M2.m13+M1.m42*M2.m14,M1.m13*M2.m11+M1.m23*M2.m12+M1.m33*M2.m13+M1.m43*M2.m14,M1.m14*M2.m11+M1.m24*M2.m12+M1.m34*M2.m13+M1.m44*M2.m14,M1.m11*M2.m21+M1.m21*M2.m22+M1.m31*M2.m23+M1.m41*M2.m24,M1.m12*M2.m21+M1.m22*M2.m22+M1.m32*M2.m23+M1.m42*M2.m24,M1.m13*M2.m21+M1.m23*M2.m22+M1.m33*M2.m23+M1.m43*M2.m24,M1.m14*M2.m21+M1.m24*M2.m22+M1.m34*M2.m23+M1.m44*M2.m24,M1.m11*M2.m31+M1.m21*M2.m32+M1.m31*M2.m33+M1.m41*M2.m34,M1.m12*M2.m31+M1.m22*M2.m32+M1.m32*M2.m33+M1.m42*M2.m34,M1.m13*M2.m31+M1.m23*M2.m32+M1.m33*M2.m33+M1.m43*M2.m34,M1.m14*M2.m31+M1.m24*M2.m32+M1.m34*M2.m33+M1.m44*M2.m34,M1.m11*M2.m41+M1.m21*M2.m42+M1.m31*M2.m43+M1.m41*M2.m44,M1.m12*M2.m41+M1.m22*M2.m42+M1.m32*M2.m43+M1.m42*M2.m44,M1.m13*M2.m41+M1.m23*M2.m42+M1.m33*M2.m43+M1.m43*M2.m44,M1.m14*M2.m41+M1.m24*M2.m42+M1.m34*M2.m43+M1.m44*M2.m44)
    endfunction

    private function Init takes nothing returns nothing
       set VECTOR3.Zero = VECTOR3.New_0()
       set VECTOR3.oneX = VECTOR3.New_1(1,0,0)
       set VECTOR3.oneY = VECTOR3.New_1(0,1,0)
       set VECTOR3.oneZ = VECTOR3.New_1(0,0,1)
       set MATRIX3.Zero = MATRIX3.New_0()
       set MATRIX3.E = MATRIX3.New_1(1,0,0,0,1,0,0,0,1)
       set MATRIX4.Zero = MATRIX4.New_0()
       set MATRIX4.E = MATRIX4.New_1(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)
    endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//*  Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//*  To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************

//==================================================
   globals
       // High enough so the unit is no longer visible, low enough so the
       // game doesn't crash...
       //
       // I think you need 0.0 or soemthing negative prior to patch 1.22
       //
       private constant real EXTRA = 700.0
   endglobals

   //=========================================================================================
   globals
       private real maxx
       private real maxy
       private real minx
       private real miny
   endglobals

   //=======================================================================
   private function dis takes nothing returns nothing
    local unit u=GetTriggerUnit()
    local real x=GetUnitX(u)
    local real y=GetUnitY(u)
    local real x2=x
    local real y2=y
       if(x>maxx) then
           set x=maxx
       elseif(x<minx) then
           set x=minx
       endif
       if(y>maxy) then
           set y=maxy
       elseif(y<miny) then
           set y=miny
       endif
       if (x != x2) then
        call SetUnitX(u,x)
       endif
       if (y != y2) then
         call SetUnitY(u,y)
       endif
    set u=null
   endfunction

   private function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    local region  r=CreateRegion()
    local rect    rc

       set minx=GetCameraBoundMinX() - EXTRA
       set miny=GetCameraBoundMinY() - EXTRA
       set maxx=GetCameraBoundMaxX() + EXTRA
       set maxy=GetCameraBoundMaxY() + EXTRA
       set rc=Rect(minx,miny,maxx,maxy)
       call RegionAddRect(r, rc)
       call RemoveRect(rc)

       call TriggerRegisterLeaveRegion(t,r, null)
       call TriggerAddAction(t, function dis)

    //this is not necessary but I'll do it anyway:
    set t=null
    set r=null
    set rc=null
   endfunction
endlibrary