1. Are you planning to upload your awesome map 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. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  5. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  6. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  7. The results are out! Check them out.
    Dismiss Notice
  8. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  9. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  10. 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

Battle Tanks 8.80.w3x
Variables
Main and Gameplay
Libraries
Settings
---------------------------
Timer and Attachments
BT Framework
Unit Types
General mapwide functions
---------------------------
script.ini
Bounty
Damage Detection System
Effects
Enumerations
Projectile System
Teleports
Trace
----- External libs -----
ArmorUtils
Dummy Management
Logartihm
Walkability
Paladons vJass Scripts Addon
Paladons Lightning Manager
Initialization
Init Main
Init GUI
Init Players
Game Mode
Mode Hostbot
Mode Dialog Options
Mode Dialog Main
Mode Manager
Start Game
VIP List
BetaMessages
Game
Control Point
Fog Modifier
Mill
Stat Progress
Timer
Tree Death
Victory
Victory Stats
Victory Cinematic
Weather Change
Players
Actions Per Minute
Anti Shared Control
Anti Team Attack
Camera
DenialOfPlay
Gold Distribution
Kick Library
Multiboards
Multiboard OpenClose
Player Income
Player Remove
Player Leaves
Start Killboard
Commands
String Evaluation
Command Event
Jass Commands
Stats
Chat
Team
Give
Kick Start
Kick Vote
Kick End
GiveUp Vote
GiveUp End
Playerswap
Afk Return
Bot Communication
Veritas Code
Excubitor Code
BTStats
BTDLS
Endgame Stats
---------------------------
LeagueInit
LeagueUpdate
LeagueSend
LeagueDestroy
Unit Management
Buildings
Add Towers to Ruin
Build Only 3 Factories
Building Established
Building Start
Build Tower
Building Destroyed
Tower Protection
Multipage Shops
Trader Market Info
Units
Unit Anti Flee
Unit Anti Flee Move
Unit Dies
Unit Spawn
Unit Upgrade
Aura Upgrade
Creep Wave Buffs
---------------------------
Unit Enters Left 1
Unit Enters Left 2
Unit Enters Left 3
Unit Enters Right 1
Unit Enters Right 2
Unit Enters Right 3
Unit Enters Center
Unit Enters Center Team 1
Unit Enters Center Team 2
Unit Enters Air Team 1
Unit Enters Air Team 2
Tanks
Assist Detection
Debug HP Regen
Magic Armor
Strategic Loc
Tank Change Library
Update Speed And Fire
Vehicle Buy
Vehicle Level
Vehicle Dies
Weapon Range
XP and Bounty
Custom Weapon System
Weapon Init and Update
Weapon Holder
Weapon
Game Content
Item Management
Items Full Resale Library
Items Buy
Items Take
Items Sell
Items Drop
Items Healer
Items Upgrade
Items Combine
Items Explosives Upgrade
Items Tinker Tower Weapon
Items Module Remove
Items Module Remove 2
Items Use Stacked
Show the Owners
Take items from Force
Items Abilities
Advanced Troop Command
Battery
Black Sun Project
Black Sun Project UnPause
Bomb Channel
Bomb Setting
Bomb Explosion
Build Troop Command Center
Blocking Armor
Center Info
Concealing Engine
Defuse Pack
Emergency Repair
Energy Converter
Explosive
Extended Repair Kit
Illusion Pack
Kinetic Shield
Maintenance
Mass Converter
Mine Conditions
Mine Explodes
Mine Place
Mortar Team Command
Negator Pack
Net Launcher
Orbital Command
Plasma Discharger
Radar
Recharge
Repair Kit
Siege Pack
Smoke Generator
Spawn Delay Register
Teleport Breaker
Test Item
Trader Hunter Pack Speed
Troop Command
Tank Abilities - Early
------ Tinker ------
Build Towers
Build Towers Limit
Tower Modules
Learn TowerModules
------ Exploder ------
EMP
Explosives
Reconstruction
------ Scout ------
Watch Tower
Rune Carving
Defensive Systems
Tank Ensnared
------ Light Tank ------
Triple Shot
Repair
Multi Rocket Shot
------ Helicopter ------
Fire Bomb
------ Anti Grav ------
EMP Torpedos
Jump
Dimension Shift
------ Medivac ------
Superthrust
Life Converter
Decoy
Support Systems
------ Distributor ------
HeatSeeking Missile
Reactive Armor
Tankers Little Helper
Sharing Is Caring
------ Demolisher ------
Artillery Shot
Burning Oil
------ Raider ------
Static Charge
Shape Memory Alloy
Ricochet
HitnRun
------ Trader ------
Trader Init
Trader Floating
Trader Trading
Trader Trade Skill
------ Shredder ------
Metal Stars
Magnetic Pull
Goblin Synergy
Unit Teleport
Tank Abilities - Mid
------ Thunder Tank ------
Lightning Strike
Thunder Storm
Chain Lightning
------ Storm Tank ------
Energy Leash
Storm Shock
Air Mines
Eye of the Storm
Air Cannon
------ Air Ship -------
Electro Shocker
------ Guard ------
Roots
Summon Treants
Natural Energy
Entangling Roots
------ Ghost Tank ------
Soul Transfer
Soul Collector
Soul Restore
------ Heavy Tank ------
Heavy Armor
Hail of Bombs
------ Architect ------
Splash Cannon
Cannon Module
Freezing Missile
Build Portal
Portal Teleport
Build Pacifista
------ Goblin Tank ------
Thunder Hammer
Thunder Hammer Speed
Shockwave
Tank Abilities - Late
------ Earth Robot ------
Dustwave
Granitic Defense
Transparent Armor
Earthquake
------ Darkness Tank ------
Orb of Darkness
Conservation
Energy Leak
Darkness Sphere
Missile Battey
------ Sky Tank ------
Lightning Quake
Energy Bomb
------ Hunter ------
Main Gun
Acid Cloud
Gravity Grenade
Gravity Grenade Dummy Cast
------ Demon Tank ------
Banish
Demon Fire
Hell Fire
------ Frost Robot ------
Ice Prison
------ Goliath ------
LookAt
Heavy Tank Cannon
Offroad Engine
Take Aim
Mobile Shields
Devastator Shot
Devastator Shot Animation
------ Sky Fortress ------
Turbo Boost
Bomb Carpet
System Overload
------ Infernal Robot ------
Chaos Teleport Start
Chaos Teleport
Chaos Wave
Chaos Servant
Infernal Fire Rain
------ Titan ------
Lightning Obelisk
Devil Fire
Unstable Projectiles
Orbital Strike
Keep On Fighting
General Abilities
Ability End
CP Teleport Start
CP Teleport
DeactivateTeleportItems
Destroy Towers Mines and Portals
Factory Heal
Invisibility
Learn Skills
Move Summoned
Random Tank
Repack Building
Show Tip
Spell Effect Event
Summon Troops
Tank Factory Teleport
Target Factory Heal
Tech Mech Detector
Tech Mech Net Launcher
Tech Mech Repair
Tech Mech Teleport Breaker
Teleport
Troop Command Center Rally Point
Artificial Intelligence
AI
AI General Functions
AI Start
AI InitTrig
AI Move
AI Fight
AI Level
AI Skills
AI Only Hero Skills
AI Spells
AI Weapons
AI Weapons 1
AI Weapons 2
AI Weapons 3
AI Weapons 4
AI ItemTrader
AI Respawn
AI Die
AI 2.0
ThreatMap Debug
AI Debug Info
Show Team Stats
---------------------------
AI Interface
AI Functions
AI Group Conditions
AI Threat Map
AI Weapon Ranges
AI Combat Range
---------------------------
AI Global Planner
AI Shopping
AI Skill
AI Situation
AI Combat
AI Behaviour
AI Core
AI Loop
---------------------------
AI Init
//TESH.scrollpos=0
//TESH.alwaysfold=0
Name Type Is Array Initial Value
Actions integer Yes
ActionsLast real Yes
Afk boolean Yes
Afk_Dialog dialog No
AFKTime integer Yes
AI_BuyID integer No
AI_BuyMaxCosts integer No
AI_BuyMinCosts integer No
AI_Command string Yes
AI_DangerLevel real No
AI_Debug boolean Yes true
AI_Debug_Info texttag Yes
AI_Initialized boolean No
AI_IsBot boolean Yes
AI_IsNewBot boolean Yes
AI_Number integer No
AI_SafetyLevel real No
AI_Spell_Aborted boolean No
AI_Tank unit No
Assists integer Yes
AssistSpree integer Yes
Aura_Creeps integer Yes
AV_Bool1 boolean Yes
AV_Bool2 boolean Yes
AV_Effect1 effect Yes
AV_Group1 group Yes
AV_Group2 group Yes
AV_Handles integer Yes
AV_Int1 integer Yes
AV_Int2 integer Yes
AV_Int3 integer Yes
AV_Item1 item Yes
AV_Lightning lightning Yes
AV_Loc1 location Yes
AV_Player1 player Yes
AV_Real1 real Yes
AV_Real2 real Yes
AV_Real3 real Yes
AV_String1 string Yes
AV_String2 string Yes
AV_Tag1 texttag Yes
AV_Timers timer Yes
AV_Triggers trigger Yes
AV_Unit1 unit Yes
AV_Unit2 unit Yes
AV_Unit3 unit Yes
AV_Units unit Yes
BaseFactory unit Yes
BaseLast real Yes
BaseProtTower unit Yes
Cam_Angle real No 90.00
Cam_AngleZ real No 56.00
Cam_Distance real No 2750.00
Color string Yes
Color_Blue integer Yes
Color_Green integer Yes
Color_Red integer Yes
Command_Center integer Yes
Conquest_Button button Yes
Conquest_Dialog dialog No
Control_Point unit Yes
CustomOptions_Button button Yes
CustomOptions_Dialog dialog No
CV boolean No
CV_Timer timer No
CV_Timer_Remain real Yes 0.00
CV_Timer_Stop real Yes
CV_Timer_Team integer No
CV_TimerWindow timerdialog No
Damage_Dealt real Yes
DamageLast real Yes
Deaths integer Yes
Draw boolean No
enumGrp group No
ExtRequirements boolean No
Fog boolean No
Fog_Modifier fogmodifier Yes
Fog_Modifier_Max integer No
Force player Yes
Force_UpgradeGold integer Yes
GameMode integer No 1
GameMode_Button button Yes
GameMode_Dialog dialog No
GameMode_Host player No
GameMode_Name string Yes
GameTime timer No
GaveUp boolean Yes
GiveUp_Count integer Yes
GiveUp_HasAlreadyVoted boolean Yes
GiveUp_Progress boolean Yes
GiveUp_Ready boolean Yes true
GoldFactor integer No 1
GoldFactorBase real No 1.00
Handicap_Ready boolean Yes true
Healer_Effect effect Yes
Healer_Rune item Yes
HighTech boolean No
HQ unit Yes
IllusionPackCasted boolean Yes
Income real Yes
Item_Buy boolean No
Item_Check boolean No true
Item_CreateForHero itemcode Yes
Item_Upgradeable boolean No
JunkyardRect rect Yes
Kick_Count integer Yes
Kick_HasAlreadyVoted boolean Yes
Kick_Player player No
Kick_Progress boolean No
Kick_Ready boolean Yes true
Kick_Timeout integer Yes 60
Kick_Votes_Needed integer No
KickBoard leaderboard No
Kicked boolean Yes
Killboard multiboard Yes
KillingSpree integer Yes
KillLast integer Yes
Kills integer Yes
Language string No
Leaver player No
LeaversToAI boolean No
MaxAssistStreak integer Yes
MaxKillStreak integer Yes
Mode_Command string No
MonopolyCountTrades integer Yes
MonopolyLastTrade real Yes
MonopolyTeam integer Yes
Move_Points location Yes
Multiboard_Escape boolean No true
MultiboardType integer Yes
Multikill_Count integer Yes
Multikill_Sounds sound Yes
NoEffects boolean No true
NoExploder boolean No
NoiseForPlayer real Yes
NoRequirements boolean No
NoTinker boolean No
NoTrader boolean No
NoUpgrades boolean No
ObserverInGame boolean No
OnlyTinker boolean No
p_KickerLeft boolean No
Playable_Map rect No RectNull
Player_Team integer Yes
PlayerIn boolean Yes
PlayerInStart boolean Yes
Players force No
Players_Start force No
Players_Team force Yes
Profit_Creeps real Yes
Profit_Force real Yes
Profit_Income real Yes
Profit_Sold real Yes
Profit_Tanks real Yes
Profit_Trader real Yes
Progress_Bar texttag Yes
Progress_Timer texttag Yes
ProMode boolean No
Reconstruction_Ready boolean Yes true
ReviveCamPan boolean No true
Selfkills integer Yes
Show_WeaponRange real No
Show_WeaponRange_Mode integer No
Show_WeaponRange_Text texttag Yes
Spawn_Points location Yes
SpeedIndicatorNeg integer Yes
SpeedIndicatorPos integer Yes
StartingGold integer No
StatLast real Yes
Stats integer Yes
Stats_Buildings integer Yes
Stats_Chat integer Yes
Stats_CPs integer Yes
Stats_CreepBuy integer Yes
Stats_CreepKills integer Yes
Stats_Trader integer Yes
Stats_Upgrades integer Yes
Support real Yes
Tank unit Yes
Tank_AttachedFire effect Yes
Tank_FireLevel integer Yes
Tank_HP real Yes
Tank_Invisible integer Yes
Tank_Invulnerable integer Yes
Tank_Timer timer Yes
Tank_TimerWindow timerdialog Yes
TankBounty real Yes
TankCosts integer Yes
TankMonopoly boolean No
TC_Food integer Yes
TC_Unit integer Yes
TCC_Mana real Yes
TCC_Max integer No 2
Team_CountPlayers integer Yes
Team_CPs integer Yes 3
Team_Rename integer Yes
Team_Winning integer No
TempGroup group No
TempInt integer No
TempLoc location No
TempRect rect No
Time_Dead real Yes
Time_Value integer No -10
TimerStack timer Yes
TimerStackN integer No
Tips string Yes
TowerModuleUsed boolean Yes
TradeMarket unit Yes
TradeMarket_Page2 unit Yes
Trader_Gold integer Yes
Trader_Wood integer Yes
Treant integer Yes
VehicleFactory unit Yes
VehicleFactory_Page2 unit Yes
Weapon_Basic integer Yes
Weapon_Upgrade integer Yes
WeaponModifier real Yes
WeaponRange integer Yes
Weather_Current integer No
Weather_Effects weathereffect Yes
Weather_Enabled boolean No false
Weather_Timer timer No
//TESH.scrollpos=0
//TESH.alwaysfold=0
    // general game mode / hosting variables
    globals
        constant string  Version        = "880c"
        constant integer FillAIPlayers  = 0         // how much players should be in a single team minimum, missing players will be filled in by the AI
        constant boolean OnlyAI         = false     // when true, an AI will also play for the local player
                 boolean DebugMode      = false     // when true, debug messages, information and commands are available
        constant boolean BetaMode       = false      // when true, the beta messages are enabled
        constant boolean VIPCheck       = true      // when true, only the VIPs may host a beta map online
       
        constant boolean AlternateBounty = true
       
                real InitXPFactor = 1.07            // general XP factor for all players (higher = more XP)

                //! Gold
                real GeneralGoldFactor = 1.00       // general gold factor, increases bounties, changed by game modes
                real IncomeFactor = 1.00            // general passive income factor, does not influence force gold, changed by game modes            
       
                boolean GameOver = false
                boolean LeagueConfirmed = false     // shows if the league bots confirmed that the current game is a league game            
                boolean LeagueImitation = false     // league mode, but without the stats recording on the bots
                boolean OpenBeta = false            // will only be open beta, if it is properly hosted
                boolean SpecialMode = false         // Game mode initialization in relation to the host bots
                boolean VipGame = false             // if a VIP is in game, this allows beta hosting outside of singleplayer and bot hosted games
    endglobals
   
    // game constants
    globals
        constant integer DUMMY_ID           = 'h01B'
        constant integer PROJECTILE_ID      = 'h022'
        constant integer DUMMY_EXTENDER_ID  = 'A0CA' // There is a trigger, that removes dummies, that casted an ability after some time, some skills channel for a longer time
                                                     // So, to prevent that trigger to remove those dummies too early, they are given that ability, which lets the trigger wait a bit more        
        constant integer AWAY_ITEM_ID       = 'I048'
       
        constant integer GAME_START_DELAY = 10      // amount of seconds before the actual game starts (creeps spawn, you get income etc)
        constant integer MAX_JUNKYARD_ITEM_COUNT = 15
        constant integer MAX_ARRAY_SIZE = 8190
       
        // Unit user data for creeps
        // They decide over which waypoints are followed and are used to check for player food reduction
        constant integer CF_NORMAL          = 1     // Nothing special, go along, nothing to see here
        constant integer CF_ALTERNATE_ROUTE = 2     // Take a different path then the other creeps (mid etc)
        constant integer CF_MANUAL_CONTROL  = 4     // Creeps that got ordered by the player -> don't follow waypoints anymore
        constant integer CF_COSTS_FOOD      = 8     // Unit_dies trigger has to reduce the players food manually
                                                    // This is because those units have a timed life and normally don't count towards the food ressource
                                                   
        constant integer CF_MAX_FLAGS       = 4     // The number of flags that are currently used      

        constant string LeagueWelcome1 = "|cFF1CE6B9Welcome to the official |cFFFF0303Battle Tanks League|cFF1CE6B9!"
        constant string LeagueWelcome2 = "|cFF1CE6B9Your personal player stats will be transferred to |cFFFF0303www.btanks.net|cFF1CE6B9!"
        constant string LeagueWelcome3 = "|cFF1CE6B9Thanks for trying the new League Mode imitation. This mode exists for pure practicing purposes and does not save your stats."

        constant string Cgrey = "|cffaaaaaa"
        constant string Cred = "|cffff0000"
        constant string Cgreen = "|cff00ff00"
        constant string Cpurple = "|c00400080"
        constant string Clightblue = "|c000080ff"
        constant string Cbesch = "|c00808000"
        constant string Cdarkblue = "|c000000a0"
       
                 string  TeamOneName    = "Dark Force"
                 string  TeamTwoName    = "Light Force"
        constant real    CamSmooth      = 1.        
       
                 string array ModeName
    endglobals
   
    // player related variables
    globals
        // League variables
        integer array TankBounty        // the amount of gold a player got through tank kills
        integer array CPTeles           // the amount of Control Point Teleports
        timer array PlayTimer           // counts how long each player was in a game
        integer array Multikill         // the biggest multikill that a player got
        boolean array TankDead          //! investigate the function of this one!
        integer array LeagueMKCounter   // counts the number of multikills of each player
        integer array BuildingsKilled
       
        // player customization
        boolean array SmoothCam
       
        // player stats
        integer array GotTeamkilled
        integer array DidTeamkill
        integer array ManaUpgrade       //the buyable Mana Batteries
        real array DeathPenalty         //The value that decides if a player gets kicked
        real array DeathPenaltyValue    //The amount that is added, when a player dies
        timer array PenaltyTimer        //Timer, that reduces those penalties
       
        boolean array DiedRooted        // for Guards, used for correct initialization on respawn
       
    endglobals
   
    // temporal information holders
    globals
        unit    tempControlPoint           // used for Control Point Teleports
        player KickVotePlayer           // the player that initiated the current kick vote
        integer DivideMoney             // Leaver gold
        boolean IgnoreNextItemOnJunkyard = false
    endglobals
//TESH.scrollpos=69
//TESH.alwaysfold=0
library TimerAndAttachments
// This library includes functions, that enable you to attach several variables
// to timers or units

// --- template for using this system to attach variables to a timer:
//  local timer t
//  local integer TimerIndex
//    
//  set t = NewTimer()
//  set TimerIndex = NewTimerIndex(t)
//  set udg_AV_Unit1[TimerIndex] = GetTriggerUnit()
//  set udg_AV_Int1[TimerIndex] = GetUnitAbilityLevel(GetTriggerUnit(),'A000')
//  call TimerStart(t, 2.00, true, function Example_Loop)

// --- To use and release this variables, you have to use this:
// function Example_Loop takes nothing returns nothing
//  local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
//  local unit Caster = udg_AV_Unit1[LoopTimerId]
//  local integer lvl = udg_AV_Int1[LoopTimerId]
// 
//  if lvl > 5 then
//      //You only have to call this, when you want the timer to stop looping of course
//      call ReleaseTimer(GetExpiredTimer())
//      call ReleaseHandleIndex(LoopTimerId)
//  endif
//  //Do stuff
// endfunction

function NewTimerIndex takes timer h returns integer
    local integer hi=GetHandleId(h)
    local integer i=hi-(hi/8000)*8000
    loop
        exitwhen udg_AV_Handles[i]==0
        set i=i+1
    endloop
    set udg_AV_Handles[i]=hi
    set udg_AV_Timers[i]=h
   
    return i
endfunction

function NewTriggerIndex takes trigger h returns integer
    local integer hi=GetHandleId(h)
    local integer i=hi-(hi/8000)*8000
    loop
        exitwhen udg_AV_Handles[i]==0
        set i=i+1
    endloop
    set udg_AV_Handles[i]=hi
    set udg_AV_Triggers[i]=h
    return i
endfunction

function GetHandleIndex takes handle h returns integer
    local integer hi=GetHandleId(h)
    local integer i=hi-(hi/8000)*8000
    loop
        exitwhen (udg_AV_Handles[i]==hi) or (i==MAX_ARRAY_SIZE)
        set i=i+1
    endloop
    return i
endfunction

globals
    WeaponCore array udg_AV_WeapCore[1]
    AISkill array udg_AV_AISkill[1]
endglobals

function ReleaseHandleIndex takes integer i returns nothing
    set udg_AV_Handles[i] = 0
    set udg_AV_Timers[i] = null
    set udg_AV_Triggers[i] = null
    set udg_AV_Units[i] = null
    set udg_AV_Int1[i] = 0
    set udg_AV_Int2[i] = 0
    set udg_AV_Int3[i] = 0
    set udg_AV_Real1[i] = 0.0
    set udg_AV_Real2[i] = 0.0
    set udg_AV_Real3[i] = 0.0    
    set udg_AV_Unit1[i] = null
    set udg_AV_Unit2[i] = null
    set udg_AV_Unit3[i] = null
    set udg_AV_Effect1[i] = null
    set udg_AV_Item1[i] = null
    set udg_AV_WeapCore[i] = 0
    set udg_AV_AISkill[i] = 0
    set udg_AV_Bool1[i] = false
    set udg_AV_Bool2[i] = false
    set udg_AV_Lightning[i] = null
    set udg_AV_String1[i] = ""
    set udg_AV_String2[i] = ""
    set udg_AV_Player1[i] = null
   
    if udg_AV_Loc1[i] != null then
        call RemoveLocation(udg_AV_Loc1[i])
        set udg_AV_Loc1[i] = null
    endif
   
    if udg_AV_Group1[i] != null then
        //call DestroyGroup(udg_AV_Group1[i])
        set udg_AV_Group1[i] = null
    endif
    if udg_AV_Group2[i] != null then
        //call DestroyGroup(udg_AV_Group2[i])
        set udg_AV_Group2[i] = null
    endif
    if udg_AV_Tag1[i] != null then
        call DestroyTextTag(udg_AV_Tag1[i])
        set udg_AV_Tag1[i] = null
    endif
endfunction

function ReleaseUnit takes nothing returns nothing
    //call TriggerSleepAction(2.0)
    call ReleaseHandleIndex(GetHandleIndex(GetTriggerUnit()))
    call DestroyTrigger(GetTriggeringTrigger())
endfunction

function NewUnitIndex takes unit h returns integer
    local integer hi=GetHandleId(h)
    local integer i=hi-(hi/8000)*8000
    local trigger t = CreateTrigger()
   
    loop
        exitwhen udg_AV_Handles[i]==0
        set i=i+1
    endloop
    set udg_AV_Handles[i]=hi
    set udg_AV_Units[i]=h
   
    call TriggerRegisterUnitEvent( t, h, EVENT_UNIT_DEATH )
    call TriggerAddAction( t, function ReleaseUnit )
    return i
endfunction


function NewTimer takes nothing returns timer
    if udg_TimerStackN==0 then
        return CreateTimer()
    endif
    set udg_TimerStackN=udg_TimerStackN-1
   
    return udg_TimerStack[udg_TimerStackN]
endfunction

function ReleaseTimer takes timer t returns nothing
    call PauseTimer(t)

    // Though the array is capable of carrying more timers,
    // it does not seem intelligent to keep more than 500 unused timers
    if udg_TimerStackN==500 then
        call DestroyTimer(t)
    else
        set udg_TimerStack[udg_TimerStackN]=t
        set udg_TimerStackN=udg_TimerStackN+1
    endif
endfunction
endlibrary
//TESH.scrollpos=168
//TESH.alwaysfold=0
library BTFramework  requires Trace
    // Note, that this library is not allowed to use FILTER_XXXXXX in its initializer
   
    // This library includes functions, that either extend or simplify wc3 functions
    // These functions are meant to replace the genuine functions in every case, to make sure that this map uses a coherent code base

    // Functions in this library shall match the following criteria:
    // - they replace a specific wc3 function, to make it easier to use or to adapt it to this map (i.e. GetPlayerNr, NewGroup)
    // - they simplify a function call by combining several functions into one, to improve the usability (i.e. ShowTextTag)
    // - they extend already existing functions to offer more possibilities
    // - they shall not have any dependency to any other library, since most other libraries should use this one, and not the other way round

    function DebugMsg takes string s returns nothing
        if DebugMode then
            call BJDebugMsg(s)
        endif
    endfunction

    // May be used to kill an entire thread from within a called function,
    // to find the source of an error
    function BreakThread takes nothing returns nothing
        local integer i = 1/0
    endfunction

    function WaitForGameTime takes real GameTime returns real
        local real r
        loop
            set r = GameTime - TimerGetElapsed(udg_GameTime)
            exitwhen r <= 0
            call TriggerSleepAction(r*0.2)
        endloop
        return -r
    endfunction

    function GameTimeWait takes real Duration returns real
        local real GameTime = TimerGetElapsed(udg_GameTime)+Duration
        local real r
       
        loop
            set r = GameTime - TimerGetElapsed(udg_GameTime)
            exitwhen r <= 0
            call TriggerSleepAction(r*0.2)
        endloop
        return -r
    endfunction

    globals
        integer udg_GroupStackN=0
        group array udg_GroupStack
    endglobals

    // This function is meant to replace CreateGroup, because this function uses a group array
    // which improves performance, since it recycles old groups instead of repeatedly creating new ones
    function NewGroup takes nothing returns group
        if udg_GroupStackN==0 then
            return CreateGroup()
        endif
        //call DebugMsg("Group count: " + I2S(udg_GroupStackN))
        set udg_GroupStackN=udg_GroupStackN-1

       
        if BetaMode and (udg_GroupStack[udg_GroupStackN] == null) then
            call BJDebugMsg("Please write \"!report empty group\"")
        endif
       
        return udg_GroupStack[udg_GroupStackN]
    endfunction

    // This function has to be used together with NewGroup, to ensure they both work correctly
    // It replaces the DestroyGroup function in this context
    function ReleaseGroup takes group g returns nothing
        // Though the array is capable of carrying more groups,
        // it does not seem intelligent to keep more than 500 unused groups
        if udg_GroupStackN==500 then
            call DestroyGroup(g)
        else
            call GroupClear(g)
            set udg_GroupStack[udg_GroupStackN]=g
            set udg_GroupStackN=udg_GroupStackN+1
        endif
    endfunction


    function GetMaxHumanPlayers takes nothing returns integer
        //returns the maximum possible number of human players
        return 10
    endfunction

    function GetMaxPlayers takes nothing returns integer
        //returns the maximum possible number of players in the game (including Forces)
        return 12
    endfunction

    globals
        boolean udg_PlayerNrInitialized = false
        integer array udg_ConvertedPlayerNr
        player array udg_PlayerByPlayerNr
    endglobals

    function Init_PlayerNr_GetPlayerNr takes player p returns integer
        local integer Id= GetPlayerId(p)

        //to make sure every player Id is 1-based,
        //instead of a mixed 0- and 1-based system

        if ( Id < GetMaxHumanPlayers() ) then
            return Id + 1
        elseif ( Id == PLAYER_NEUTRAL_PASSIVE ) then
            return 15
        else
            if ( p == udg_Force[1] ) then
                return 11
            elseif ( p == udg_Force[2] ) then
                return 12
            endif
        endif

        return 0
    endfunction

    function Init_PlayerNr takes nothing returns nothing
        local integer i = 0
        if udg_PlayerNrInitialized then
            return
        endif
       
        set udg_ObserverInGame = not ((GetPlayerSlotState(Player(10)) == PLAYER_SLOT_STATE_EMPTY) and (GetPlayerSlotState(Player(11)) == PLAYER_SLOT_STATE_EMPTY))

        // udg_Force[i] is required to initialize this correctly
        if udg_ObserverInGame then
            set udg_Force[1] = Player(bj_PLAYER_NEUTRAL_VICTIM) //Dark Force
            set udg_Force[2] = Player(bj_PLAYER_NEUTRAL_EXTRA)  //Light Force
        else
            set udg_Force[1] = Player(10)
            set udg_Force[2] = Player(11)
        endif
       
        loop
            exitwhen i>15
            set udg_ConvertedPlayerNr[i] = Init_PlayerNr_GetPlayerNr(Player(i))
            set udg_PlayerByPlayerNr[udg_ConvertedPlayerNr[i]] = Player(i)
            set i = i + 1
        endloop
        set udg_PlayerNrInitialized = true
    endfunction

    function GetPlayerNr takes player p returns integer
        // Just to fix Critical, should not be called with "null" at all,
        // and if, it should return 0, not 1 (but that causes a crit)
        // Once this problem is solved, remove the if to make this function being inlined
        //if(p==null) then
            //call DebugMsg("GetPlayerNr(null)")
            //call Trace_Print()
        //endif
        return udg_ConvertedPlayerNr[GetPlayerId(p)]
    endfunction
   
    function PolarProjX takes real x, real dist, real angle returns real
        return x + dist * Cos(angle * bj_DEGTORAD)
    endfunction
   
    function PolarProjY takes real y, real dist, real angle returns real
        return y + dist * Sin(angle * bj_DEGTORAD)
    endfunction
   
    function GetAngle takes real x1, real y1, real x2, real y2 returns real
        return bj_RADTODEG*Atan2(y2-y1,x2-x1)
    endfunction
   
    function GetUnitsAngleDeg takes unit a, unit b returns real
        return bj_RADTODEG*Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
    endfunction

    function GetUnitsDistance takes unit a, unit b returns real
        return SquareRoot((GetUnitX(a) - GetUnitX(b)) * (GetUnitX(a) - GetUnitX(b)) + (GetUnitY(a) - GetUnitY(b)) * (GetUnitY(a) - GetUnitY(b)))
    endfunction
   
    function GetXYDistance takes real x1, real y1,real x2,real y2 returns real
        return SquareRoot((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
    endfunction

    function GetPlayer takes integer Id returns player
        return udg_PlayerByPlayerNr[Id]
    endfunction

    function GetName takes unit u returns string
        if IsUnitType(u, UNIT_TYPE_HERO) then
            return GetHeroProperName(u)
        endif
        return GetUnitName(u)
    endfunction

    function ShowTextTag takes string s, unit target, real x, real y, player p returns nothing
        local texttag tag = CreateTextTag(  )
        local real PosX
        local real PosY
        local real PosZ
        local boolean visibility
       
        //x, y - additional offset to the target units position
        //when unit is null, these two are used for the position completely
        if target == null then
            set PosX = x
            set PosY = y
            set PosZ = 300
        else
            //target - where to show the text tag
            set PosX = GetUnitX(target) + x
            set PosY = GetUnitY(target) + y
            set PosZ = GetUnitFlyHeight(target)
        endif
        //p - the player who is supposed to see the tag, when null, everyone can see the tag
        if p == null then
            set visibility = true
        else
            set visibility = false
           
            if (p == udg_Force[1]) or (p == udg_Force[2]) then
                set visibility = IsPlayerAlly(GetLocalPlayer(), p)
            elseif GetLocalPlayer() == p then
                set visibility = true
            endif
        endif
        //s - string to be shown, including color code
        call SetTextTagText(tag, s, 0.023)
        call SetTextTagPos(tag, PosX, PosY, PosZ)
        call SetTextTagVisibility(tag, visibility)
        call SetTextTagVelocity(tag, 0, 0.03)
        call SetTextTagFadepoint(tag, 2 )
        call SetTextTagLifespan(tag, 3 )
        call SetTextTagPermanent(tag, false )
       
        set tag = null
    endfunction
               
    function IsUnitNetted takes unit Caster returns boolean
        if (GetUnitAbilityLevel(Caster, 'Beng') > 0) or (GetUnitAbilityLevel(Caster, 'Bens') > 0) or (GetUnitAbilityLevel(Caster, 'Bena') > 0) then
            return true
        endif
       
        return false
    endfunction

    function IsUnitStunned takes unit Caster returns boolean
        if GetUnitAbilityLevel(Caster, 'BSTN')>0 then   //stunned
            return true
        elseif GetUnitAbilityLevel(Caster, 'BNcs')>0 then   //stunned (swarm rockets buff)
            return true
        elseif GetUnitAbilityLevel(Caster, 'BPSE')>0 then   //stunned (pause)
            return true
        elseif GetUnitAbilityLevel(Caster, 'B010')>0 then   //System Overload
            return true
        elseif IsUnitNetted(Caster) then                    //Ensnare
            return true
        elseif GetUnitAbilityLevel(Caster, 'B00K')>0 then   //Dimension Shift
            return true
        elseif GetUnitAbilityLevel(Caster, 'B003')>0 then   //Ice Prison (pause)
            return true
        elseif IsUnitPaused(Caster) then                    //paused unit
            return true
        endif
        return false
    endfunction

    // Used for the Hostbot API but hidden here! So sshhh!
    // Will not be inlined, and that is important
    function Code takes real i returns real
        local real j = (i/10.)
        set j = SquareRoot(j/7.)
        return j
    endfunction

    function HexToInt takes string Hex returns integer
        local integer i = 0
        local integer result = 0
        local string s
        loop
            exitwhen i >= StringLength(Hex)
            set s = SubString(Hex,i,i+1)
            set result = result*16
            if s == "A" or s=="a" then
                set result = result+10
            elseif s == "B" or s=="b" then
                set result = result+11
            elseif s == "C" or s=="c" then
                set result = result+12
            elseif s == "D" or s=="d" then
                set result = result+13
            elseif s == "E" or s=="e" then
                set result = result+14
            elseif s == "F" or s=="f" then
                set result = result+15
            else
                set result = result+S2I(s)
            endif
            set i = i + 1
        endloop
        return result
    endfunction

    function HexString takes integer i returns string
        local string alpha = "0123456789abcdef"
        return SubString(alpha, i, i+1)
    endfunction

    function IntToHex takes integer i returns string
        local integer r = ModuloInteger( i, 16)
        local string result
     
        if (i-r <= 16) then
            set result = HexString(r)
        else
            set result = IntToHex( (i-r)/16 ) + HexString(r)
        endif
        return result
    endfunction
   
    function ExecuteCode takes code c returns nothing
        call ForForce(bj_FORCE_PLAYER[0], c)
    endfunction

    function GetUnitZ takes unit U returns real
        call MoveLocation(udg_TempLoc, GetUnitX(U), GetUnitY(U))
        return (GetUnitFlyHeight(U) + GetLocationZ(udg_TempLoc))
    endfunction
   
   
    function GetItemCosts takes integer id returns integer
        local item temp = CreateItem(id, 0,0)
        local integer costs = R2I(GetWidgetLife(temp))
       
        call RemoveItem(temp)
        set temp = null
       
        return costs
    endfunction
   
    function Round takes real r returns integer
        return R2I(r+0.5)
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitTypes requires BTFramework
    function IsTinkerTower takes unit U returns boolean
        local integer Id = GetUnitTypeId(U)
        return (Id-'h01A')*(Id-'h01F')*(Id-'h00T')*(Id-'h010')*(Id-'h01G')==0
    endfunction

    function IsWatchTower takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        return (ID-'h01O')*(ID-'h01N')*(ID-'h01Q')*(ID-'h01P')*(ID-'h01M')==0
    endfunction

    function IsForceTower takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        local boolean result = false
       
        if (ID == 'h01Y') then      // Protective Tower
            set result = true
        elseif (ID == 'o000') then  // Barricade
            set result = true
        elseif (ID == 'h000') then  // Rocket Tower
            set result = true
        elseif (ID == 'h001') then  // Laser Tower
            set result = true
        elseif (ID == 'o003') then  // Teleport Beacon
            set result = true
        elseif (ID == 'o005') then  // Camouflage Generator
            set result = true
        endif
       
        return result
       
        // the following line apparently returns true, when a Marine ('z000') is checked ... I have no idea why, the code above works though
        //return (ID-'h01Y')*(ID-'o000')*(ID-'h000')*(ID-'h001')*(ID-'o003')*(ID-'o005')==0
    endfunction

    function IsPacifista takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        return ( ID - 'z020' ) * ( ID - 'z021' ) * ( ID - 'z022' ) * ( ID - 'z023' ) * ( ID - 'z024' ) == 0
    endfunction
   
    function IsObelisk takes unit U returns integer
        local integer ID = GetUnitTypeId(U)
       
        if ( ID == 'h02B') then
            return 1
        elseif ( ID == 'h02C') then
            return 2
        elseif ( ID == 'h02D') then
            return 3
        elseif ( ID == 'h02E') then
            return 4
        elseif ( ID == 'h02F') then
            return 5
        endif
        return 0
    endfunction

    // True for force and player summoned creeps (of the factory and TCC)
    // False for heroes, buildings, dummies and creeps summoned by skills (<- not sure if this should stay this way or not)
    function IsCreep takes unit U, boolean onlyForce returns boolean
        local integer ID = GetUnitTypeId(U)
        local integer ownerId = GetPlayerNr(GetOwningPlayer(U))
       
        if (IsUnitType(U, UNIT_TYPE_HERO) or IsUnitType(U, UNIT_TYPE_MECHANICAL) or IsUnitType(U, UNIT_TYPE_STRUCTURE)) then
            return false
        endif
       
       
        /*
        // normal creeps of the force
        if ( ID - 'z001' ) * ( ID - 'z002' ) * ( ID - 'z000' ) * ( ID - 'z00C' ) == 0 then
            return true
        endif
        // low bounty creeps
        if ( ID - 'z008' ) * ( ID - 'z007' ) * ( ID - 'z006' ) * ( ID - 'z00D' ) == 0 then
            return true
        endif        
        // creeps of the Troop Command Center
        if ( ID - 'z01E' ) * ( ID - 'z01F' ) * ( ID - 'z01K' ) * ( ID - 'z00Y' ) * ( ID - 'z01O' ) == 0 then
            return true
        endif
        */

       
        return false
    endfunction

    function IsDecoy takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        return ( ID - 'n008' ) * ( ID - 'n009' ) * ( ID - 'n00H' ) * ( ID - 'n00V' ) * ( ID - 'n00W' ) == 0
    endfunction
   
    function IsItemHealer takes item I returns boolean
        // Also returns true for manipulated healers.
        local integer typeId = GetItemTypeId(I)
        return (typeId-'I04C')*(typeId-'I050')==0
    endfunction
   
    function IsDummy takes unit U returns boolean
        // Each dummy unit should have this ability, if not, add this ability to the dummy unit
        return (GetUnitAbilityLevel(U, 'A09G') > 0)
    endfunction
   
    function IsTechMech takes unit U returns boolean
        return (GetUnitTypeId(U) == 'n00I')
    endfunction
   
    function IsWard takes unit U returns boolean
        // here is the thing: there is no way to check directly if a unit is a ward (IsUnitType has no enum for wards)
        // So make sure, that every ward has the ability mentioned below, so you can check for it
        return (GetUnitAbilityLevel(U, 'A0BZ') > 0)
    endfunction
   
    function IsAirUnit takes unit U returns boolean
        local integer id = GetUnitTypeId(U)
       
        // H00R = Ghost Tank, H02V = Goliath (with active Offroad Engine)
        if (id == 'H00R') or (id == 'H02A') then
            return false
        endif
        return IsUnitType(U, UNIT_TYPE_FLYING)
    endfunction
   
    function IsGroundUnit takes unit U returns boolean
        local integer id = GetUnitTypeId(U)
       
        // H00R = Ghost Tank, H02V = Goliath (with active Offroad Engine)
        if (id == 'H00R') or (id == 'H02A') then
            return true
        endif
        return IsUnitType(U, UNIT_TYPE_GROUND) or IsUnitNetted(U) or IsUnitType(U, UNIT_TYPE_STRUCTURE)
    endfunction
   
    function IsRadar takes item I returns boolean
        local integer typeId = GetItemTypeId(I)
        // Radar, Ultimate Pack, Trader Hunter Pack, Mine Defuse Pack, Mine Defuse Pack (Trader)
        return (typeId-'I02G')*(typeId-'I01J')*(typeId-'I00L')*(typeId-'I04R')*(typeId-'I05G')==0
    endfunction
   
    function IsTeleporter takes item I returns boolean
        local integer typeId = GetItemTypeId(I)
        // Teleporter, Ultimate Pack, Speed Pack, Trader Hunter Pack
        return (typeId-'I014')*(typeId-'I01J')*(typeId-'I012')*(typeId-'I00L')==0
    endfunction
   
    function IsBlockingArmor takes item I returns boolean
        local integer typeId = GetItemTypeId(I)
        // Burst Armor, Burst Armor (Upgrade), Deflective Armor, Deflective Armor (Upgrade)
        return (typeId-'I05N')*(typeId-'I05U')*(typeId-'I05O')*(typeId-'I05T')==0
    endfunction
   
    function IsItemWithMagicResistance takes item I returns boolean
        local integer typeId = GetItemTypeId(I)
        // Negator Pack, Mass Converter
        return (typeId-'I05V')*(typeId-'I05S')==0
    endfunction
   
    function IsEnergyConverter takes item I returns boolean
        local integer typeId = GetItemTypeId(I)
        // Energy Converter, Emergency Repair
        return (typeId-'I05R')*(typeId-'I06A')==0
    endfunction
   
    function GetMineDamage takes unit U returns real
        local integer i = GetUnitTypeId(U)
        local real dmg = 0.0
       
        if i=='z00P' then   // Goblin Riot
            return 450.0
        elseif i=='n002' then   // Demo-Mine 1
            set dmg = 320.0
        elseif i=='n003' then   // Demo-Mine 2
            set dmg = 640.0
        elseif i=='n004' then   // Demo-Mine 3
            set dmg = 960.0
        elseif i=='n005' then   // Demo-Mine 4
            set dmg = 1280.0
        elseif i=='n000' then   // Demo-Mine 5 = normal Mine
            set dmg = 1600.0
        elseif i=='n001' then   // Heavy Mine
            set dmg = 3200.0
        elseif i=='n00J' then   // Huge Mine
            set dmg = 4800.0
        elseif i=='n00L' then   // Carpet Bomb 1
            return 500.0
        elseif i=='n00R' then   // Carpet Bomb 2
            return 1000.0
        elseif i=='n00S' then   // Carpet Bomb 3
            return 1500.0
        elseif i=='n00T' then   // Carpet Bomb 4
            return 2000.0
        elseif i=='n00U' then   // Carpet Bomb 5
            return 2500.0
        endif
        return dmg * (1.0 + 0.03 * I2R(GetPlayerTechCount(GetOwningPlayer(U), 'R002', true)) )
    endfunction
   
    function IsMine takes unit U returns boolean
        return (GetMineDamage(U) > 0) and GetUnitTypeId( U ) != 'z00P'
    endfunction
   
    function IsAirMine takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        return ( ID - 'n016' )==0
    endfunction
   
    function IsTransparentUnit takes unit U returns boolean
        local integer ID = GetUnitTypeId(U)
        // Earth Robot
        if (ID == 'H01U') then
            return (GetUnitAbilityLevel(U, TRANSPARENT_ARMOR_ABILITY_ID) > 0)
        // Ghost Tank
        elseif (ID == 'H00R') then
            return true
        endif
       
        return false
    endfunction
endlibrary
//TESH.scrollpos=1018
//TESH.alwaysfold=0
library MapAttachedSettings requires TimerAndAttachments, AIInterface, UnitTypes, Bounty
// This library includes functions, that has been moved out of their respective triggers, to make sure
// that they can be used in other triggers too.

// Functions in this library should match the following criteria:
// - they are used in more than one trigger or are expected to be used in more then one in the future
// - they should not be tied to a specific trigger (through the usage of a trigger specific variables and such)

function L2S takes location loc returns string
    return "("+R2S(GetLocationX(loc))+","+R2S(GetLocationY(loc))+")"
endfunction

function DistBetweenUnits takes unit U1, unit U2 returns real
    local real dx = GetUnitX(U1)-GetUnitX(U2)
    local real dy = GetUnitY(U1)-GetUnitY(U2)
    return SquareRoot(dx*dx+dy*dy)
endfunction

function SetXPandUpgradeLevel takes integer lvl returns nothing
    local integer Id = 1

    loop
        exitwhen Id > GetMaxPlayers()

        if (Id <= GetMaxHumanPlayers()) then
            call SuspendHeroXP( udg_Tank[Id], false )
            call SetHeroLevel( udg_Tank[Id], lvl, false )
            call SuspendHeroXP( udg_Tank[Id], true )
        endif
       
        call SetPlayerTechResearched( GetPlayer(Id), 'R003', lvl )
        call SetPlayerTechResearched( GetPlayer(Id), 'R002', lvl )
       
        set Id = Id + 1
    endloop
endfunction

function GetWeaponRange takes integer ID returns integer
    local integer index = ID - (ID/8192)*8192
   
    if index < 0 then
        return udg_WeaponRange[index+8192]
    endif
   
    return udg_WeaponRange[index]
endfunction

function GetWeaponDamageModifier takes integer ID returns real
    local integer index = ID - (ID/8192)*8192
   
    if index < 0 then
        return udg_WeaponModifier[index+8192]
    endif
   
    return udg_WeaponModifier[index]
endfunction

globals
    constant integer RANGE_AVG = 1
    constant integer RANGE_MAX = 2
    constant integer RANGE_MIN = 3
endglobals

function GetWeaponRangeMode takes unit U, integer Mode returns real
    local integer Slot = 1
    local item ItemInSlot
    local real ItemCosts
    local real ItemRange
    local real AllCosts = 0
    local real AllRange = 0

    //A01J - Tank Cannon, A06M - Creep Sniper, A08Z - Hero Sniper
    if  ((GetUnitAbilityLevel(U, 'A06M') > 0) or (GetUnitAbilityLevel(U, 'A08Z') > 0)) then
        set AllCosts = AllCosts + 8 * I2R(GetUnitAbilityLevel(U, 'A06M'))
        set AllCosts = AllCosts + 8 * I2R(GetUnitAbilityLevel(U, 'A08Z'))
        set AllRange = 1300
    elseif GetUnitAbilityLevel(U, 'A01J') > 0 then
        set AllCosts = AllCosts + 8 * I2R(GetUnitAbilityLevel(U, 'A01J'))
        set AllRange = 900
    endif
    if Mode == 1 then
        //Mode 1 calculates the average range of this tank
        //more expensive weapons have a bigger influence on the range
        set AllRange = AllRange * AllCosts
    endif
   
    loop
        exitwhen Slot > 6
        set ItemInSlot = UnitItemInSlotBJ(U, Slot)
        set ItemRange = I2R(GetWeaponRange(GetItemTypeId(ItemInSlot)))
        if ItemRange != 0 then
            if (Mode == RANGE_AVG) then     //Average Range
                set ItemCosts = GetWidgetLife(ItemInSlot)
                set AllRange = AllRange + ItemRange * ItemCosts / 100
                set AllCosts = AllCosts + ItemCosts / 100
            elseif (Mode == RANGE_MAX) then // Maximum Range
                if ItemRange > AllRange then
                    set AllRange = ItemRange
                endif
            elseif (Mode == RANGE_MIN) then //Minumum Range
                if AllRange == 0 then
                    set AllRange = ItemRange
                else
                    if ItemRange < AllRange then
                        set AllRange = ItemRange
                    endif
                endif
            endif
        endif
        set Slot = Slot + 1
    endloop
    if Mode == RANGE_AVG then
        set udg_Show_WeaponRange = AllRange / AllCosts
    else
        set udg_Show_WeaponRange = AllRange
    endif
   
    return udg_Show_WeaponRange
endfunction

function ItemDropInvalid takes unit U, item DropItem returns nothing
    local real ItemX = GetItemX(DropItem)
    local real ItemY = GetItemY(DropItem)
    local real DistX = ItemX-GetUnitX(U)
    local real DistY = ItemY-GetUnitY(U)
    local integer Team = udg_Player_Team[GetPlayerNr(GetItemPlayer(DropItem))]
    local boolean ItemCheck = udg_Item_Check
   
    set udg_Item_Check = false
    if DistX*DistX+DistY*DistY <= 40000 then
        call SetItemPosition(DropItem, GetItemX(DropItem), GetItemY(DropItem))
    else
        call SetItemPosition(DropItem, GetUnitX(U), GetUnitY(U))
    endif
    set udg_Item_Check = ItemCheck
endfunction

function ItemDropAll takes unit U returns nothing
    local integer i = 0
    local item SlotItem
    local integer Team = udg_Player_Team[GetPlayerNr(GetOwningPlayer(U))]
   
    loop
        exitwhen i > 5
        set SlotItem = UnitItemInSlot(U,i)
        if GetItemTypeId(SlotItem)=='I048' or GetItemType(SlotItem)==ITEM_TYPE_POWERUP then
            call RemoveItem( SlotItem )
        else
            call SetItemPosition(SlotItem, GetUnitX(U)+88*Cos((360-(i*60))*bj_DEGTORAD), GetUnitY(U)+88*Sin((360-(i*60))*bj_DEGTORAD))
        endif
        set i = i + 1
    endloop
    set SlotItem = null
endfunction

function GiveStartingGold takes integer NormalStartingGold returns nothing
    local integer CountTeam1Players = 0
    local integer CountTeam2Players = 0
    local integer CountTeam1PlayersStart = 0
    local integer CountTeam2PlayersStart = 0
    local integer CountTeamPlayersStartMax
    local integer Team1StartingGold
    local integer Team2StartingGold
    local integer i = 1

    loop
        exitwhen i > GetMaxHumanPlayers()
        if IsPlayerInForce( GetPlayer(i), udg_Players_Start ) then
            if i <= 5 then
                set CountTeam1PlayersStart = CountTeam1PlayersStart + 1
            else
                set CountTeam2PlayersStart = CountTeam2PlayersStart + 1
            endif
            if IsPlayerInForce( GetPlayer(i), udg_Players ) then
                if i <= 5 then
                    set CountTeam1Players = CountTeam1Players + 1
                else
                    set CountTeam2Players = CountTeam2Players + 1
                endif
            endif
        endif
        set i = i + 1
    endloop
    set CountTeamPlayersStartMax = IMaxBJ(CountTeam1PlayersStart,CountTeam2PlayersStart)
    set NormalStartingGold = NormalStartingGold*CountTeamPlayersStartMax
    set i = 1
    if CountTeam1Players > 0 then
        set Team1StartingGold = ( NormalStartingGold / CountTeamPlayersStartMax + NormalStartingGold / CountTeam1Players ) / 2
    endif
    if CountTeam2Players > 0 then
        set Team2StartingGold = ( NormalStartingGold / CountTeamPlayersStartMax + NormalStartingGold / CountTeam2Players ) / 2
    endif
    loop
        exitwhen i > GetMaxHumanPlayers()
        if IsPlayerInForce( GetPlayer(i), udg_Players ) then
            if i <= 5 then
        call SetPlayerState(GetPlayer(i), PLAYER_STATE_RESOURCE_GOLD, Team1StartingGold)
            else
        call SetPlayerState(GetPlayer(i), PLAYER_STATE_RESOURCE_GOLD, Team2StartingGold)
            endif
        endif
        set i = i + 1
    endloop
endfunction

function PlayerAfkReturn takes player P returns nothing
    local integer PlayerId = GetPlayerNr(P)
    local integer i = 1
    local item SlotItem
    local unit U
    local group G = NewGroup()
       
    set udg_ActionsLast[PlayerId] = udg_Time_Value
    call DebugMsg(I2S(R2I(udg_AFKTime[PlayerId])))
    call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(P) + " has returned (" + I2S(R2I(udg_AFKTime[PlayerId]/60.)) + " minutes afk in this match).|r" )

    loop
        exitwhen i > GetMaxHumanPlayers()
        if udg_Player_Team[PlayerId]==udg_Player_Team[i] and PlayerId!=i then
            call SetPlayerAllianceBJ( P, ALLIANCE_SHARED_CONTROL, false, GetPlayer(i) )
        endif
        set i = i + 1
    endloop
    call GroupEnumUnitsOfPlayer(G, P, FILTER_NULL)
    loop
        set U = FirstOfGroup(G)
        exitwhen U==null
        if UnitInventorySize(U)>0 then
            set i = 0
            loop
                exitwhen i > 5
                set SlotItem = UnitItemInSlot(U, i)
                if SlotItem!=null then
                    if GetItemTypeId(SlotItem) == AWAY_ITEM_ID then
                        call RemoveItem( SlotItem )
                    elseif GetItemUserData(SlotItem) == 0 then
                        call SetItemDroppable( SlotItem, true )
                    endif
                endif
                set i = i + 1
            endloop
        endif
        call GroupRemoveUnit(G, U)
    endloop
    call ReleaseGroup(G)
    set G = null
    set SlotItem = null
    set udg_Afk[PlayerId] = false
    call RegisterAI( PlayerId, 0 )

    if (GetLocalPlayer() == GetPlayer(PlayerId)) then
        call MultiboardDisplay( udg_Killboard[PlayerId-1], true )
        call MultiboardMinimize( udg_Killboard[PlayerId-1], IsMultiboardMinimized(udg_Killboard[PlayerId-1]) )
    endif
endfunction

function PlayerAfkActivate takes player P, string Message, boolean TurnToAI returns nothing
    local integer PlayerId = GetPlayerNr(P)
    local integer i = 1
    local item SlotItem
    local unit U
    local group G

    if TurnToAI then
        if (Message!=null) and (Message!="") then
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(P) + " is away, units are now controlled by AI. ("+Message+")")
        else
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(P) + " is away, units are now controlled by AI.")
        endif
        call RegisterAI( PlayerId, 1 )
    else
        if (Message!=null) and (Message!="") then
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(P) + " is away. ("+Message+")")
        else
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(P) + " is away.")
        endif
        loop
            exitwhen i > GetMaxHumanPlayers()
            if udg_Player_Team[PlayerId]==udg_Player_Team[i] and PlayerId!=i then
                call SetPlayerAllianceBJ( P, ALLIANCE_SHARED_CONTROL, true, GetPlayer(i) )
            endif
            set i = i + 1
        endloop
        set G = NewGroup()
        call GroupEnumUnitsOfPlayer(G, P, FILTER_NULL)
        loop
            set U = FirstOfGroup(G)
            exitwhen U==null
            if (GetUnitState(U, UNIT_STATE_LIFE)>0) and (UnitInventorySize(U)>0) then
                set i = 0
                loop
                    exitwhen i > 5
                    set SlotItem = UnitItemInSlot(U, i)
                    if SlotItem==null then
                        call UnitAddItemById( U, AWAY_ITEM_ID )
                    else
                        call SetItemDroppable( SlotItem, false )
                    endif
                    set i = i + 1
                endloop
            endif
            call GroupRemoveUnit(G, U)
        endloop
        call ReleaseGroup(G)
        set G = null
        set SlotItem = null
    endif
    set udg_Afk[PlayerId] = true

    call DialogDisplay( P, udg_Afk_Dialog, true )
    if (GetLocalPlayer() == GetPlayer(PlayerId)) then
        call MultiboardDisplay( udg_Killboard[PlayerId-1], true )
        call MultiboardMinimize( udg_Killboard[PlayerId-1], IsMultiboardMinimized(udg_Killboard[PlayerId-1]) )
    endif
endfunction

function ShowTip takes player ToPlayer returns nothing
    local integer tipId = GetRandomInt(1,20)
    local string text = "|cfffed312Tip " + I2S(tipId) + "/20:|r |cff9999ff" + udg_Tips[tipId] +"|r"
    call DisplayTextToPlayer(ToPlayer, 0, 0, text)
endfunction

function FloatingTimedLife_Child takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local real duration = udg_AV_Real1[LoopTimerId]
    local string Pre = udg_AV_String1[LoopTimerId]
    local string End = udg_AV_String2[LoopTimerId]
    local string msg
   
    if (GetUnitState(Target, UNIT_STATE_LIFE) > 0) and (duration > 0) then
        set msg = Pre+I2S(R2I(duration))+End
        call ShowTextTag(msg, Target, 0, 0, udg_AV_Player1[LoopTimerId])
        set udg_AV_Real1[LoopTimerId] = udg_AV_Real1[LoopTimerId] - 1.0
    else
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)    
    endif

    set Target = null
endfunction

function PermanentTimedLife_Explosive takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local real duration = udg_AV_Real1[LoopTimerId]
    local real offset = udg_AV_Real2[LoopTimerId]
    local texttag tag = udg_AV_Tag1[LoopTimerId]
    local string Pre = udg_AV_String1[LoopTimerId]
    local string End = udg_AV_String2[LoopTimerId]
    local string msg
   
    if (GetUnitState(Target, UNIT_STATE_LIFE) > 0) and (duration > 0) and (GetUnitAbilityLevel(Target, EXPLOSIVE_BUFF_ID) > 0) then
        call SetTextTagText(tag, Pre+I2S(R2I(duration+1))+End, 0.023)
        call SetTextTagPos(tag, GetUnitX(Target) + offset, GetUnitY(Target), GetUnitFlyHeight(Target) + 150)
        call SetTextTagVisibility(tag, IsUnitVisible(Target, GetLocalPlayer()))
        set udg_AV_Real1[LoopTimerId] = udg_AV_Real1[LoopTimerId] - 0.02
    else
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)    
    endif

    set Target = null
endfunction

function PermanentTimedLife_KineticShield takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local integer tId = GetPlayerNr(GetOwningPlayer(Target))
    local real duration = udg_AV_Real1[LoopTimerId]
    local real offset = udg_AV_Real2[LoopTimerId]
    local real percentage = RMaxBJ(0, (GetUnitUserData(Target) - KS_TakenDamage[tId]) / GetUnitUserData(Target)) * 100
    local texttag tag = udg_AV_Tag1[LoopTimerId]
    local string Pre = udg_AV_String1[LoopTimerId]
    local string End = udg_AV_String2[LoopTimerId]
    local string msg = I2S(R2I(percentage))+ "%"
   
    if (GetUnitState(Target, UNIT_STATE_LIFE) > 0) and (duration > 0) then
        call SetTextTagText(tag, Pre+msg+End, 0.023)
        call SetTextTagPos(tag, GetUnitX(Target) + offset, GetUnitY(Target), GetUnitFlyHeight(Target) + 150)
        set udg_AV_Real1[LoopTimerId] = udg_AV_Real1[LoopTimerId] - 0.02
    else
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)    
    endif

    set Target = null
endfunction

function PermanentTimedLife_HitnRun takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local integer tId = GetPlayerNr(GetOwningPlayer(Target))
    local real duration = udg_AV_Real1[LoopTimerId]
    local real offset = udg_AV_Real2[LoopTimerId]
    local texttag tag = udg_AV_Tag1[LoopTimerId]
    local string Pre = udg_AV_String1[LoopTimerId]
    local string End = udg_AV_String2[LoopTimerId]
    local string msg = I2S(R2I(hitnRunDamage[tId]))
   
    if (GetUnitState(Target, UNIT_STATE_LIFE) > 0) and (duration > 0) and (GetUnitAbilityLevel(Target, HITNRUN_BUFF_ABILITY_ID) > 0) then
        call SetTextTagText(tag, Pre+msg+End, 0.023)
        call SetTextTagPos(tag, GetUnitX(Target) + offset, GetUnitY(Target), GetUnitFlyHeight(Target) + 150)
        set udg_AV_Real1[LoopTimerId] = udg_AV_Real1[LoopTimerId] - 0.02
    else
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)    
    endif

    set Target = null
endfunction

function PermanentTimedLife_HeatSeekingMissile takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local integer tId = GetPlayerNr(GetOwningPlayer(Target))
    local integer level = GetUnitAbilityLevel(Target, HSM_ABILITY_ID)
    local real offset = udg_AV_Real2[LoopTimerId]
    local texttag tag = udg_AV_Tag1[LoopTimerId]
    local string Pre = udg_AV_String1[LoopTimerId]
    local string End = udg_AV_String2[LoopTimerId]
    local string msg = "|cff00ff00"
    local integer maxMissiles = HSM_MAX_RESTOCK_BASE + ( level * HSM_MAX_RESTOCK_UP )
    local integer i = 1
       
    if (GetUnitTypeId(Target) == 'H02J') then
        // first, check if the string to be displayed changed compared to the last loop
        if (udg_AV_Real1[LoopTimerId] != HSM_MissileCount[tId]) then
            if (HSM_MissileCount[tId] == 0) then
                set msg = "|cff000000"
            endif
            loop
                exitwhen (i > maxMissiles)
               
                if (i == HSM_MissileCount[tId]) and (i < maxMissiles) then
                    set msg = msg + Pre + " |r|cff000000"
                else
                    set msg = msg + Pre + " "
                endif
               
                set i = i + 1
            endloop
            set msg = SubStringBJ(msg, 1, StringLength(msg)-1) + "|r"
        else
            set msg = udg_AV_String2[LoopTimerId]
        endif
       
        set udg_AV_Real1[LoopTimerId] = HSM_MissileCount[tId]
        set udg_AV_String2[LoopTimerId] = msg
   
        call SetTextTagText(tag, msg, 0.023)
        call SetTextTagPos(tag, GetUnitX(Target) + offset, GetUnitY(Target), GetUnitFlyHeight(Target) + 150)
       
        if (GetUnitState(Target, UNIT_STATE_LIFE) <= 0) then
            call SetTextTagVisibility(tag, false)
        else
            call SetTextTagVisibility(tag, GetLocalPlayer() == GetOwningPlayer(Target))
        endif
    else
        call DebugMsg("End HSM count")
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)    
    endif

    set Target = null
endfunction

function PermanentTimedLife takes unit TimedUnit, real duration, player p, string Pre, string End, real OffsetX, code action returns nothing
    local timer t
    local integer TimerIndex
    local string msg
    local texttag tag = CreateTextTag()
    local boolean visibility
   
    if (p == null) then    
        set visibility = IsUnitVisible(TimedUnit, GetLocalPlayer())
    else
        set visibility = GetLocalPlayer() == p
    endif

    call SetTextTagPos(tag, GetUnitX(TimedUnit) + OffsetX, GetUnitY(TimedUnit), GetUnitFlyHeight(TimedUnit))
    call SetTextTagVisibility(tag, visibility)
    call SetTextTagVelocity(tag, 0, 0.0)
    call SetTextTagPermanent(tag, true )
   
    set t = NewTimer()
    set TimerIndex = NewTimerIndex(t)
    set udg_AV_Unit1[TimerIndex] = TimedUnit
    set udg_AV_Real1[TimerIndex] = duration
    set udg_AV_Real2[TimerIndex] = OffsetX
    set udg_AV_String1[TimerIndex] = Pre
    set udg_AV_String2[TimerIndex] = End
    set udg_AV_Tag1[TimerIndex] = tag
    call TimerStart(t, 0.02, true, action)
   
    set tag = null
endfunction

//Shows a countdown over the target unit
function FloatingTimedLife takes unit TimedUnit, real duration, player p, string Pre, string End returns nothing
    local timer t
    local integer TimerIndex
    local string msg

    set msg = Pre+I2S(R2I(duration))+End
    call ShowTextTag(msg, TimedUnit, 0, 0, p)
   
    set t = NewTimer()
    set TimerIndex = NewTimerIndex(t)
    set udg_AV_Unit1[TimerIndex] = TimedUnit
    set udg_AV_Real1[TimerIndex] = duration - 1
    set udg_AV_String1[TimerIndex] = Pre
    set udg_AV_String2[TimerIndex] = End
    set udg_AV_Player1[TimerIndex] = p
    call TimerStart(t, 1.0, true, function FloatingTimedLife_Child)
endfunction

function AreCoordsInStrategicLocation takes real X, real Y returns boolean
    if RectContainsCoords(gg_rct_Strategic_Location_1, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_2, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_3, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_4, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_5, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_6, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_7, X, Y) then
        return true
    elseif RectContainsCoords(gg_rct_Strategic_Location_8, X, Y) then
        return true
    endif
    return false
endfunction

function IsUnitInStrategicLocation takes unit U returns boolean
    return AreCoordsInStrategicLocation( GetUnitX(U), GetUnitY(U) )
endfunction

function EnableStrategicLocationBuff takes unit Tank, boolean activate returns nothing
    /*
    if activate then
        if (GetUnitState(Tank, UNIT_STATE_LIFE) > 0) then
            call SetUnitAbilityLevel( Tank, STRATEGIC_LOCATION_RESISTANCE_ID, 2 )
            call SetUnitAbilityLevel( Tank, STRATEGIC_LOCATION_HEALING_ID, 2 )
            call UnitAddAbility( Tank, STRATEGIC_LOCATION_BUFF_ABILITY_ID)
        endif
    else
        if (GetUnitState(Tank, UNIT_STATE_LIFE) > 0) then      
            call SetUnitAbilityLevel( Tank, STRATEGIC_LOCATION_RESISTANCE_ID, 1 )
            call SetUnitAbilityLevel( Tank, STRATEGIC_LOCATION_HEALING_ID, 1 )
            if GetUnitAbilityLevel(Tank, STRATEGIC_LOCATION_BUFF_ID) > 0 then  
                call UnitRemoveAbility(Tank, STRATEGIC_LOCATION_BUFF_ABILITY_ID)
                call UnitRemoveAbility( Tank, STRATEGIC_LOCATION_BUFF_ID )
            endif
        endif
    endif
    */

endfunction

function AnyAllyHasTankOfType takes player P, integer TankType returns boolean
    local integer i = 1
    set i = 1
    loop
        exitwhen i > GetMaxHumanPlayers()
        if udg_Player_Team[i]==udg_Player_Team[GetPlayerNr(P)] then
            if GetUnitTypeId(udg_Tank[i])==TankType then
                return true
            elseif (GetUnitTypeId(udg_Tank[i])=='H013' and TankType=='H012') or (GetUnitTypeId(udg_Tank[i])=='H02A' and TankType=='H01V') then
                return true
            endif
        endif
        set i = i + 1
    endloop
    return false
endfunction

function IsItemUpgradeable takes item BaseItem returns boolean
    local integer i = 0
    local integer itype = GetItemTypeId(BaseItem)
    loop
        exitwhen udg_Weapon_Basic[i] == null or udg_Weapon_Basic[i] == itype
    endloop
    return udg_Weapon_Basic[i] == itype
endfunction

//Determines, which lane should be targetted, based on the current position
function GetTargetMovePoint takes unit U returns integer
    local integer TeamNumber = udg_Player_Team[GetPlayerNr(GetOwningPlayer(U))]
    local integer MovePoint = 3+25 - TeamNumber
    local location EnemyBase = udg_Move_Points[(3+25 - TeamNumber)]
    local location OwnBase = udg_Move_Points[TeamNumber+25]
    local location Unit = GetUnitLoc(U)

    if DistanceBetweenPoints(Unit, OwnBase) < DistanceBetweenPoints(Unit, EnemyBase) then // 20 - TeamNumber
        if DistanceBetweenPoints(Unit, udg_Move_Points[10 + TeamNumber * 2]) < DistanceBetweenPoints(Unit, udg_Move_Points[17 + TeamNumber]) then
            // Left MovePoint
            set MovePoint = 13
        elseif DistanceBetweenPoints(Unit, udg_Move_Points[13 + TeamNumber * 2]) < DistanceBetweenPoints(Unit, udg_Move_Points[17 + TeamNumber]) then
            // Right MovePoint
            set MovePoint = 16
        else
            // Middle MovePoint
            set MovePoint = 20 - TeamNumber
        endif
    else
        // Enemy Base
        set MovePoint = 3+25 - TeamNumber
    endif
   
    call RemoveLocation(Unit)
    set EnemyBase = null
    set OwnBase = null
    set Unit = null
   
    return MovePoint
endfunction

function TCC_Ability takes player Force, integer UnitID, real X, real Y returns nothing
    local unit dummy = CreateUnit(Force,DUMMY_ID, X, Y, bj_UNIT_FACING)
    local item scanner
   
    // Scan
    if UnitID == '1777' then
        call UnitAddAbility(dummy, 'A03P')
        set scanner = UnitAddItemById(dummy, 'I05Q')
        call UnitUseItem(dummy, scanner)
        call UnitApplyTimedLife(dummy, 'BTLF', 7)
        call RemoveItem(scanner)
    endif
   
    set scanner = null
    set dummy = null
endfunction

function UnitDrop takes player Force, integer UnitID, integer number, real X, real Y returns nothing
    local unit array U
    local effect array E
    local real beginGameTime = TimerGetElapsed(udg_GameTime)
    local integer i = 1

    loop
        exitwhen i > number

        if UnitID == '1999' then
            set U[i]  = CreateUnit( Force, udg_TC_Unit[i], X, Y, bj_UNIT_FACING)
        elseif UnitID == '1888' then
            set U[i]  = CreateUnit( Force, udg_Aura_Creeps[i], X, Y, bj_UNIT_FACING)
        else
            set U[i]  = CreateUnit( Force, UnitID, X, Y, bj_UNIT_FACING)  
        endif
        call UnitAddAbility( U[i], 'Amrf' )
        call UnitRemoveAbility( U[i], 'Amrf' )
        call SetUnitInvulnerable( U[i], true )
        call PauseUnit( U[i], true )
        call SetUnitFlyHeight(U[i], 1000, 0)
        call SetUnitPosition(U[i], X, Y)
        call SetUnitFlyHeight(U[i], 0, 200)

        if (GetUnitTypeId(U[i]) != 'z00D') then
            set E[i] =  AddSpecialEffectTarget( "war3mapImported\\parachute.mdx", U[i], "origin" )
        endif

        set i = i+1
    endloop

    set i = 1
    call WaitForGameTime(beginGameTime + 5.0)

    loop
        exitwhen i > number
        call DestroyEffect( E[i] )
        call SetUnitInvulnerable( U[i], false )
        call PauseUnit( U[i], false )
        //Bomb
        if (GetUnitTypeId(U[i]) == 'h00V') then
            call UnitApplyTimedLife(U[i], 'BTLF', 3.00)
            call FloatingTimedLife(U[i], 3, null, "|cffff0000", "!|r")
            //call SetUnitMoveSpeed(U[i], 0)
        else
            call IssuePointOrderLoc( U[i], "attack", udg_Move_Points[GetTargetMovePoint(U[i])] )
        endif
        set U[i] = null
        set E[i] = null

        set i = i+1
    endloop

endfunction

function RefreshTransparency takes unit U returns nothing
    // Earth Robot
    if GetUnitTypeId(U)=='H01U' then
        call SetUnitVertexColor( U, 255, 255, 255, R2I(Pow(0.85, GetUnitAbilityLevel(U,TRANSPARENT_ARMOR_ABILITY_ID))*255) )
    // Ghost Tank
    elseif GetUnitTypeId(U) == 'H00R' then
        call SetUnitVertexColor( U, 255, 255, 255, 170 )
    else
        call SetUnitVertexColor( U, 255, 255, 255, 255 )
    endif
endfunction

function ShowSpecialOptions takes player p returns nothing
    local string Options = ""
    local player tempP
    local integer i = 0

    if udg_StartingGold!=3000 then
        set Options = Options + I2S(udg_StartingGold)+" Gold, "
    endif
    if udg_CV then
        set Options = Options + "Conquest Victory, "
    endif
    if udg_NoRequirements then
        set Options = Options + "No Tank Requirements, "
    endif
    if udg_NoTinker then
        set Options = Options + "No Tinker, "
    endif
    if udg_NoExploder then
        set Options = Options + "No Exploder, "
    endif
    if udg_NoTrader then
        set Options = Options + "No Trader, "
    endif
    if udg_HighTech then
        set Options = Options + "High Tech, "
    endif
    if udg_Fog then
        set Options = Options + "Fog, "
    endif
    if udg_TankMonopoly then
        set Options = Options + "Tank Monopoly, "
    endif
    if udg_ExtRequirements then
        set Options = Options + "Extended Tank Requirements, "
    endif
   
    if p==null then
        loop
            exitwhen i >= 13
            if Options=="" then
                call DisplayTimedTextToPlayer(Player(i), 0, 0, 20, "No special options were chosen.")
            else
                call DisplayTimedTextToPlayer(Player(i), 0, 0, 20, "Special options: "+SubString(Options,0,StringLength(Options)-2))
            endif
            set i = i + 1
        endloop
    else
        if Options=="" then
            call DisplayTimedTextToPlayer(p, 0, 0, 20, "No special options were chosen.")
        else
            call DisplayTimedTextToPlayer(p, 0, 0, 20, "Special options: "+SubString(Options,0,StringLength(Options)-2))
        endif
    endif
endfunction

function BooleanToColor takes boolean b, boolean enabled returns string
    if enabled then
        if b == true then
            return "|cff00ff00" //green
        else
            return "|cffff0000" //red
        endif
    endif
    return "|cffaaaaaa" // grey
endfunction

function ApplyManaUpgrade takes integer PlayerId returns nothing
    local unit tank = udg_Tank[PlayerId]
    local integer level = ManaUpgrade[PlayerId]
    local integer manaId = 0
   
    //There are several abilities, because you can't level the mana bonus ability
    if (GetUnitState(tank, UNIT_STATE_LIFE) > 0) then
        call UnitRemoveAbility(tank, 'A0DQ')
        call UnitRemoveAbility(tank, 'A0DX')
        call UnitRemoveAbility(tank, 'A0DR')
        call UnitRemoveAbility(tank, 'A0DS')
        call UnitRemoveAbility(tank, 'A0D9')    
        call UnitRemoveAbility(tank, 'A0DT')
        call UnitRemoveAbility(tank, 'A0DU')
        call UnitRemoveAbility(tank, 'A0DV')
        call UnitRemoveAbility(tank, 'A0DW')
        call UnitRemoveAbility(tank, 'A075')
       
        if (level == 1) then
            set manaId = 'A0DQ'
        elseif (level == 2) then
            set manaId = 'A0DX'
        elseif (level == 3) then
            set manaId = 'A0DR'
        elseif (level == 4) then
            set manaId = 'A0DS'        
        elseif (level == 5) then
            set manaId = 'A0D9'
        elseif (level == 6) then
            set manaId = 'A0DT'
        elseif (level == 7) then
            set manaId = 'A0DU'
        elseif (level == 8) then
            set manaId = 'A0DV'
        elseif (level == 9) then
            set manaId = 'A0DW'
        elseif (level == 10) then
            set manaId = 'A075'        
        endif
       
        if (manaId != 0) then
            call UnitAddAbility(tank, manaId)
        endif
    endif
   
    set tank = null
endfunction

function RefreshSkill takes unit U, integer skill returns nothing
    local integer level = GetUnitAbilityLevel(U, skill)

    if (level > 0) then
        call UnitRemoveAbility(U, skill)
        call UnitAddAbility(U, skill)
        call SetUnitAbilityLevel(U, skill, level)
    endif
endfunction

function AddAbilityTimed_Child takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
   
    if (udg_AV_Unit1[LoopTimerId] != null) and (GetUnitAbilityLevel(udg_AV_Unit1[LoopTimerId], udg_AV_Int1[LoopTimerId]) > 0) then
        call UnitRemoveAbility(udg_AV_Unit1[LoopTimerId], udg_AV_Int1[LoopTimerId])
    endif
   
    call ReleaseTimer(GetExpiredTimer())
    call ReleaseHandleIndex(LoopTimerId)
endfunction

function AddAbilityTimed takes unit U, integer skill, integer level, real duration returns nothing
    local timer t
    local integer LoopTimerId  
   
    if (U != null) then
        call UnitAddAbility(U, skill)
        call SetUnitAbilityLevel(U, skill, level)
       
        set t = NewTimer()
        set LoopTimerId = NewTimerIndex(t)
        set udg_AV_Unit1[LoopTimerId] = U
        set udg_AV_Int1[LoopTimerId] = skill
        call TimerStart(t, duration, false, function AddAbilityTimed_Child)
    else
        call DebugMsg("AddAbilityTimed: Unit == null")
    endif
endfunction

function RefreshRuinSupply takes unit Ruin returns nothing
    if (GetUnitTypeId(Ruin) == 'h00Q') then
        call AddUnitToStock(Ruin, BARRICADE_SELL_ID             , 1, 1)
        call AddUnitToStock(Ruin, ROCKET_TOWER_SELL_ID          , 1, 1)
        call AddUnitToStock(Ruin, LASER_TOWER_SELL_ID           , 1, 1)    
        call AddUnitToStock(Ruin, TELEPORT_BEACON_SELL_ID       , 1, 1)
        call AddUnitToStock(Ruin, CAMOUFLAGE_GENERATOR_SELL_ID  , 1, 1)        
    endif
endfunction

function KillTrigger_Child takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
   
    if (udg_AV_Triggers[LoopTimerId] != null) then
        call DestroyTrigger(udg_AV_Triggers[LoopTimerId])
    endif
   
    call ReleaseTimer(GetExpiredTimer())
    call ReleaseHandleIndex(LoopTimerId)
endfunction

function KillTrigger takes trigger trig returns nothing
    local timer t
    local integer LoopTimerId  
   
    set t = NewTimer()
    set LoopTimerId = NewTimerIndex(t)
    set udg_AV_Triggers[LoopTimerId] = trig
    call TimerStart(t, 0, false, function KillTrigger_Child)
endfunction

globals
    integer ItemCount
    real cheapestValue
    item cheapestItem
endglobals

function CountItems takes nothing returns nothing
    set ItemCount = ItemCount + 1
   
    if ((GetWidgetLife(GetEnumItem()) < cheapestValue) or (cheapestValue == 0)) and (GetItemTypeId(GetEnumItem()) != DUMMY_ITEM_ID) then
        set cheapestValue = GetWidgetLife(GetEnumItem())
        set cheapestItem = GetEnumItem()
    endif
endfunction

function IsCoordInJunkyard takes real X, real Y, integer team returns boolean
    return RectContainsCoords(udg_JunkyardRect[team], X, Y)
endfunction

function PlaceItemOnJunkyard takes integer ownerId, item soldItem, boolean droppedOnYard returns nothing
    local item NewItem
    local integer Team = udg_Player_Team[ownerId]
    local integer i = GetItemCharges(soldItem)
    local integer soldId = GetItemTypeId(soldItem)

    // (TempItemID!='I051') and (TempItemID!='I03N') and (TempItemID!='I04O')
    if (GetWidgetLife(soldItem)<700) or (soldId == DUMMY_ITEM_ID) or (soldId=='I051') or (soldId=='I03N') or (soldId=='I04O') then
        //items which are worth less are not placed on the Junkyard
        return
    endif
   
    set ItemCount = 0
    set cheapestValue = 0
    set cheapestItem = null
    call EnumItemsInRect(udg_JunkyardRect[Team], FILTER_NULL, function CountItems)
   
    if (ItemCount >= MAX_JUNKYARD_ITEM_COUNT) then
        call RemoveItem(cheapestItem)
        call DebugMsg("Removed cheapest item")
    endif
   
    // When you place the item directly on the yard, there is no need to create a new one (except when it has charges)
    if droppedOnYard then
        if i>1 then
            call SetItemCharges(soldItem,1)
        endif
        call SetItemUserData(soldItem, 2)
        set i = i - 1
    else
        if (i == 0) then
            set i = i + 1
        endif
    endif
   
    loop
        exitwhen i <= 0
        set NewItem = CreateItem(GetItemTypeId(soldItem),GetRandomReal(GetRectMinX(udg_JunkyardRect[Team])+16,GetRectMaxX(udg_JunkyardRect[Team])-16), GetRandomReal(GetRectMinY(udg_JunkyardRect[Team])+16,GetRectMaxY(udg_JunkyardRect[Team])-16))
        call SetItemPlayer( NewItem, GetPlayer(ownerId), true )
        call SetItemUserData(NewItem, 2)
        set i = i - 1
    endloop
endfunction

function CountItemTypeInInventory takes unit U, integer itemId returns integer
    local integer index = 0
    local integer count = 0

    loop
        if (GetItemTypeId(UnitItemInSlot(U, index)) == itemId) then
            set count = count + 1
        endif

        set index = index + 1
        exitwhen index >= bj_MAX_INVENTORY
    endloop

    return count
endfunction

function CountBlockingArmorInInventory takes unit U returns integer
    local integer index = 0
    local integer count = 0

    loop
        if IsBlockingArmor(UnitItemInSlot(U, index)) then
            set count = count + 1
        endif

        set index = index + 1
        exitwhen index >= bj_MAX_INVENTORY
    endloop

    return count
endfunction

function Factory_EnableHealing takes unit building, boolean enable returns nothing
    if (GetUnitTypeId(building) == 'h01Z') then
        if enable then
            call UnitAddAbility(building, 'A00G')
            call UnitAddAbility(building, 'A07A')
            call UnitAddAbility(building, 'A00Y')
        else
            call UnitRemoveAbility(building, 'A00G')
            call UnitRemoveAbility(building, 'A07A')
            call UnitRemoveAbility(building, 'A00Y')        
        endif
    endif
endfunction

function HealOverTime_Loop takes nothing returns nothing
    local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
    local unit Target = udg_AV_Unit1[LoopTimerId]
    local real HealedLife = udg_AV_Real1[LoopTimerId]
    local real HealedMana = udg_AV_Real2[LoopTimerId]
    local real Intervall = 0.10
    local real HealedAmount = 0
    local integer Ability = udg_AV_Int1[LoopTimerId]
    local real Duration = udg_AV_Real3[LoopTimerId]
    local boolean healEnded = false
    /*
    //remove the buff, when the unit is fully healed
    if (GetUnitState(Target, UNIT_STATE_MAX_LIFE) == GetUnitState(Target, UNIT_STATE_LIFE)) and (GetUnitState(Target, UNIT_STATE_MAX_MANA) == GetUnitState(Target, UNIT_STATE_MANA)) then
        //call DebugMsg("Stop triggered heal (full hp)")
        call UnitRemoveAbility(Target, Ability)
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)
        set Target = null        
        return
    endif
    */

    if (Ability == 0) then
        if ((udg_AV_Int2[LoopTimerId] * Intervall) > Duration) then
            set healEnded = true
        endif
    else
        if (GetUnitAbilityLevel(Target, Ability) <= 0) then
            set healEnded = true
        endif
    endif
   
    if healEnded then
        call UnitRemoveAbility(Target, Ability)
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)
    else        
        set HealedAmount = HealedLife / Duration * Intervall
        call SetUnitState(Target, UNIT_STATE_LIFE, GetUnitState(Target, UNIT_STATE_LIFE) + HealedAmount)
       
        set HealedAmount = HealedMana / Duration * Intervall
        call SetUnitState(Target, UNIT_STATE_MANA, GetUnitState(Target, UNIT_STATE_MANA) + HealedAmount)
    endif
   
    set udg_AV_Int2[LoopTimerId] = udg_AV_Int2[LoopTimerId] + 1
   
    set Target = null
endfunction

function HealOverTime takes unit Target, real healHP, real healMana, real duration, integer abilityId, boolean showHeal returns nothing
    local timer t
    local integer LoopTimerId

    if (showHeal) then
        if (healHP > 0) then
            call ShowTextTag("|cff00ff00+"+I2S(R2I(healHP))+"|r", Target, 0, 0, GetOwningPlayer(Target))
        endif
       
        if (healMana > 0) then
            call ShowTextTag("|cff3264C8+"+I2S(R2I(healMana))+"|r", Target, 0, 50, GetOwningPlayer(Target))
        endif
    endif
   
    set t = NewTimer()
    set LoopTimerId = NewTimerIndex(t)
    set udg_AV_Unit1[LoopTimerId] = Target
    set udg_AV_Int1[LoopTimerId] = abilityId
    set udg_AV_Int2[LoopTimerId] = 0
    set udg_AV_Real1[LoopTimerId] = healHP
    set udg_AV_Real2[LoopTimerId] = healMana
    set udg_AV_Real3[LoopTimerId] = duration
    call TimerStart(t, 0.10, true, function HealOverTime_Loop)
endfunction

function DeactivateAirOnlyWeapons takes unit U returns nothing
    local integer i = 0
    local integer weaponID    
    local real ItemLife
   
    loop
        exitwhen i > 5
        set weaponID = GetItemTypeId(UnitItemInSlot(U, i))
        //Bombs, Bombs (Upgrade), Flak, Flak (Upgrade)
        if weaponID == 'I02Z' or weaponID == 'I03C' or weaponID == 'I057' or weaponID == 'I058' then
            set ItemLife = GetWidgetLife(UnitItemInSlot(U, i))
            call RemoveItem(UnitItemInSlot(U, i))
            call UnitAddItemToSlotById(U, 'I051', i)
            call SetWidgetLife(UnitItemInSlot(U, i), ItemLife)
        endif
        set i = i + 1
    endloop
endfunction

function ActivateAirOnlyWeapons takes unit U returns nothing
    local integer i = 0
    local integer PlayerId = GetPlayerNr(GetOwningPlayer(U))   
    local integer weaponID    
    local item bombs    
   
    loop
        exitwhen i > 5
        if GetItemTypeId(UnitItemInSlot(U, i)) == 'I051' then

            if GetWidgetLife(UnitItemInSlot(U, i)) == 6000 then
                set weaponID = 'I02Z'
            elseif GetWidgetLife(UnitItemInSlot(U, i)) == 7500 then
                set weaponID = 'I03C'
            elseif GetWidgetLife(UnitItemInSlot(U, i)) == 8000 then
                set weaponID = 'I057'
            elseif GetWidgetLife(UnitItemInSlot(U, i)) == 11000 then
                set weaponID = 'I058'
            endif

            call RemoveItem(UnitItemInSlot(U, i))

            if GetUnitState(U, UNIT_STATE_LIFE) > 0 then
                set bombs = UnitAddItemById( U, weaponID)
                call SetItemPlayer( bombs, GetOwningPlayer(U), false )
                if udg_Afk[PlayerId] then
                    call SetItemDroppable( bombs, false )
                endif
            else
                //when the tank is dead, give the weapon to him, after he revives
                call GiveItemToDeadTank(PlayerId, weaponID)
            endif
        endif
        set i = i + 1
    endloop
   
    set bombs = null
endfunction

function TradeMarketTeam takes unit market, integer team returns boolean
    return ((market == udg_TradeMarket[team]) or (market == udg_TradeMarket_Page2[team]))
endfunction

function SetLaneCreepFlag takes unit creep, integer flag returns nothing
    local integer setFlags = GetUnitUserData(creep)
    local integer tempFlags = setFlags
    local integer i = CF_MAX_FLAGS
    local integer currentFlag
   
    loop
        exitwhen i < 0
        set currentFlag = R2I(Pow(2, i))
       
        if (tempFlags >= currentFlag) then
            set tempFlags = tempFlags - currentFlag
        elseif (currentFlag == flag) then
            call SetUnitUserData(creep, setFlags + flag)
            return
        endif

        set i = i - 1
    endloop
endfunction

function GetLaneCreepFlag takes unit creep, integer flag returns boolean
    local integer setFlags = GetUnitUserData(creep)
    local integer i = CF_MAX_FLAGS
    local integer currentFlag
   
    loop
        exitwhen i < 0
        set currentFlag = R2I(Pow(2, i))
       
        if (setFlags >= currentFlag) then
            if (currentFlag == flag) then
                return true
            endif
            set setFlags = setFlags - currentFlag
        endif

        set i = i - 1
    endloop
   
    return false
endfunction

function GetSupportRewardForItem takes integer itemId returns real
    // Troop Command Center, Healing Factory, Spawning Factory
    if (itemId=='I04Y') or (itemId=='I00Z') or (itemId=='I04Z') then
        return 2.0
    // Detector, Teleport Breaker
    elseif (itemId=='I03F') or (itemId=='I04P') then
        return 0.5
    endif
   
    return 0.0
endfunction

function CheckItemRequirements takes item checkItem, player owner returns string
    local integer WeaponsLevel = GetPlayerTechCount(owner, 'R002', true)
    local integer ArmorLevel = GetPlayerTechCount(owner, 'R003', true)
    local integer ReqWeapon = 0
    local integer ReqArmor = 0
    local integer Id = GetItemTypeId(checkItem)
    local boolean BothRequired = true // true, when the item requires both upgrades at the stated level
    local string s = null
   
    if Id=='I013' then      // Reinforcement
        set BothRequired = false
        set ReqWeapon = 2
        set ReqArmor = 2
    elseif Id=='I04X' then  // Mortar Team
        set ReqWeapon = 1
    elseif Id=='I04Q' then  // Bomb
        set ReqWeapon = 4
    elseif Id=='I037' then  // Orbital Command
        set ReqWeapon = 5
    elseif Id=='I00Z' then  // Factory
        set ReqWeapon = 2
        set ReqArmor = 2
    elseif Id=='I04Y' then  // Troop Command Center
        set ReqWeapon = 4
        set ReqArmor = 4
    elseif Id=='I068' then  // Black Sun Project
        set ReqWeapon = 6
    endif
   
    if (WeaponsLevel < ReqWeapon) then
        set s = "|cfffed312You need|r " + I2S(ReqWeapon - WeaponsLevel) + " |cfffed312more weapon"
    endif
    if (ArmorLevel < ReqArmor) then
        if (s != null) then
            if BothRequired then
                set s = s + " and|r "
            else
                set s = s + " or|r "
            endif
            set s = s + I2S(ReqArmor - ArmorLevel) + " |cfffed312more armor upgrades.|r"
        elseif (s == null) and BothRequired then
            set s = "|cfffed312You need|r " + I2S(ReqArmor - ArmorLevel) + " |cfffed312more armor upgrades.|r"
        endif
    elseif (s != null) and (BothRequired == false) then        
        // when only one upgrade type is required, but the armor requirement is fulfilled, erase the message about the weapon requirement again
        set s = null
    elseif (s != null) then
        set s = s + " upgrades.|r"
    endif
   
    return s
endfunction

function UnitAddItemToSlot takes unit target, item targetItem, integer slot returns boolean
    local integer i = 0
    local item SlotItem
   
    if (UnitInventoryCount(target) >= UnitInventorySize(target) ) then
        return false
    else
        // only bother trying to place the item in the given slot, when it is actually free
        if (UnitItemInSlot(target, slot) == null) then
            loop
                exitwhen (i > slot)
               
                if (i != slot) then
                    if (UnitItemInSlot(target, i) == null) then
                        call UnitAddItemToSlotById(target, AWAY_ITEM_ID, i)
                    endif
                else
                    call UnitAddItem(target, targetItem)
                endif                
               
                set i = i + 1
            endloop
           
            set i = 0
            loop
                exitwhen i >= UnitInventorySize(target)  
                set SlotItem = UnitItemInSlot(target, i)
                if SlotItem!=null then
                    if GetItemTypeId(SlotItem) == AWAY_ITEM_ID then
                        call RemoveItem( SlotItem )
                    endif
                endif
                set i = i + 1
            endloop
           
            set SlotItem = null
        else
            call UnitAddItem(target, targetItem)
        endif
    endif
   
    return true
endfunction

function ReactivateBlockingArmor takes unit caster returns nothing
    local integer i = 0
    local item temp
    local real itemLife
   
    loop
        exitwhen i >= UnitInventorySize(caster)  
        set temp = UnitItemInSlot(caster, i)
       
        if IsBlockingArmor(temp) then
            call UnitRemoveItem(caster, temp)
            // if the unit is on its own Junkyard, then dropped item will be sold, which we don't want
            if IsCoordInJunkyard(GetItemX(temp), GetItemY(temp), udg_Player_Team[GetPlayerNr(GetItemPlayer(temp))]) then
                set IgnoreNextItemOnJunkyard = true
            endif
            call UnitAddItemToSlot(caster, temp, i)
        endif
        set i = i + 1
    endloop
       
    if (GetUnitAbilityLevel(caster, DEFLECTIVE_UPGRADE_BUFF_ABILITY_ID) == 0) and UnitHasItemOfTypeBJ(caster, DEFLECTIVE_UPGRADE_ITEM_ID) then
        call UnitAddAbility(caster, DEFLECTIVE_UPGRADE_BUFF_ABILITY_ID)
    endif
   
    set temp = null
endfunction

function ReAddTempItems takes unit Tank returns nothing
    local item TempItem
    local integer top = GetPlayerNr(GetOwningPlayer(Tank)) * 6
    local integer i = top - 5
   
    call DebugMsg("Readd")
   
    loop
        exitwhen i > top
        if udg_Item_CreateForHero[i] != null and udg_Item_CreateForHero[i] != 0 then
            set TempItem = UnitAddItemById( Tank, udg_Item_CreateForHero[i] )
            call DebugMsg("Added " + GetItemName(TempItem))
            call SetItemPlayer( TempItem, GetOwningPlayer(Tank), false )
            if udg_Afk[GetPlayerNr(GetOwningPlayer(Tank))] then
                call SetItemDroppable( TempItem, false )
            endif
            set udg_Item_CreateForHero[i] = 0
        endif
        set i = i + 1
    endloop
   
    set TempItem = null
endfunction


endlibrary
 
include("injectTracer.gsl");

// USAGE
// This code can be executed using the JassNewGen GMSI injection.
//
// If you want to inject the stack tracer, be sure to open the output map in the editor and save it once more,
// so that warcraft compiles the map. GMSI only changes the source data (e.g. vJass code and not the final code).

// Inject the tracer into the map.
// The first argument determines whether "debug" keywords are used,
// the second one sets the rate of registered lines.
injectTracer(map, false, 1);

// Save the map into the same folder, but add GMSI suffix
string savePath = substr(@map,0,strlen(@map)-4)+" GMSI.w3x";
saveMap(map,savePath);

// Done.
echoln("\nMap saved to: "+savePath);
echoln("Now open the map in the WorldEditor and re-save it.");
This library contains functions used for calculating the bounty and anything related to tank deaths
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Bounty requires BTFramework
globals
    real KillDeathBountyInfluence = 0.00    // Set by the game mode, determines how much influence your KD stats have on your bounty
    real BasicBounty = 0.00                 // Set by the game mode, the bounty cannot go under this percantage of your basic bounty
    real MinBounty = 0.01                   // 125 + TankWorth * MinBounty is the smallest bounty allowed
    real MaxBounty = 0.03                   // 125 + TankWorth * MaxBounty is the biggest bounty allowed
   
    real BountyIncrease = 0.05              // Only used with the Alternate Bounty Mode: your bounty increases by this percentage times the bounty of your kill
    real BountyDecrease = 0.12              // Only used with the Alternate Bounty Mode: your bounty decreases by this percentage when you die
   
    real KillingSpree_Bonus = 0.05          // the percentage of the original bounty that is added for every additional kill in the spree
    real AssistSpree_Bonus = 0.05           // the percentage of the original bounty that is added for each additional assist in the spree
endglobals

 function GiveItemToDeadTank takes integer playerId, integer weaponId returns nothing
    local integer j = 1
   
    loop
        exitwhen j == 0
       
        if udg_Item_CreateForHero[(playerId - 1) * 6 + j] == null or udg_Item_CreateForHero[(playerId - 1) * 6 + j] == 0 then
            set udg_Item_CreateForHero[(playerId - 1) * 6 + j] = weaponId
            set j = 0
        else
            set j = j + 1
        endif
    endloop
endfunction

function GetKillDeathRatio takes integer p returns real
    local real r
    if udg_Deaths[p]-GotTeamkilled[p] != 0 then
        if (udg_Kills[p] == 0) then
            set r = I2R(udg_Kills[p]+1)/I2R(udg_Deaths[p]-GotTeamkilled[p])
        else
            set r = I2R(udg_Kills[p])/I2R(udg_Deaths[p]-GotTeamkilled[p])
        endif
    else
        set r = 1.00
    endif
    return r
endfunction

function GetBountyKillDeathRatio takes integer p returns real
    local real r = RMinBJ(1.00, GetKillDeathRatio(p) + BasicBounty)
    //if r < 1.00 and GetInventoryIndexOfItemTypeBJ(udg_Tank[p], 'I01W') == 0 and GetInventoryIndexOfItemTypeBJ(udg_Tank[p], 'I04D') == 0 and GetInventoryIndexOfItemTypeBJ(udg_Tank[p], 'I01X') == 0 then
    //    set r = RMinBJ(1.00, r + ExplosivesFactor)
    //endif

    set r = 1.00 - (1.00-r) * KillDeathBountyInfluence
    return r
endfunction

function GetBountyBonus takes integer killingId returns real
    if (udg_KillingSpree[killingId] < 3) then
        return 1.0
    else
        return (1.0 + KillingSpree_Bonus * IMinBJ(10, udg_KillingSpree[killingId]))
    endif
   
    return 1.0
endfunction

function GetAssistBonus takes integer assistsId returns real
    if (udg_AssistSpree[assistsId] < 3) then
        return 0.0
    else
        return (AssistSpree_Bonus * IMinBJ(10, udg_AssistSpree[assistsId]))
    endif
   
    return 0.0
endfunction

function GetBounty takes unit Tank, boolean bounty returns integer
    local integer ownerID
    if Tank==null then
        return 0
    endif
    set ownerID = GetPlayerNr(GetOwningPlayer(Tank))
    if Tank!=udg_Tank[ownerID] then
        return 1
    endif
    if bounty then
        return R2I(RMaxBJ(udg_TankBounty[ownerID], 1))
    endif
    return udg_TankCosts[ownerID]
endfunction

function UpdateBounty takes player owner, integer amount returns nothing
    local integer ownerID = GetPlayerNr(owner)
    local real minBounty = 125 + udg_TankCosts[ownerID] * MinBounty
    local real maxBounty = 125 + udg_TankCosts[ownerID] * MaxBounty
   
    if AlternateBounty then
        if (GetPlayerNr(owner) <= GetMaxHumanPlayers()) then
            if (amount > 0) then
                set udg_TankBounty[ownerID] = RMinBJ(maxBounty * GeneralGoldFactor, udg_TankBounty[ownerID] + (amount * BountyIncrease))
            else
                set udg_TankBounty[ownerID] = RMaxBJ(minBounty * GeneralGoldFactor, udg_TankBounty[ownerID] + (amount * BountyDecrease))
            endif
            call ModifyHeroStat( bj_HEROSTAT_STR, udg_Tank[GetPlayerNr(owner)], bj_MODIFYMETHOD_SET, R2I(udg_TankBounty[ownerID]) )
        endif
    endif
endfunction

function ResetMoveSpeedIndicator takes unit Tank returns nothing
    local integer i = 0
   
    loop
        exitwhen i > 8
       
        call UnitRemoveAbility(Tank, udg_SpeedIndicatorPos[i])
        call UnitRemoveAbility(Tank, udg_SpeedIndicatorNeg[i])
       
        set i = i + 1
    endloop
endfunction

function GetRespawnTime takes unit tank, boolean showIncrease returns integer
    local integer ownerId = GetPlayerNr(GetOwningPlayer(tank))
    local integer ownerTeam = udg_Player_Team[ownerId]
    local integer devastatorLevel = GetUnitAbilityLevel(tank, DEVASTATOR_SHOT_BUFF_ID)
    local real devastatorFactor = RESPAWN_INCREASE_BASE + ( devastatorLevel * RESPAWN_INCREASE_UP )
    local real baseTime = 10.00 + udg_TankCosts[ownerId]/3333.00
    local real timeFactor = 1.0
   
    if (ownerTeam == 1) and RectContainsUnit(gg_rct_Team_1_Base_Main, tank) then
        set timeFactor = timeFactor + 0.20
    elseif (ownerTeam == 2) and RectContainsUnit(gg_rct_Team_2_Base_Main, tank) then
        set timeFactor = timeFactor + 0.20
    endif
   
    if (devastatorLevel > 0) then
        set timeFactor = timeFactor + devastatorFactor
    endif
   
    if (showIncrease and (devastatorLevel > 0)) then
        call ShowTextTag("+" + I2S(R2I(baseTime * (timeFactor-1))) + "s respawn" , tank , 0 , 100 , null)
    endif
   
    return R2I(baseTime * timeFactor)
endfunction

function SetRespawnTimeIndicator takes unit tank returns nothing
    //call ModifyHeroStat( bj_HEROSTAT_AGI, tank, bj_MODIFYMETHOD_SET, GetRespawnTime(tank, false) )
endfunction

function SetMoveSpeedIndicator takes unit Tank returns nothing
    //local integer defaultSpeed = R2I(GetUnitDefaultMoveSpeed(Tank))
    local integer currentSpeed = R2I(GetUnitMoveSpeed(Tank))
    //local integer a= IAbsBJ(defaultSpeed-currentSpeed)
    //local integer array b
    //local integer c=1
    //local integer i=0
    //local integer max = 8 //the number of speed indicator skills
   
    if currentSpeed>0 then
        call ModifyHeroStat( bj_HEROSTAT_INT, Tank, bj_MODIFYMETHOD_SET, currentSpeed )
    else
        call ModifyHeroStat( bj_HEROSTAT_INT, Tank, bj_MODIFYMETHOD_SET, 1 )
    endif

    //Idea: display the default speed as a white number and the difference to the current speed in either a green or red number
    //Problem: when displaying a red number (substracting int), the tank cannot regenerate any mana
    //Problem 2: due to the adding and removing of the abilities, the current aiming order of your tanks gets disrupted
   
    //loop
    //    exitwhen c==0
    //    set c=a/2
    //    set b[i]=a-c*2
    //    set a=c
    //    set i=i+1
    //endloop
    //set i=0
   
    //call ResetMoveSpeedIndicator(Tank)
   
    //if (currentSpeed > defaultSpeed) then
    //    loop
    //        exitwhen i>max

    //        if b[i]==1 then
    //            call UnitAddAbility(Tank, udg_SpeedIndicatorPos[i])
    //        else
    //            call UnitRemoveAbility(Tank, udg_SpeedIndicatorPos[i])
    //        endif
    //        set i=i+1
    //    endloop
    //else
    //    loop
    //        exitwhen i>max

    //        if b[i]==1 then
    //            call UnitAddAbility(Tank, udg_SpeedIndicatorNeg[i])
    //        else
    //            call UnitRemoveAbility(Tank, udg_SpeedIndicatorNeg[i])
    //        endif
    //        set i=i+1
    //    endloop
    //endif    
endfunction

function UpdateTankCosts takes integer ownerID returns nothing
    local unit Tank = udg_Tank[ownerID]
    local integer i
    local integer iend
    local integer worth = GetUnitPointValue(Tank)
    local integer RewardID
    local integer ItemCharges
    local item Temp
    local unit PickUnit
    local group G = NewGroup()
 
    call GroupEnumUnitsOfPlayer(G, GetOwningPlayer(Tank), FILTER_NULL)

    loop
        set PickUnit = FirstOfGroup( G )
        exitwhen PickUnit == null
        set i = 0
        set iend = UnitInventorySize(PickUnit)
        loop
            exitwhen i >= iend
            set Temp = UnitItemInSlot(PickUnit, i)
            if Temp != null then
                set ItemCharges = GetItemCharges(Temp)
           
                if ItemCharges == 0 then
                    set worth = worth + R2I(GetWidgetLife(Temp))
                elseif GetItemType(Temp) == ITEM_TYPE_CHARGED then
                    if udg_Player_Team[ownerID] == 1 then
                        set RewardID = R2I(GetWidgetLife(Temp))  
                    else
                        set RewardID = 11 - R2I(GetWidgetLife(Temp))
                    endif

                    set worth = worth + ItemCharges * 5 * udg_Trader_Gold[RewardID]
                else
                    set worth = worth + R2I( GetWidgetLife(Temp) * I2R(ItemCharges) )
                endif
            endif
            set i = i + 1
        endloop
        call GroupRemoveUnit( G, PickUnit )
    endloop
    call ReleaseGroup(G)

    call SetMoveSpeedIndicator(Tank)
   
    set Temp = null
    set udg_TankCosts[ownerID] = worth
    if not AlternateBounty then
        set udg_TankBounty[ownerID] = (((125 + worth / 60) * GetBountyKillDeathRatio(ownerID) * GeneralGoldFactor))
        call ModifyHeroStat( bj_HEROSTAT_STR, Tank, bj_MODIFYMETHOD_SET, R2I(udg_TankBounty[ownerID]) )
    endif
   
    //call SetRespawnTimeIndicator(Tank)

    set Tank = null
    set G = null
endfunction

function GetBountyFactor takes nothing returns real
    return GeneralGoldFactor+TimerGetElapsed(udg_GameTime)/3600
endfunction

function GetForceIncome takes integer forceGold, integer playersInTeam returns integer
    return (4 * forceGold / ( playersInTeam * 9 + 15 ))
endfunction

function GetBaseIncome takes integer playersInTeam returns real
    return ( IncomeFactor * (32.0 / ( playersInTeam*3+5 )) )
endfunction

function GetPassiveIncome takes real goldRemainder, integer playersInTeam returns real
    local real base = GetBaseIncome(playersInTeam)
    local real value = 0
   
    set value = RMaxBJ(goldRemainder/75.0, base)
    if (value > goldRemainder) then
        set value = goldRemainder
    endif
   
    return (value + base)
endfunction
endlibrary
//TESH.scrollpos=134
//TESH.alwaysfold=0
library DamageDetection initializer Init requires Effects

// The interface for functions, that include the code, that gets executed, once a registered unit takes damage
function interface DamageAlterationFunction takes nothing returns real
// The interface for functions, that include the code, that gets executed, after the final damage has been calculated
function interface PostDamageFunction takes real finalDamage returns nothing

globals
    constant real MIN_TRIGGER_DAMAGE = 2.0      // at least this much damage has to be dealt to activate the calculations

    unit DamageDetection_DamageSource           // the unit from which the damage came from
    unit DamageDetection_DamageTarget           // the unit that suffered the damage
    real DamageDetection_Damage                 // the amount of the taken damage
    integer DamageDetection_Progress = 0        // gets bigger than 1, if this trigger gets called recursively -> don't deal damage anymore within this trigger
    boolean DamageDetection_Pause = false       // set to true, if you want the system to ignore damage being dealt
   
    integer DamageDetection_Count = 0           // counts, how many skill trigger use the damage detection at the moment, when 0, the trigger gets disabled

    private integer RegisteredAddendFunctions = 0                               // the amount of functions, that alter the taken damage by absolute numbers, used for iterations
    DamageAlterationFunction array DamageDetection_AddendCode[MAX_ARRAY_SIZE]   // the code that gets executed, once a registered unit takes damage
    private integer RegisteredFactorFunctions = 0                               // the amount of functions, that alter the taken damage by relative numbers, used for iterations
    DamageAlterationFunction array DamageDetection_FactorCode[MAX_ARRAY_SIZE]   // the code that gets executed, once a registered unit takes damage
    private integer RegisteredPostFunctions = 0                                 // the amount of functions, that are executed, after the final damage has been calculated, used for iterations
    PostDamageFunction array DamageDetection_PostCode[MAX_ARRAY_SIZE]           // the code that gets executed, after the final damage has been calculated
   
    private integer array FunctionIsActive[MAX_ARRAY_SIZE]                      // to reduce the overhead, just check for this variable, instead of using the "evaluate()" function -> better perfomance
   
    private constant integer ADDEND_START_ID = 0
    private constant integer FACTOR_START_ID = 2500
    private constant integer POST_START_ID = 5000
endglobals

// ---------------------------------------------------------------------------------------
// Example for a function, that's registered with one of the RegisterDamageDetectionCode- functions

// no parameters and returns real, it has to be this way, to match the function interface
function SkillEffect takes nothing returns real
    // don't do anything unnecessary, end the function as soon as possible    
    if GetUnitAbilityLevel(DamageDetection_DamageTarget, 'A123') > 0 then
        // if this is registered as a AddendCode function, this will reduce the incoming damage by 100
        return -100.0
    endif
   
    return 0.0
endfunction

// Example for registering the skill trigger for the damage detection
// This registers a specific function from the skill trigger with the damage detection

// call RegisterDamageDetectionCodeRelative(damDetFunction.Trig_Banish_DamageDetection)


// ---------------------------------------------------------------------------------------

// Since we don't want to waste any performance, this trigger only runs, when it's really needed
// This means, that every skill, that wants to use damage detection, has to activate it with EnableSkillDamageDetection
// but must also deactivate it, once the skill duration is over, with DisableSkillDamageDetection
// So it's discouraged to use this trigger for passive abilities, since they are running all the time
// (which is the case for the upgraded Deflective Armor, so yeah ... so much for that)

// If this trigger is not yet running, activate it and count how often is has been activated
function EnableSkillDamageDetection takes integer functionId returns nothing
    if DamageDetection_Count == 0 then
        call EnableTrigger( gg_trg_Damage_Detection_System )
    endif
   
    set FunctionIsActive[functionId] = FunctionIsActive[functionId] + 1
    set DamageDetection_Count = DamageDetection_Count + 1
    //call DebugMsg("DD count: " + I2S(DamageDetection_Count))
endfunction

// If no skill trigger needs damage detection at the moment, deactivate it
// Note, that either the damage detection is running for all skill trigger or not at all
// By calling this function, you are not disabling the damage detection for a specific skill
function DisableSkillDamageDetection takes integer functionId returns nothing
   
    if (DamageDetection_Count > 0) then
        set DamageDetection_Count = DamageDetection_Count - 1
    else
        call DebugMsg("Disable SDD - " + I2S(DamageDetection_Count))
        call SendBotDataOnline(DEBUG_INFO_DATA, "Disable SDD - " + I2S(DamageDetection_Count))
    endif
   
    set FunctionIsActive[functionId] = FunctionIsActive[functionId] - 1
   
    if DamageDetection_Count <= 0 then
        call DisableTrigger( gg_trg_Damage_Detection_System )
    endif
endfunction

function PauseSkillDamageDetection takes boolean pause returns nothing
    set DamageDetection_Pause = pause
endfunction

function RegisterUnitForSkillDamageDetection takes unit U returns nothing
    call TriggerRegisterUnitEvent( gg_trg_Damage_Detection_System, U, EVENT_UNIT_DAMAGED )
endfunction

// Registers functions, that modify the taken damage by adding / substracting a constant number
function RegisterDamageDetectionCodeAbsolute takes DamageAlterationFunction ddfunc returns integer
    set DamageDetection_AddendCode[RegisteredAddendFunctions] = ddfunc
    set RegisteredAddendFunctions = RegisteredAddendFunctions + 1
   
    return ADDEND_START_ID + (RegisteredAddendFunctions-1)
endfunction

// Registers functions, that multiply the taken damage by a certain factor
function RegisterDamageDetectionCodeRelative takes DamageAlterationFunction ddfunc returns integer
    set DamageDetection_FactorCode[RegisteredFactorFunctions] = ddfunc
    set RegisteredFactorFunctions = RegisteredFactorFunctions + 1
   
    return FACTOR_START_ID + (RegisteredFactorFunctions-1)
endfunction

// Registers functions, that are executed after the final damage has been calculated
// They only evaluate the taken damage, they do not alter it anymore
function RegisterDamageDetectionCodePost takes PostDamageFunction ddfunc returns integer
    set DamageDetection_PostCode[RegisteredPostFunctions] = ddfunc
    set RegisteredPostFunctions = RegisteredPostFunctions + 1
   
    return POST_START_ID + (RegisteredPostFunctions-1)
endfunction

// Use this function, if you want to deal damage within a skill effect trigger
// This function prevents an endless re-triggering of the damage detection and with it, a crash
function DDS_UnitDamageTarget takes unit source, unit target, real damage returns nothing
    //if (DamageDetection_Progress <= 1) then
    call PauseSkillDamageDetection(true)
        call UnitDamageTarget(source, target, damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
    call PauseSkillDamageDetection(false)
    //endif
endfunction

function Trig_Damage_Detection_System_ShowBlockedDamage takes unit target, real amount returns nothing
    local texttag tag
    local real PosX
    local real PosY
    local real PosZ
    local boolean visibility = false
   
    if (HitpointChanges) then
        set tag = CreateTextTag(  )
        set PosX = GetUnitX(target)
        set PosY = GetUnitY(target)
        set PosZ = GetUnitFlyHeight(target)

        if GetLocalPlayer() == GetOwningPlayer(target) then
            set visibility = true
        endif
       
        //s - string to be shown, including color code
        call SetTextTagText(tag, "|c00ffdead"+I2S(R2I(amount))+"|r", 0.013)
        call SetTextTagPos(tag, PosX, PosY, PosZ)
        call SetTextTagVisibility(tag, visibility)
        call SetTextTagVelocity(tag, 0, -0.02)
        call SetTextTagFadepoint(tag, 0.5 )
        call SetTextTagLifespan(tag, 1 )
        call SetTextTagPermanent(tag, false )
       
        set tag = null
    endif
endfunction

function Trig_Damage_Detection_System_Actions takes nothing returns nothing
    local integer i = 0
    local real damageFactor = 1.0
    local real damageAddend = 0.0
    local real damageAdjustment = 0.0
   
    if (DamageDetection_Pause == false) then    
        set DamageDetection_Progress = DamageDetection_Progress + 1
        set DamageDetection_DamageSource = GetEventDamageSource()
        set DamageDetection_DamageTarget = GetTriggerUnit()
        set DamageDetection_Damage = GetEventDamage()
       
        if (DamageDetection_Damage > MIN_TRIGGER_DAMAGE) then
            // first, check all registered functions and their influence on the triggering unit
            loop
                exitwhen i >= RegisteredFactorFunctions
                if (FunctionIsActive[FACTOR_START_ID+i] > 0) then
                    set damageFactor = damageFactor + DamageDetection_FactorCode[i].evaluate()
                endif
                //call DebugMsg("Factor: " + R2S(damageFactor))
                set i = i + 1
            endloop
           
            set i = 0
            loop
                exitwhen i >= RegisteredAddendFunctions
                if (FunctionIsActive[ADDEND_START_ID+i] > 0) then
                    set damageAddend = damageAddend + DamageDetection_AddendCode[i].evaluate()
                endif
                set i = i + 1
            endloop
           
            // also make sure, that those values don't become too small
            set damageFactor = RMaxBJ(damageFactor, 0.0)
            set damageAddend = RMaxBJ(damageAddend, -DamageDetection_Damage)
           
            set damageAdjustment = (damageFactor * (DamageDetection_Damage + damageAddend)) - DamageDetection_Damage
           
            set i = 0
            loop
                exitwhen i >= RegisteredPostFunctions
                if (FunctionIsActive[POST_START_ID+i] > 0) then
                    call DamageDetection_PostCode[i].evaluate(damageAdjustment + DamageDetection_Damage)
                endif
         
                set i = i + 1
            endloop
           
            if (damageAdjustment == 0.0) then
                // do nothing, damage has not been modified
            elseif (damageAdjustment < 0.0) then
                // heal up the damage, that has been dealt to match the blocked / reduced damage
                //call DebugMsg("Blocked damage: " + R2S(-damageAdjustment))
                call Trig_Damage_Detection_System_ShowBlockedDamage(DamageDetection_DamageTarget, damageAdjustment)
                call DynamicDamageBlock(DamageDetection_DamageTarget, -damageAdjustment)
            elseif (damageAdjustment > 0.0) then
            call DebugMsg("Extra damage: " + R2S(damageAdjustment))
                call DisableTrigger(GetTriggeringTrigger())
                call UnitDamageTarget(DamageDetection_DamageSource, DamageDetection_DamageTarget, damageAdjustment, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                call EnableTrigger(GetTriggeringTrigger())
            endif
        endif
       
        set DamageDetection_Progress = DamageDetection_Progress - 1
    endif
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    set gg_trg_Damage_Detection_System = CreateTrigger(  )
    call DisableTrigger( gg_trg_Damage_Detection_System )
    call TriggerAddAction( gg_trg_Damage_Detection_System, function Trig_Damage_Detection_System_Actions )
endfunction
endlibrary
This library contains functions for coding effects, both visual and damage/mana-related
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Effects requires TimerAndAttachments, MapAttachedSettings
    function SetUnitManaDelayed_Child takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer i = GetHandleIndex(t)
       
        call SetUnitState(udg_AV_Unit1[i], UNIT_STATE_MANA, udg_AV_Real1[i])
        call ReleaseTimer(t)
        call ReleaseHandleIndex(i)
    endfunction

    function SetUnitManaDelayed takes unit U, real Mana returns nothing
        local timer t = NewTimer()
        local integer i = NewTimerIndex(t)
        set udg_AV_Unit1[i] = U
        set udg_AV_Real1[i] = Mana
        call TimerStart(t,0.00,false,function SetUnitManaDelayed_Child)
    endfunction

    function DestroyTreesInRange_Enum takes nothing returns nothing
        local destructable Tree = GetEnumDestructable()
        if GetDestructableTypeId(Tree)=='ATtr' and Pow(bj_cineFadeContinueRed-GetDestructableX(Tree),2)+Pow(bj_cineFadeContinueGreen-GetDestructableY(Tree),2) <= bj_enumDestructableRadius then
            call KillDestructable(Tree)
        endif
        set Tree = null
    endfunction

    function DestroyTreesInRange takes real X, real Y, real Range returns nothing
        if not udg_NoEffects then
            call SetRect(udg_TempRect, X-Range,Y-Range,X+Range,Y+Range)
            set bj_cineFadeContinueRed = X
            set bj_cineFadeContinueGreen = Y
            set bj_enumDestructableRadius = Range*Range
            call EnumDestructablesInRect(udg_TempRect, FILTER_NULL, function DestroyTreesInRange_Enum )
        endif
    endfunction

    function ShowEffectAtPos_Child takes nothing returns nothing
        local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
       
        call DestroyEffect(udg_AV_Effect1[LoopTimerId])
        call ReleaseDummy(udg_AV_Unit1[LoopTimerId])
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)
    endfunction
   
    function ShowEffectAtPos takes string Effect, real x, real y, real z, real scale, real duration returns nothing
        local unit EffectDummy = XE_NewDummyUnit(GetPlayer(11), x, y, 270)
        local timer t
        local integer TimerIndex        
       
        call SetUnitScale(EffectDummy, scale, scale, scale)
        call SetUnitFlyHeight(EffectDummy, z, 0)
       
        set t = NewTimer()
        set TimerIndex = NewTimerIndex(t)
        set udg_AV_Unit1[TimerIndex] = EffectDummy
        set udg_AV_Effect1[TimerIndex] = AddSpecialEffectTarget(Effect, EffectDummy, "origin")
        call TimerStart(t, duration, false, function ShowEffectAtPos_Child)

        set EffectDummy = null
    endfunction

    function CreateExplosionSizedTimed takes player Owner, real x, real y, real scaleSize, real scaleSpeed returns unit
        set bj_lastCreatedUnit = CreateUnit( Owner, 'h00W', x, y, 90 )
        call SetUnitPathing(bj_lastCreatedUnit,false)
        call SetUnitX(bj_lastCreatedUnit,x)
        call SetUnitY(bj_lastCreatedUnit,y)
        call SetUnitScale(bj_lastCreatedUnit, scaleSize, scaleSize, scaleSize)
        call SetUnitTimeScale( bj_lastCreatedUnit, scaleSpeed )
        call KillUnit( bj_lastCreatedUnit )
        return bj_lastCreatedUnit
    endfunction

    //Deals max damage in the middle of the area and decreases gradually to the outer area
    function AreaSpellDamageFromUnit takes unit Source, real X, real Y, real Rng, real Dmg, real BuildingsFactor, real AlliesFactor, boolexpr filter returns nothing
        local unit U
        local real Damage
        local real DistX
        local real DistY
        local real Dist
        local group G = NewGroup()
       
        if filter==null then
            set filter=FILTER_NULL
        endif

        call GroupEnumUnitsInRange( G, X, Y, Rng, filter )
        loop
            set U = FirstOfGroup( G )
            exitwhen U == null

            set DistX = X - GetUnitX(U)
            set DistY = Y - GetUnitY(U)
            set Dist = SquareRoot((DistX * DistX) + (DistY * DistY))

            if Dist <= 0.5 * Rng then
                set Damage = Dmg
            else
                set Damage = (1.75-1.5*(Dist/Rng)) * Dmg
            endif

            if IsUnitType(U, UNIT_TYPE_STRUCTURE) and IsUnitAlly(U, GetOwningPlayer(Source)) then
                //Don't deal any damage to allied buildings
                set Damage = 0
            else        
                if IsUnitType(U, UNIT_TYPE_STRUCTURE) then
                    set Damage=Damage*BuildingsFactor
                endif
                if IsUnitAlly(U, GetOwningPlayer(Source)) then
                    set Damage=Damage*AlliesFactor
                endif
            endif

            if (Damage > 0) then
                if (IsUnitType(U, UNIT_TYPE_HERO)) and (GetUnitTypeId(Source) != 'z00P') then
                    call ShowTextTag("|cfffe0101"+I2S(R2I(Damage))+"!|r", U, 50, 50, null)
                endif
                call UnitDamageTarget(Source, U, Damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
            endif

            call GroupRemoveUnit( G, U )
        endloop
        call ReleaseGroup(G)
        set G = null
    endfunction

    function AreaSpellDamageExplosive takes unit Source, real Rng, real Dmg, real BuildingsFactor, real AlliesFactor returns nothing
        call AreaSpellDamageFromUnit( Source, GetUnitX(Source), GetUnitY(Source), Rng, Dmg, BuildingsFactor, AlliesFactor, FILTER_VALID_TARGET )
    endfunction

    //Deals equal damage to each unit in the area
    function FixedAreaDamageFromUnit takes unit Source, real X, real Y, real Rng, real Dmg, real BuildingsFactor, real AlliesFactor, boolean ShowDmg, boolexpr filter returns nothing
        local unit U
        local real Damage
        local group G = NewGroup()
       
        if filter==null then
            set filter=FILTER_NULL
        endif

        call GroupEnumUnitsInRange( G, X, Y, Rng, filter )
        loop
            set U = FirstOfGroup( G )
            exitwhen U == null
           
            set Damage = Dmg

            if IsUnitType(U, UNIT_TYPE_STRUCTURE) then
                set Damage=Damage*BuildingsFactor
            endif
            if IsUnitAlly(U, GetOwningPlayer(Source)) then
                set Damage=Damage*AlliesFactor
            endif
           
            if (IsUnitType(U, UNIT_TYPE_HERO)) and ShowDmg then
                call ShowTextTag("|cfffe0101"+I2S(R2I(Damage))+"!|r", U, 50, 50, null)
            endif
           
            call UnitDamageTarget(Source, U, Damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)

            call GroupRemoveUnit( G, U )
        endloop
        call ReleaseGroup(G)
        set G = null
    endfunction

    function FlashPointSizedTimed takes real x, real y, real power, real size, real startFadeTime, real duration returns nothing
        local real distx = x-GetCameraTargetPositionX()
        local real disty = y-GetCameraTargetPositionY()
        local real dist = SquareRoot(distx*distx + disty*disty)
        if dist > size*2 then
            set power=0
        elseif dist > size then
            set power = power*(2*size-dist)/size
        endif
        if startFadeTime!=0 then
            call AbortCinematicFadeBJ()
            call SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp")
            call SetCineFilterBlendMode(BLEND_MODE_BLEND)
            call SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE)
            call SetCineFilterStartUV(0, 0, 1, 1)
            call SetCineFilterEndUV(0, 0, 1, 1)
            call SetCineFilterStartColor(255, 255, 255, IMaxBJ(R2I(power*2.55),0))
            call SetCineFilterEndColor(255, 255, 255, 0)
            call SetCineFilterDuration(10000)
            call DisplayCineFilter(true)
            call GameTimeWait( startFadeTime )
        endif
        call AbortCinematicFadeBJ()
        call SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp")
        call SetCineFilterBlendMode(BLEND_MODE_BLEND)
        call SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE)
        call SetCineFilterStartUV(0, 0, 1, 1)
        call SetCineFilterEndUV(0, 0, 1, 1)
        call SetCineFilterStartColor(255, 255, 255, IMaxBJ(R2I(power*2.55),0))
        call SetCineFilterEndColor(255, 255, 255, 0)
        call SetCineFilterDuration(duration)
        call DisplayCineFilter(true)
        call FinishCinematicFadeAfterBJ(duration)
    endfunction

    function CameraAddEQNoiseSave takes real magnitude returns nothing
        local real Noise = udg_NoiseForPlayer[GetPlayerNr(GetLocalPlayer())] + magnitude
        local real richter

        set udg_NoiseForPlayer[GetPlayerNr(GetLocalPlayer())] = Noise
        if Noise > 5.0 then
            set richter = 5.0
        else
            if Noise < 2.0 then
                set richter = 2.0
            else
                set richter = Noise
            endif
        endif
        call CameraSetSourceNoise( Noise * 2.0, Noise * Pow( 10, richter ) )
        call CameraSetTargetNoise( Noise * 2.0, Noise * Pow( 10, richter ) )
    endfunction

    function ShakeCamAtLoc_Child takes nothing returns nothing
        local real X = bj_meleeNearestMineDist
        local real Y = bj_cineFadeContinueRed
        local real fadeStart = bj_cineFadeContinueGreen * 0.85
        local real fadeEnd = bj_cineFadeContinueGreen * 1.20
        local real magnitude = bj_cineFadeContinueBlue
        local real duration = bj_cineFadeContinueDuration
        local real DistX
        local real DistY
        local real Dist
        local real LastShakingMagnitude = 0
        local real NewShakingMagnitude
        local real beginGameTime = TimerGetElapsed(udg_GameTime)

        loop
            exitwhen TimerGetElapsed(udg_GameTime)-beginGameTime < 0.20
            set NewShakingMagnitude = -LastShakingMagnitude
            set DistX = GetCameraTargetPositionX(  ) - X
            set DistY = GetCameraTargetPositionY(  ) - Y
            set Dist = SquareRoot( DistX * DistX + DistY * DistY )
            if Dist < fadeStart then
                set NewShakingMagnitude = NewShakingMagnitude + magnitude
            else
                if Dist < fadeEnd then
                    set NewShakingMagnitude = NewShakingMagnitude + magnitude * ( fadeEnd - Dist ) / 200
                endif
            endif
            set LastShakingMagnitude = LastShakingMagnitude + NewShakingMagnitude
            call CameraAddEQNoiseSave( NewShakingMagnitude )
            call TriggerSleepAction( 0.20 )
        endloop
        call CameraAddEQNoiseSave( -LastShakingMagnitude )
    endfunction

    function ShakeCamAtLoc takes real X, real Y, real range, real magnitude, real duration returns nothing
        set bj_meleeNearestMineDist = X
        set bj_cineFadeContinueRed = Y
        set bj_cineFadeContinueGreen = range
        set bj_cineFadeContinueBlue = magnitude
        set bj_cineFadeContinueDuration = duration
        call ExecuteFunc("ShakeCamAtLoc_Child")
    endfunction

    function GetExplosiveDamage takes unit U, boolean onDeath returns real
        local integer i = 0
        local integer ItemType
        local real Damage = 0
        local real DamageFactor = 1
        local real explosiveLevel = GetUnitAbilityLevel(U,EXPLOSIVES_ABILITY_ID)

        if onDeath then
            if (IsPacifista(U)) then
                //This is the level of his inventory ability, it's only below level 6, when he is in 1500 range of the Architect
                // -> when the level is 6 or above, no explosion occurs at all
                if (GetUnitAbilityLevel(U, PACIFISTA_INVENTORY_ID) < 6) then
                    set Damage = PACIFISTA_EXPLOSION_DAMAGE_BASE + (GetUnitAbilityLevel(U, PACIFISTA_INVENTORY_ID) * PACIFISTA_EXPLOSION_DAMAGE_UP)
                else
                    return 0.0
                endif            
            else
                if (explosiveLevel > 0) then
                    set DamageFactor = EXPLOSIVES_FACTOR_BASE + (explosiveLevel * EXPLOSIVES_FACTOR_UP)
                else
                    set DamageFactor = 0
                endif
            endif        
        endif
       
        loop
            exitwhen i > 5
            set ItemType = GetItemTypeId(UnitItemInSlot(U, i))
            if ItemType == 'I01W' then
                set Damage = Damage + 1800
            elseif ItemType == 'I01X' then
                set Damage = Damage + 3600
            elseif ItemType == 'I04D' then
                set Damage = Damage + 5400
            endif
            set i = i + 1
        endloop
       
        return (Damage*DamageFactor)
    endfunction

    function UnitBlowUpExplosives takes unit Exploder, unit Caster, boolean onDeath returns nothing
        local real Damage = GetExplosiveDamage(Exploder, onDeath)
        local player Owner
        local unit dummy
        local real wait
        local real X
        local real Y

        if Damage > 0 then
            set Owner = GetOwningPlayer(Caster)
            set X = GetUnitX(Exploder)
            set Y = GetUnitY(Exploder)
           
            if onDeath then
                set wait = 0.5
            else
                set wait = 0
            endif

            //To make sure, that the AI evades the explosion
            set dummy = CreateUnit(Owner, DUMMY_ID, X, Y, 0)
            call SetUnitState(dummy, UNIT_STATE_LIFE, 499999)
            call UnitApplyTimedLife( dummy, 'BTLF', 1.50 )
            set dummy = null
           
            call GameTimeWait(wait)

            call CreateExplosionSizedTimed(Owner,X,Y,2.4,0.25)
            call CreateExplosionSizedTimed(Owner,X,Y,2.4,0.25)
            call GameTimeWait(wait)
            if not IsUnitInvisible(Exploder, GetOwningPlayer(Exploder)) then
                //call AreaSpellDamageExplosive(Caster,600,Damage,0.25, 0.5)
                call AreaSpellDamageFromUnit( Caster, X, Y, 600, Damage, 0.25, 0.5, FILTER_VALID_TARGET )
            endif
            call DestroyTreesInRange(X,Y,600)
        endif        
    endfunction

    function MakeUnitInvulnerable takes unit U, boolean invul returns nothing
        local integer PlayerId = GetPlayerNr(GetOwningPlayer(U))
       
        if not IsUnitType(U, UNIT_TYPE_HERO) then
            call SetUnitInvulnerable( U, invul )
            return
        endif        

        // For tanks there is a stack for invulnerability, so that
        // call MakeUnitInvulnerable(U,true)
        // call MakeUnitInvulnerable(U,true)
        // call MakeUnitInvulnerable(U,false)
        // // Still is invulnerable
        // call MakeUnitInvulnerable(U,false)
        // // Now is vulnerable again

        if invul then
            set udg_Tank_Invulnerable[PlayerId] = udg_Tank_Invulnerable[PlayerId] + 1
           
            if udg_Tank_Invulnerable[PlayerId] == 1 then
                call SetUnitInvulnerable( U, true )
            endif
        else
            if (udg_Tank_Invulnerable[PlayerId] == 0) then
                call SendBotDataOnline(DEBUG_INFO_DATA, "MakeUnitInvulnerable - " + I2S(udg_Tank_Invulnerable[PlayerId]))
            else
                set udg_Tank_Invulnerable[PlayerId] = udg_Tank_Invulnerable[PlayerId] - 1
            endif
           
            if udg_Tank_Invulnerable[PlayerId] == 0 and udg_Team_Winning == 0 then
                call SetUnitInvulnerable( U, false )
            endif
        endif
    endfunction

    function ResetSplashCannon_Child takes nothing returns nothing
        local integer ResetTimerId = GetHandleIndex(GetExpiredTimer())
        local unit tank = udg_AV_Unit1[ResetTimerId]

        call IssueImmediateOrder(tank, "defend")
       
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(ResetTimerId)
        set tank = null
    endfunction

    function ResetSplashCannon takes unit tank returns nothing
        local integer Id = GetPlayerNr(GetOwningPlayer(tank))
        local timer t
        local integer ResetTimerId
       
        if (GetUnitTypeId(tank) == 'H021') then
            call GameTimeWait(0)
            if SplashIsActive[Id] then
                call IssueImmediateOrder(tank, "undefend")
               
                set t = NewTimer()
                set ResetTimerId = NewTimerIndex(t)
                set udg_AV_Unit1[ResetTimerId] = tank
                call TimerStart(t, 0.25, true, function ResetSplashCannon_Child)
            else
                call IssueImmediateOrder(tank, "undefend")
            endif
        endif
    endfunction
   
    globals
        integer DAMAGE_BLOCK_HP_BONUS_ID = 'A0C9'
    endglobals

    function PostDamageHeal_Child takes nothing returns nothing
        local integer LoopTimerId = GetHandleIndex(GetExpiredTimer())
        local unit U = udg_AV_Unit1[LoopTimerId]
        local real heal = udg_AV_Real1[LoopTimerId]
       
        call SetUnitState(U, UNIT_STATE_LIFE, GetUnitState(U, UNIT_STATE_LIFE) + heal)
       
        if (GetUnitAbilityLevel(U, DAMAGE_BLOCK_HP_BONUS_ID) > 0) then
            call UnitRemoveAbility(U, DAMAGE_BLOCK_HP_BONUS_ID)
        endif
       
        call ReleaseTimer(GetExpiredTimer())
        call ReleaseHandleIndex(LoopTimerId)
       
        set U = null
    endfunction

    function PostDamageHeal takes unit U, real heal returns nothing
        local timer t
        local integer LoopTimerId
       
        set t = NewTimer()
        set LoopTimerId = NewTimerIndex(t)
        set udg_AV_Unit1[LoopTimerId] = U    
        set udg_AV_Real1[LoopTimerId] = heal
        call TimerStart(t, 0, false, function PostDamageHeal_Child)
    endfunction

    //This function combines post- and pre-heal to ensure the triggered heal always heals the desired amount
    //The only case this function is not able to cover, is when the damage is higher than the max HP
    function DynamicDamageBlock takes unit U, real damage returns nothing
        local real curHP = GetUnitState(U, UNIT_STATE_LIFE)
        local real maxHP = GetUnitState(U, UNIT_STATE_MAX_LIFE)
       
        if (curHP > 0) then
            if ((damage > (maxHP - curHP)) and (damage < (maxHP))) then
                if (damage > curHP) then
                    //Pre-damage heal
                    call SetUnitState(U, UNIT_STATE_LIFE, maxHP)
                    call PostDamageHeal(U, damage - (maxHP - curHP))
                else
                    call PostDamageHeal(U, damage)
                endif
            elseif (damage > (maxHP)) then
                call UnitAddAbility(U, DAMAGE_BLOCK_HP_BONUS_ID)
                if (GetUnitState(U, UNIT_STATE_LIFE) < damage) then
                    call DebugMsg("Extra HP und doch zu wenig")
                    call SetUnitState(U, UNIT_STATE_LIFE, GetUnitState(U, UNIT_STATE_LIFE) + damage)
                    call PostDamageHeal(U, 0)
                else
                    call PostDamageHeal(U, damage)
                endif
            else
                //Pre-damage heal
                call SetUnitState(U, UNIT_STATE_LIFE, curHP + damage)
            endif
        endif
    endfunction

    // Attaches fire to the tank or removes it,
    // this is used for tanks which do not have a damaged-animation on their own
    function Tank_Attached_Fire_Change takes integer PlayerId, integer NewFireLevel returns nothing
        local unit Tank = udg_Tank[PlayerId+1]
        local integer CurrentFireLevel = udg_Tank_FireLevel[PlayerId+1]

        if CurrentFireLevel < NewFireLevel then
            loop
                exitwhen CurrentFireLevel >= NewFireLevel
                set CurrentFireLevel = CurrentFireLevel + 1
                if CurrentFireLevel==1 then
                    set udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel] = AddSpecialEffectTarget("Environment\\SmallBuildingFire\\SmallBuildingFire2.mdl", Tank, "hand, left")
                elseif CurrentFireLevel==2 then
                    set udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel] = AddSpecialEffectTarget("Environment\\SmallBuildingFire\\SmallBuildingFire2.mdl", Tank, "hand, right")
                elseif CurrentFireLevel==3 then
                    set udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel] = AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", Tank, "chest")
                elseif CurrentFireLevel==4 then
                    set udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel] = AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", Tank, "head")
                else
                    set udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel] = AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", Tank, "origin")
                endif
            endloop
        elseif CurrentFireLevel > NewFireLevel then
            loop
                exitwhen CurrentFireLevel <= NewFireLevel
                call DestroyEffect(udg_Tank_AttachedFire[(PlayerId*5)+CurrentFireLevel])
                set CurrentFireLevel = CurrentFireLevel - 1
            endloop
        endif
        set udg_Tank_FireLevel[PlayerId+1] = CurrentFireLevel
        set Tank = null
    endfunction
endlibrary
This library contains functions to find unit groups or a specific unit from a group
//TESH.scrollpos=471
//TESH.alwaysfold=0
library Enumerations initializer Init requires BTFramework, UnitTypes
    function GetClosestUnitFromGroup takes group G, real X, real Y returns unit
        local unit tmp = null
        local unit U = null
        local real DistX
        local real DistY
        local real DistXYSq
        local real SmallDistSq = 10000000000.0
       
        loop
            set tmp = FirstOfGroup( G )
            exitwhen tmp == null   
           
            set DistX = X - GetUnitX(tmp)
            set DistY = Y - GetUnitY(tmp)
            set DistXYSq = DistX*DistX+DistY*DistY
           
            if (DistXYSq < SmallDistSq) then
                set U = tmp
                set SmallDistSq = DistXYSq
            endif
            call GroupRemoveUnit( G, tmp )
        endloop
       
        return U
    endfunction

    function GroupEnumLocustsInRange takes group g, real x, real y, real radius, boolexpr filter returns nothing
        local integer i = 0
        local unit u
        if filter == null then
            set filter = FILTER_NULL
        endif
        loop
            exitwhen i > 11
            call GroupEnumUnitsOfPlayer( udg_enumGrp, Player( i ), filter )
            loop
                set u = FirstOfGroup(udg_enumGrp)
                exitwhen u == null
                if IsUnitInRangeXY( u, x, y, radius ) and GetUnitAbilityLevel(u,'Aloc') > 0 then
                    call GroupAddUnit( g, u )
                endif
                call GroupRemoveUnit(udg_enumGrp,u)
            endloop
            set i = i + 1
        endloop
    endfunction

    function GroupEnumUnitsInRangeEx takes group g, real x, real y, real radius, boolexpr filter returns nothing
        local integer i = 0
        local unit u
        if filter == null then
            set filter = FILTER_NULL
        endif
        loop
            exitwhen i > 11
            call GroupEnumUnitsOfPlayer( udg_enumGrp, Player( i ), filter )
            loop
                set u = FirstOfGroup(udg_enumGrp)
                exitwhen u == null
                if IsUnitInRangeXY( u, x, y, radius ) then
                    call GroupAddUnit( g, u )
                endif
                call GroupRemoveUnit(udg_enumGrp,u)
            endloop
            set i = i + 1
        endloop
    endfunction

    function GroupEnumLocustsInRect takes group g, rect r, boolexpr filter returns nothing
        local integer i = 0
        local unit u
        local region re = CreateRegion()
        call RegionAddRect( re, r )
        if filter == null then
            set filter = FILTER_NULL
        endif
        loop
            exitwhen i > 11
            call GroupEnumUnitsOfPlayer( udg_enumGrp, Player( i ), filter )
            loop
                set u = FirstOfGroup(udg_enumGrp)
                exitwhen u == null
                if GetUnitAbilityLevel( u, 'Aloc' ) > 0 and IsUnitInRegion( re, u ) then
                    call GroupAddUnit( g, u )
                endif
                call GroupRemoveUnit(udg_enumGrp,u)
            endloop
            set i = i + 1
        endloop
        call RegionClearRect( re, r )
        call RemoveRegion( re )
        set re = null
    endfunction

    function GroupEnumUnitsInRectEx takes group g, rect r, boolexpr filter returns nothing
        local integer i = 0
        local unit u
        local region re = CreateRegion()
        call RegionAddRect( re, r )
        if filter == null then
            set filter = FILTER_NULL
        endif
        loop
            exitwhen i > 11
            call GroupEnumUnitsOfPlayer( udg_enumGrp, Player( i ), filter )
            loop
                set u = FirstOfGroup(udg_enumGrp)
                exitwhen u == null
                if IsUnitInRegion( re, u ) then
                    call GroupAddUnit( g, u )
                endif
                call GroupRemoveUnit(udg_enumGrp,u)
            endloop
            set i = i + 1
        endloop
        call RegionClearRect( re, r )
        call RemoveRegion( re )
        set re = null
    endfunction

    function GroupEnumNormalUnitsOfPlayer takes group g, player p, boolexpr filter returns nothing
        local unit u
        if filter == null then
            set filter = FILTER_NULL
        endif
        call GroupEnumUnitsOfPlayer( udg_enumGrp, p, filter )
        loop
            set u = FirstOfGroup( udg_enumGrp )
            exitwhen u == null
            if GetUnitAbilityLevel( u, 'Aloc' ) == 0 then
                call GroupAddUnit( g, u )
            endif
            call GroupRemoveUnit( udg_enumGrp, u )
        endloop
    endfunction

    function GroupEnumNormalUnitsOfType takes group g, string unitName, boolexpr filter returns nothing
        local unit u
        if filter == null then
            set filter = FILTER_NULL
        endif
        call GroupEnumUnitsOfType( udg_enumGrp, unitName, filter )
        loop
            set u = FirstOfGroup( udg_enumGrp )
            exitwhen u == null
            if GetUnitAbilityLevel( u, 'Aloc' ) == 0 then
                call GroupAddUnit( g, u )
            endif
            call GroupRemoveUnit( udg_enumGrp, u )
        endloop
    endfunction

// +--------------------------------------------
// | Replacements for unsafe Blizzard.j functions
// +--------------------------------------------
   
// In general, every call to DestroyBoolExpr() can lead to problems,
// so we are trying to avoid any calls to this function.
// As this replacement list is not complete, you should check in the Blizzard.j,
// if a function calls DestroyBoolExpr(), and at least do never use it with a FILTER_XXX constant

    // GetUnitsInRectMatching
    function GetUnitsInRectMatchingSafe takes rect r, boolexpr filter returns group
        local group g = NewGroup()
        call GroupEnumUnitsInRect(g, r, filter)
        return g
    endfunction
   
    // GetUnitsInRectAll
    function GetUnitsInRectAllSafe takes rect r returns group
        local group g = NewGroup()
        call GroupEnumUnitsInRect(g, r, FILTER_NULL)
        return g
    endfunction

    // GetUnitsInRangeOfLocMatching
    function GetUnitsInRangeOfLocMatchingSafe takes real radius, location whichLocation, boolexpr filter returns group
        local group g = NewGroup()
        call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
        return g
    endfunction

    // GetUnitsInRangeOfLocAll
    function GetUnitsInRangeOfLocAllSafe takes real radius, location whichLocation returns group
        local group g = NewGroup()
        call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, FILTER_NULL)
        return g
    endfunction

    // GetUnitsOfPlayerMatching
    function GetUnitsOfPlayerMatchingSafe takes player whichPlayer, boolexpr filter returns group
        local group g = NewGroup()
        call GroupEnumUnitsOfPlayer(g, whichPlayer, filter)
        return g
    endfunction

    // GetUnitsOfPlayerAll
    function GetUnitsOfPlayerAllSafe takes player whichPlayer returns group
        local group g = NewGroup()
        call GroupEnumUnitsOfPlayer(g, whichPlayer, FILTER_NULL)
        return g
    endfunction

    // GetPlayersMatching
    function GetPlayersMatchingSafe takes boolexpr filter returns force
        local force f = CreateForce()
        call ForceEnumPlayers(f, filter)
        return f
    endfunction


   
// +--------------------------------------------
// | Filter Functions
// +--+-----------------------------------------  
//    | General units functions
//    +-----------------------------------------

    function Filter_AntiLeak takes nothing returns boolean
        return true
    endfunction

    function Filter_IsAlive takes nothing returns boolean
        return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.0
    endfunction
   
    function Filter_IsValidTarget takes nothing returns boolean
        return Filter_IsAlive() and (GetUnitAbilityLevel(GetFilterUnit(), 'Avul') == 0) and (not IsDummy(GetFilterUnit())) and (not IsWard(GetFilterUnit()))
    endfunction
   
    function Filter_IsEnemy takes nothing returns boolean
        return IsUnitEnemy(GetFilterUnit(), udg_Filter_Player )
    endfunction
   
    function Filter_IsAlly takes nothing returns boolean
        return IsUnitAlly(GetFilterUnit(), udg_Filter_Player )
    endfunction

    function Filter_IsAnyTank takes nothing returns boolean
        return Filter_IsValidTarget() and IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO)
    endfunction
   
    function Filter_AllyTank takes nothing returns boolean
        return Filter_IsAnyTank() and Filter_IsAlly()
    endfunction
   
    function Filter_EnemyTank takes nothing returns boolean
        return Filter_IsAnyTank() and Filter_IsEnemy()
    endfunction
   
    function Filter_IsAnyStructure takes nothing returns boolean
        return IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
    endfunction    
       
    function Filter_IsAnyCreep takes nothing returns boolean
        return Filter_IsValidTarget() and (not Filter_IsAnyTank()) and (not Filter_IsAnyStructure())
    endfunction
   
    function Filter_EnemyCreeps takes nothing returns boolean
        return Filter_IsAnyCreep() and Filter_IsEnemy()
    endfunction
   
    function Filter_AllyCreeps takes nothing returns boolean
        return Filter_IsAnyCreep() and Filter_IsAlly()
    endfunction

    function Filter_EnemyUnits takes nothing returns boolean
        return Filter_IsValidTarget() and Filter_IsEnemy()
    endfunction
   
    function Filter_AllyUnits takes nothing returns boolean
        return Filter_IsValidTarget() and Filter_IsAlly()
    endfunction
   
    function Filter_VisibleEnemyUnits takes nothing returns boolean
        return IsUnitVisible(GetFilterUnit(), udg_Filter_Player) and Filter_EnemyUnits()
    endfunction  
   
    function Filter_IsAnyTower takes nothing returns boolean
        return Filter_IsAnyStructure() and IsUnitType(GetFilterUnit(), UNIT_TYPE_RANGED_ATTACKER)
    endfunction    
   
    function Filter_Enemy_NonStructure takes nothing returns boolean
        return (not Filter_IsAnyStructure()) and Filter_EnemyUnits()
    endfunction  
   
    function Filter_IsAirUnit takes nothing returns boolean
        return IsAirUnit(GetFilterUnit())
    endfunction
   
    function Filter_IsGroundUnit takes nothing returns boolean
        return IsGroundUnit(GetFilterUnit())
    endfunction
   
    function Filter_IsEnemyGroundUnit takes nothing returns boolean
        return Filter_IsGroundUnit() and Filter_EnemyUnits()
    endfunction
   
    function Filter_IsAllyGroundUnit takes nothing returns boolean
        return Filter_IsGroundUnit() and Filter_AllyUnits()
    endfunction  
   
    function Filter_IsEnemyAirUnit takes nothing returns boolean
        return Filter_IsAirUnit() and Filter_EnemyUnits()
    endfunction
   
    function Filter_IsAllyAirUnit takes nothing returns boolean
        return Filter_IsAirUnit() and Filter_AllyUnits()
    endfunction  
   
    function Filter_IsAirTank takes nothing returns boolean
        return Filter_IsAirUnit() and Filter_IsAnyTank()
    endfunction
   
    function Filter_IsGroundTank takes nothing returns boolean
        return Filter_IsGroundUnit() and Filter_IsAnyTank()
    endfunction    
   
    function Filter_IsEnemyAirTank takes nothing returns boolean
        return Filter_IsAirTank() and Filter_EnemyTank()
    endfunction
   
    function Filter_IsAllyAirTank takes nothing returns boolean
        return Filter_IsAirTank() and Filter_AllyTank()
    endfunction    
   
    function Filter_IsEnemyGroundTank takes nothing returns boolean
        return Filter_IsGroundTank() and Filter_EnemyTank()
    endfunction
   
    function Filter_IsAllyGroundTank takes nothing returns boolean
        return Filter_IsGroundTank() and Filter_AllyTank()
    endfunction
   
    function Filter_IsEnemyTankOrBuilding takes nothing returns boolean
        return Filter_EnemyUnits() and (Filter_IsAnyStructure() or Filter_IsAnyTank())
    endfunction    
   
//   Specific units functions    

    private function Filter_IsBeacon takes nothing returns boolean
        return Filter_IsValidTarget() and (GetUnitTypeId(GetFilterUnit()) == 'o003')
    endfunction

    private function Filter_IsHyperSpaceBreaker takes nothing returns boolean
        return Filter_IsAlive() and Filter_IsEnemy() and (GetUnitTypeId(GetFilterUnit()) == 'o002')
    endfunction

    // This is the check that is used for cp-capturing, handle with care
    private function Filter_IsTowerOrHero takes nothing returns boolean
        local integer Id = GetUnitTypeId(GetFilterUnit())

        // isn't using the 'IsAnyHero' check, because here, invulnerable targets are also allowed
        return Filter_IsAlive() and (IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) or IsForceTower(GetFilterUnit()))
    endfunction
   
    private function Filter_IsEnemyTowerOrHero takes nothing returns boolean
        return Filter_IsTowerOrHero() and Filter_IsEnemy()
    endfunction

    private function Filter_IsTowerRuin takes nothing returns boolean
        return Filter_IsAlive() and (GetUnitTypeId(GetFilterUnit())=='h00Q')
    endfunction
   
    private function Filter_IsCP takes nothing returns boolean
        return (GetUnitTypeId(GetFilterUnit()) == 'h00P')
    endfunction
   
    private function Filter_IsCPOrPlayerFactory takes nothing returns boolean
        local unit U = GetFilterUnit()
        local integer Id = GetUnitTypeId(U)
        if Filter_IsCP() then
            set U = null
            return true
        elseif GetUnitState(U, UNIT_STATE_LIFE) <= 0 or (Id != 'h002' and Id != 'h015') then
            set U = null
            return false
        endif
        set Id = GetPlayerNr(GetOwningPlayer(U))
        set U = null
        return Id <= GetMaxHumanPlayers()
    endfunction

    private function Filter_IsRootedGuard takes nothing returns boolean
        return Filter_IsAlive() and ((GetUnitTypeId(GetFilterUnit()) == 'H013') and (GetUnitAbilityLevel(GetFilterUnit(),'A0CH') >= 1))
    endfunction

    private function Filter_IsEnemyFactory takes nothing returns boolean
        local integer Id = GetUnitTypeId(GetFilterUnit())
        //Check if the unit is a factory or TCC
        return Filter_IsEnemy() and ((Id=='h01Z') or (Id=='h015') or (Id=='h01K'))
    endfunction
   
    private function Filter_IsMine takes nothing returns boolean
        return IsMine( GetFilterUnit() ) and (GetUnitState( GetFilterUnit() , UNIT_STATE_LIFE ) > 0)
    endfunction
   
    private function Filter_IsCPTPGlow takes nothing returns boolean
        return (GetUnitTypeId(GetFilterUnit()) == 'h01X')
    endfunction

    private function Filter_IsValidCPTPTarget takes nothing returns boolean
        return IsUnitType(GetFilterUnit(), UNIT_TYPE_TOWNHALL)==true and Filter_IsAlive() and Filter_IsAlly()
    endfunction

    private function Filter_IsValidCPTPSource takes nothing returns boolean
        // It is not allowed to port from a beacon
        return Filter_IsValidCPTPTarget() and not Filter_IsBeacon()
    endfunction

    private function Filter_IsDecoy takes nothing returns boolean
        return IsDecoy(GetFilterUnit())
    endfunction
   
    private function Filter_IsKineticShield takes nothing returns boolean
        return (GetUnitTypeId(GetFilterUnit()) == KINETIC_SHIELD_ID)
    endfunction

   
    globals
        // This varialble is used to set from which "point of view" we want to enumerate the units,
        // required for ALLIED or ENEMY filters.
        player   udg_Filter_Player
       
        boolexpr FILTER_NULL
        boolexpr FILTER_ALIVE
        boolexpr FILTER_VALID_TARGET
        boolexpr FILTER_ALLY                        // Requires udg_Filter_Player        
        boolexpr FILTER_ENEMY                       // Requires udg_Filter_Player
        boolexpr FILTER_ANY_TANK
        boolexpr FILTER_ALLY_TANK                   // Requires udg_Filter_Player
        boolexpr FILTER_ENEMY_TANK                  // Requires udg_Filter_Player
        boolexpr FILTER_ANY_STRUCTURE        
        boolexpr FILTER_ANY_CREEP
        boolexpr FILTER_ALLY_CREEP                  // Requires udg_Filter_Player        
        boolexpr FILTER_ENEMY_CREEP                 // Requires udg_Filter_Player
        boolexpr FILTER_ALLY_UNITS                  // Requires udg_Filter_Player
        boolexpr FILTER_ENEMY_UNITS                 // Requires udg_Filter_Player
        boolexpr FILTER_VISIBLE_ENEMY_UNITS         // Requires udg_Filter_Player
        boolexpr FILTER_ENEMY_NONSTRUCTURE          // Requires udg_Filter_Player
        boolexpr FILTER_AIR_UNIT        
        boolexpr FILTER_GROUND_UNIT        
        boolexpr FILTER_ALLY_GROUND_UNIT            // Requires udg_Filter_Player    
        boolexpr FILTER_ENEMY_GROUND_UNIT           // Requires udg_Filter_Player
        boolexpr FILTER_ALLY_AIR_UNIT               // Requires udg_Filter_Player    
        boolexpr FILTER_ENEMY_AIR_UNIT              // Requires udg_Filter_Player        
        boolexpr FILTER_AIR_TANK
        boolexpr FILTER_GROUND_TANK    
        boolexpr FILTER_ENEMY_AIR_TANK              // Requires udg_Filter_Player
        boolexpr FILTER_ALLY_AIR_TANK               // Requires udg_Filter_Player
        boolexpr FILTER_ENEMY_GROUND_TANK           // Requires udg_Filter_Player
        boolexpr FILTER_ALLY_GROUND_TANK            // Requires udg_Filter_Player        
        boolexpr FILTER_ANY_TOWER        
        boolexpr FILTER_BEACON
        boolexpr FILTER_ENEMY_HYPERSPACE_BREAKER    // Requires udg_Filter_Player
        boolexpr FILTER_TOWER_OR_HERO
        boolexpr FILTER_ENEMY_TOWER_OR_HERO         // Requires udg_Filter_Player
        boolexpr FILTER_ENEMY_BUILDING_OR_TANK      // Requires udg_Filter_Player
        boolexpr FILTER_TOWER_RUIN
        boolexpr FILTER_CP
        boolexpr FILTER_CP_OR_PLAYER_FACTORY
        boolexpr FILTER_ROOTED_GUARD
        boolexpr FILTER_ENEMY_FACTORY               // Requires udg_Filter_Player
        boolexpr FILTER_MINE
        boolexpr FILTER_CPTPGLOW
        boolexpr FILTER_VALID_CPTP_TARGET           // Requires udg_Filter_Player
        boolexpr FILTER_VALID_CPTP_SOURCE           // Requires udg_Filter_Player
        boolexpr FILTER_AOE_EXPLOSIVE_TARGET
        boolexpr FILTER_DECOY
        boolexpr FILTER_KINETIC_SHIELD
    endglobals
   
    private function Init takes nothing returns nothing
        // This is a function that should be called at the very beginning.
        set FILTER_NULL                     = Filter( function Filter_AntiLeak )
        set FILTER_ALIVE                    = Filter( function Filter_IsAlive )
        set FILTER_VALID_TARGET             = Filter( function Filter_IsValidTarget )
        set FILTER_ALLY                     = Filter( function Filter_IsAlly )
        set FILTER_ENEMY                    = Filter( function Filter_IsEnemy )
        set FILTER_ANY_TANK                 = Filter( function Filter_IsAnyTank )
        set FILTER_ALLY_TANK                = Filter( function Filter_AllyTank )
        set FILTER_ENEMY_TANK               = Filter( function Filter_EnemyTank )        
        set FILTER_ANY_CREEP                = Filter( function Filter_IsAnyCreep )
        set FILTER_ENEMY_CREEP              = Filter( function Filter_EnemyCreeps )
        set FILTER_ALLY_CREEP               = Filter( function Filter_AllyCreeps )        
        set FILTER_ALLY_UNITS               = Filter( function Filter_AllyUnits )
        set FILTER_ENEMY_UNITS              = Filter( function Filter_EnemyUnits )
        set FILTER_VISIBLE_ENEMY_UNITS      = Filter( function Filter_VisibleEnemyUnits )
        set FILTER_ENEMY_NONSTRUCTURE       = Filter( function Filter_Enemy_NonStructure )
        set FILTER_AIR_UNIT                 = Filter( function Filter_IsAirUnit  )
        set FILTER_GROUND_UNIT              = Filter( function Filter_IsGroundUnit  )
        set FILTER_AIR_TANK                 = Filter( function Filter_IsAirTank  )
        set FILTER_GROUND_TANK              = Filter( function Filter_IsGroundTank  )
        set FILTER_ALLY_GROUND_UNIT         = Filter( function Filter_IsAllyGroundUnit )
        set FILTER_ENEMY_GROUND_UNIT        = Filter( function Filter_IsEnemyGroundUnit )
        set FILTER_ALLY_AIR_UNIT            = Filter( function Filter_IsAllyAirUnit )
        set FILTER_ENEMY_AIR_UNIT           = Filter( function Filter_IsEnemyAirUnit )
        set FILTER_ENEMY_AIR_TANK           = Filter( function Filter_IsEnemyAirTank  )
        set FILTER_ALLY_AIR_TANK            = Filter( function Filter_IsAllyAirTank  )
        set FILTER_ENEMY_GROUND_TANK        = Filter( function Filter_IsEnemyGroundTank  )
        set FILTER_ALLY_GROUND_TANK         = Filter( function Filter_IsAllyGroundTank  )
        set FILTER_BEACON                   = Filter( function Filter_IsBeacon )
        set FILTER_ENEMY_HYPERSPACE_BREAKER = Filter( function Filter_IsHyperSpaceBreaker )
        set FILTER_TOWER_OR_HERO            = Filter( function Filter_IsTowerOrHero )
        set FILTER_ENEMY_TOWER_OR_HERO      = Filter( function Filter_IsEnemyTowerOrHero )
        set FILTER_ENEMY_BUILDING_OR_TANK   = Filter( function Filter_IsEnemyTankOrBuilding )
        set FILTER_TOWER_RUIN               = Filter( function Filter_IsTowerRuin )
        set FILTER_ROOTED_GUARD             = Filter( function Filter_IsRootedGuard )
        set FILTER_CP                       = Filter( function Filter_IsCP )
        set FILTER_CP_OR_PLAYER_FACTORY     = Filter( function Filter_IsCPOrPlayerFactory )
        set FILTER_ANY_TOWER                = Filter( function Filter_IsAnyTower )
        set FILTER_ENEMY_FACTORY            = Filter( function Filter_IsEnemyFactory )
        set FILTER_MINE                     = Filter( function Filter_IsMine )
        set FILTER_CPTPGLOW                 = Filter( function Filter_IsCPTPGlow )
        set FILTER_VALID_CPTP_TARGET        = Filter( function Filter_IsValidCPTPTarget )
        set FILTER_VALID_CPTP_SOURCE        = Filter( function Filter_IsValidCPTPSource )
        set FILTER_DECOY                    = Filter( function Filter_IsDecoy )
        set FILTER_KINETIC_SHIELD           = Filter( function Filter_IsKineticShield )
    endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ProjectileSystem requires BTFramework, TimerAndAttachments
globals
    constant real   PROJECTILE_MOVE_INTERVAL = 0.03
endglobals

function interface ProjLooperFunction takes ProjectileMover pr returns boolean
function interface ProjDestructorFunction takes ProjectileMover pr returns nothing

struct ProjectileMover
    real TargetX
    real TargetY
    real TargetZ
    real ProjectileZ
    unit Source
    unit Target
    unit Projectile
    boolean FollowTarget
   
    real DistanceToTarget
    real Speed
    real Arc
    integer TimerIndex
   
    ProjDestructorFunction ProjDestructor
    ProjLooperFunction ProjLooper
   
    static method Loop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tindex = GetHandleIndex(t)
        local ProjectileMover pr = udg_AV_Int1[tindex]
        local real DistanceLeft
        local real DistX
        local real DistY
        local real TotalDistance
        local real LastHeight = pr.ProjectileZ
        local real AccelerationZ
        local real VelocityZ
        local boolean end
       
        if (pr.Target != null) then
            set pr.TargetX = GetUnitX(pr.Target)
            set pr.TargetY = GetUnitY(pr.Target)
            set pr.TargetZ = GetUnitZ(pr.Target)
        endif
       
        set DistX = pr.TargetX - GetUnitX(pr.Projectile)
        set DistY = pr.TargetY - GetUnitY(pr.Projectile)
        set TotalDistance = SquareRoot(Pow(pr.TargetX - GetUnitX(pr.Source), 2) + Pow(pr.TargetY - GetUnitY(pr.Source), 2))
        set DistanceLeft = SquareRoot(DistX * DistX + DistY * DistY)
        set pr.DistanceToTarget = DistanceLeft
       
        set end = pr.ProjLooper.evaluate(pr)
   
        if end then
            call pr.ProjDestructor.evaluate(pr)
            call ReleaseTimer(GetExpiredTimer())
            call ReleaseHandleIndex(tindex)
           
            set pr.Target = null
            set pr.Projectile = null
            set pr.Source = null
           
            call pr.destroy()
        else
            set AccelerationZ = 8*pr.Arc*pr.Speed*pr.Speed/TotalDistance
            set VelocityZ = AccelerationZ * (DistanceLeft/pr.Speed)/2 + pr.Speed * (pr.TargetZ-RMaxBJ(LastHeight,50))/DistanceLeft
           
            set VelocityZ = VelocityZ + AccelerationZ                    
           
            call SetUnitX(pr.Projectile, GetUnitX(pr.Projectile) + pr.Speed * DistX / DistanceLeft)
            call SetUnitY(pr.Projectile, GetUnitY(pr.Projectile) + pr.Speed * DistY / DistanceLeft)
           
            call SetUnitFlyHeight(pr.Projectile, RMaxBJ(GetUnitFlyHeight(pr.Projectile)+LastHeight,50) + VelocityZ - GetUnitZ(pr.Projectile), 0)                        
           
            // the stop command makes sure, that the projectile does not turn around itself (which would happen from time to time)
            call IssueImmediateOrder(pr.Projectile, "stop")

            call SetUnitAnimationByIndex(pr.Projectile, R2I(bj_RADTODEG * Atan2(VelocityZ, pr.Speed) + 0.5) + 90)
            set pr.ProjectileZ = R2I(GetUnitZ(pr.Projectile))
        endif
    endmethod
   
    method Initialize takes real Speed, real Arc, ProjLooperFunction Looper, ProjDestructorFunction Destructor returns nothing
        set this.Speed = Speed * PROJECTILE_MOVE_INTERVAL      
        set this.Arc = Arc
   
        set this.ProjLooper = Looper
        set this.ProjDestructor = Destructor
    endmethod
   
    static method Create takes unit Source, unit Projectile, unit Target, real TargetX, real TargetY returns ProjectileMover
        local ProjectileMover pr = ProjectileMover.allocate()
        local timer t
        local integer tindex

        set pr.Source = Source
        set pr.Projectile = Projectile
       
        set t = NewTimer()
        set pr.TimerIndex = NewTimerIndex(t)
        set udg_AV_Int1[pr.TimerIndex] = pr
        set pr.ProjectileZ = R2I(GetUnitZ(Projectile))

        if (Target == null) then
            set pr.FollowTarget = false
            set pr.TargetX = TargetX
            set pr.TargetY = TargetY
            set pr.TargetZ = 0
        else
            set pr.FollowTarget = true
            set pr.Target = Target
            set pr.TargetZ = R2I(GetUnitZ(Target))
        endif
       
        call TimerStart(t , PROJECTILE_MOVE_INTERVAL , true , function ProjectileMover.Loop)
       
        return pr
    endmethod
endstruct
endlibrary
This library contains functions used for teleportation.
These include checks and teleportation itself.
//TESH.scrollpos=87
//TESH.alwaysfold=0
library Teleports requires TimerAndAttachments, MapAttachedSettings
   
    function RectDistToCoords takes rect r, real X, real Y returns real
        local real dx1 = GetRectMinX(r)-X
        local real dx2 = X-GetRectMaxX(r)
        local real dy1 = GetRectMinY(r)-Y
        local real dy2 = Y-GetRectMaxY(r)
       
        if dx2>dx1 then
            set dx1=dx2
        endif
        if dy2>dy1 then
            set dy1=dy2
        endif
        if dx1<0.0 then
            if dy1<0.0 then
                return 0.0
            else
                return dy1
            endif
        elseif dy1<0.0 then
            return dx1
        endif
        return SquareRoot(dx1*dx1+dy1*dy1)
    endfunction
   
    function RectMoveLocationInside takes rect r, location loc, real tolerance returns nothing
        // tolerance describes the maximum distance to each border.
        // if tolerance is negative, the location will always be inside the rect.
        local real x = GetLocationX(loc)
        local real y = GetLocationY(loc)
       
        if x<GetRectMinX(r)-tolerance then
            set x = GetRectMinX(r)-tolerance
        elseif x>GetRectMaxX(r)+tolerance then
            set x = GetRectMaxX(r)+tolerance
        endif
        if y<GetRectMinY(r)-tolerance then
            set y = GetRectMinY(r)-tolerance
        elseif y>GetRectMaxY(r)+tolerance then
            set y = GetRectMaxY(r)+tolerance
        endif
        call MoveLocation(loc,x,y)
    endfunction
   
    function Get_CP_Teleport_Msg_DistToBase takes real X, real Y, integer team returns real
        if team==1 then
            return RectDistToCoords(gg_rct_Team_1_Base_Main, X, Y)
        endif
        return RectDistToCoords(gg_rct_Team_2_Base_Main, X, Y)
    endfunction
   
    function Get_CP_Teleport_Msg_MoveLocInBase takes location loc, integer team, real tolerance returns nothing
        if team==1 then
            call RectMoveLocationInside(gg_rct_Team_1_Base_Main, loc, tolerance)
        else
            call RectMoveLocationInside(gg_rct_Team_2_Base_Main, loc, tolerance)
        endif
    endfunction
   
    function Get_CP_Teleport_Msg_CoordsInBase takes real X, real Y, unit Tank returns boolean
        if udg_Player_Team[GetPlayerNr(GetOwningPlayer(Tank))] == 1 then
            return RectContainsCoords(gg_rct_Team_1_Base_Main, X, Y)
        endif
        return RectContainsCoords(gg_rct_Team_2_Base_Main, X, Y)
    endfunction

    // Used by the CP-Teleport, lets you choose a target even out of the normal teleport range
    // and moves the location you teleport to, into the range of the CP
    function MoveTargetLocInRange takes unit Tank, location targetLoc returns nothing
        local group G = NewGroup()
        local unit U
        local real X = GetLocationX(targetLoc)
        local real Y = GetLocationY(targetLoc)
        local real DistX
        local real DistY
        local real DistXY
       
        set tempControlPoint = null
        set udg_Filter_Player = GetOwningPlayer(Tank)
        call GroupEnumUnitsInRange(G, X, Y, 2500, FILTER_VALID_CPTP_TARGET)

        set U = GetClosestUnitFromGroup(G, X, Y)

        if U != null and not Get_CP_Teleport_Msg_CoordsInBase( X, Y, Tank) then
            set DistX = X - GetUnitX(U)
            set DistY = Y - GetUnitY(U)
            set DistXY = SquareRoot(DistX*DistX+DistY*DistY)
           
            if (DistXY > 450) then
                call MoveLocation(targetLoc, GetUnitX(U)+DistX*400/DistXY, GetUnitY(U)+DistY*400/DistXY)
            endif
        endif
       
        call ReleaseGroup(G)
        set G = null
        set tempControlPoint = U
        set U = null
    endfunction

    //Returns true, when the target is a valid CPTP target and there is a Teleport Beacon nearby
    function IsFreeTeleport takes unit Tank, location target returns boolean
        local group g = NewGroup()
        local boolean FreeTeleport = false
        local unit CP
       
        set udg_Filter_Player = GetOwningPlayer(Tank)
        call GroupEnumUnitsInRange(g, GetLocationX(target), GetLocationY(target), 500, FILTER_VALID_CPTP_SOURCE)
        set CP = FirstOfGroup(g)
       
        if (CP != null) then
            call GroupEnumUnitsInRange(g, GetUnitX(CP), GetUnitY(CP), 500, FILTER_BEACON)
            set FreeTeleport = (FirstOfGroup(g) != null)
        else
            call GroupEnumUnitsInRange(g, GetLocationX(target), GetLocationY(target), 500, FILTER_BEACON)
            set CP = FirstOfGroup(g)
           
            if (CP != null) then
                call GroupEnumUnitsInRange(g, GetUnitX(CP), GetUnitY(CP), 500, FILTER_VALID_CPTP_SOURCE)
                set FreeTeleport = (FirstOfGroup(g) != null)
            endif
        endif
       
        call ReleaseGroup(g)
        set g = null
        set CP = null
        return FreeTeleport
    endfunction

    function Get_CP_Teleport_Msg takes unit Tank, real X, real Y returns string
        local string Error = null
        local group G = NewGroup()
       
        set udg_Filter_Player = GetOwningPlayer(Tank)
        call GroupEnumUnitsInRange(G, GetUnitX(Tank), GetUnitY(Tank), 500, FILTER_VALID_CPTP_SOURCE)
        if FirstOfGroup(G) == null and not Get_CP_Teleport_Msg_CoordsInBase( GetUnitX(Tank), GetUnitY(Tank), Tank ) then
            set Error = "|cfffed312You have to be next to a Control Point.|r"
        else
            call GroupClear( G )
            call GroupEnumUnitsInRange(G, X, Y, 450, FILTER_VALID_CPTP_TARGET)
            if FirstOfGroup(G) == null and not Get_CP_Teleport_Msg_CoordsInBase( X, Y, Tank) then
                set Error = "|cfffed312The target has to be next to a Control Point.|r"
            else
                call GroupClear( G )
            endif
        endif
        call ReleaseGroup(G)
        set G = null
        return Error
    endfunction

    function CheckHyperSpaceBreaker takes unit Tank returns nothing
        // Should be called after every teleport
        local unit dummy
        local unit Breaker
        local real X
        local real Y
        local group G = NewGroup()
       
        // SleepAction is important: give the unit time to actually teleport to the target location and the ability to go into cooldown
        //call TriggerSleepAction(0)
       
        set udg_Filter_Player = GetOwningPlayer(Tank)
        call GroupEnumUnitsInRange(G, GetUnitX(Tank),GetUnitY(Tank),1300,FILTER_ENEMY_HYPERSPACE_BREAKER)
        set Breaker = FirstOfGroup( G )
        if Breaker != null then
            set X = GetUnitX(Breaker)
            set Y = GetUnitY(Breaker)
           
            call SetUnitPosition(Tank, X,Y)
            set dummy = CreateUnit(GetOwningPlayer(Breaker), DUMMY_ID, X,Y, 270)
            call UnitAddAbility(dummy, 'A06K')
            call IssueTargetOrder(dummy, "thunderbolt", Tank)
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl", Tank, "origin"))