├── mode_roam_generic.lua ├── mode_side_shop_generic.lua ├── mode_team_roam_generic.lua ├── mode_push_tower_top_generic.lua ├── mode_push_tower_mid_generic.lua ├── mode_push_tower_bot_generic.lua ├── hero_selection.lua ├── team_desires.lua ├── item_purchase_undying.lua ├── item_purchase_shadow_shaman.lua ├── item_purchase_keeper_of_the_light.lua ├── item_purchase_death_prophet.lua ├── item_purchase_venomancer.lua ├── ability_item_usage_keeper_of_the_light.lua ├── ability_item_usage_undying.lua ├── ability_item_usage_venomancer.lua ├── README.md ├── ability_item_usage_death_prophet.lua ├── ability_item_usage_shadow_shaman.lua ├── ability_item_usage_generic.lua ├── inspect.lua └── helper.lua /mode_roam_generic.lua: -------------------------------------------------------------------------------- 1 | function GetDesire() 2 | return 0.0; 3 | end -------------------------------------------------------------------------------- /mode_side_shop_generic.lua: -------------------------------------------------------------------------------- 1 | function GetDesire() 2 | return 0.0; 3 | end -------------------------------------------------------------------------------- /mode_team_roam_generic.lua: -------------------------------------------------------------------------------- 1 | function GetDesire() 2 | return 0.0; 3 | end -------------------------------------------------------------------------------- /mode_push_tower_top_generic.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | local Helper = require(GetScriptDirectory() .. "/helper"); 11 | 12 | function GetDesire() 13 | return Helper.GetPushDesire(GetBot(), LANE_TOP); 14 | end 15 | 16 | function OnStart() 17 | 18 | local npcBot = GetBot(); 19 | npcBot:ActionImmediate_Chat("Pushing Top", false); 20 | 21 | end 22 | 23 | function Think() 24 | Helper.PushThink(GetBot(), LANE_TOP); 25 | end -------------------------------------------------------------------------------- /mode_push_tower_mid_generic.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | function GetDesire() 14 | return Helper.GetPushDesire(GetBot(), LANE_MID); 15 | end 16 | 17 | function OnStart() 18 | 19 | local npcBot = GetBot(); 20 | npcBot:ActionImmediate_Chat("Pushing Mid", false); 21 | 22 | end 23 | 24 | function Think() 25 | Helper.PushThink(GetBot(), LANE_MID); 26 | end -------------------------------------------------------------------------------- /mode_push_tower_bot_generic.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | function GetDesire() 14 | return Helper.GetPushDesire(GetBot(), LANE_BOT); 15 | end 16 | 17 | function OnStart() 18 | 19 | local npcBot = GetBot(); 20 | npcBot:ActionImmediate_Chat("Pushing Bottom", false); 21 | 22 | end 23 | 24 | function Think() 25 | 26 | Helper.PushThink(GetBot(), LANE_BOT); 27 | 28 | end -------------------------------------------------------------------------------- /hero_selection.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------------------------- 2 | 3 | local MyBots = { 4 | "npc_dota_hero_undying", 5 | "npc_dota_hero_keeper_of_the_light", 6 | "npc_dota_hero_death_prophet", 7 | "npc_dota_hero_venomancer", 8 | "npc_dota_hero_shadow_shaman" 9 | }; 10 | 11 | function Think() 12 | local IDs = GetTeamPlayers(GetTeam()); 13 | for i,id in pairs(IDs) do 14 | if IsPlayerBot(id) then 15 | SelectHero(id, MyBots[i]); 16 | end 17 | end 18 | 19 | end 20 | 21 | ---------------------------------------------------------------------------------------------------- 22 | 23 | function UpdateLaneAssignments() 24 | if ( GetTeam() == TEAM_RADIANT ) then 25 | return { 26 | [1] = LANE_TOP, 27 | [2] = LANE_TOP, 28 | [3] = LANE_MID, 29 | [4] = LANE_BOT, 30 | [5] = LANE_BOT, 31 | }; 32 | elseif ( GetTeam() == TEAM_DIRE ) then 33 | return { 34 | [1] = LANE_TOP, 35 | [2] = LANE_TOP, 36 | [3] = LANE_MID, 37 | [4] = LANE_BOT, 38 | [5] = LANE_BOT, 39 | }; 40 | end 41 | end 42 | 43 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /team_desires.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | ---------------------------------------------------------------------------------------------------- 14 | 15 | -- function UpdatePushLaneDesires() 16 | 17 | -- -- { Top Middle Bottom } 18 | 19 | -- end 20 | 21 | ---------------------------------------------------------------------------------------------------- 22 | 23 | -- function UpdateDefendLaneDesires() 24 | -- -- { Top Middle Bottom } 25 | -- return { 0.5, 0.5, 0.5}; 26 | 27 | -- end 28 | 29 | ---------------------------------------------------------------------------------------------------- 30 | 31 | -- function UpdateFarmLaneDesires() 32 | -- { Top Middle Bottom } 33 | -- if (DotaTime() <= 60 * 5) then 34 | -- return { 0, 0, 0 }; 35 | -- end 36 | 37 | -- return { 0.5, 0.5, 0.5 } 38 | 39 | -- end 40 | 41 | ---------------------------------------------------------------------------------------------------- 42 | 43 | -- function UpdateRoamDesire() 44 | -- { Desire Unit } 45 | -- return { 0.5, GetTeamMember( TEAM_RADIANT, 1 ) }; 46 | 47 | -- end 48 | 49 | ---------------------------------------------------------------------------------------------------- 50 | 51 | -- function UpdateRoshanDesire() 52 | 53 | -- return 0; 54 | 55 | -- end 56 | 57 | ---------------------------------------------------------------------------------------------------- 58 | -------------------------------------------------------------------------------- /item_purchase_undying.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | local tableItemsToBuy = { 14 | 15 | "item_stout_shield", 16 | "item_branches", 17 | "item_branches", 18 | "item_tango", 19 | "item_tango", 20 | 21 | "item_boots", 22 | "item_energy_booster", 23 | 24 | "item_vitality_booster", 25 | "item_ring_of_health", 26 | 27 | "item_broadsword", 28 | "item_chainmail", 29 | "item_robe", 30 | 31 | "item_ring_of_regen", 32 | "item_recipe_headdress", 33 | "item_chainmail", 34 | "item_recipe_buckler", 35 | "item_recipe_mekansm", 36 | 37 | "item_platemail", 38 | "item_mystic_staff", 39 | "item_recipe_shivas_guard", 40 | 41 | "item_chainmail", 42 | "item_branches", 43 | "item_recipe_buckler", 44 | "item_recipe_crimson_guard", 45 | 46 | "item_vitality_booster", 47 | "item_reaver", 48 | "item_recipe_heart", 49 | 50 | "item_recipe_guardian_greaves", 51 | 52 | }; 53 | 54 | local abilities = { 55 | "special_bonus_gold_income_15", 56 | "special_bonus_hp_300", 57 | "special_bonus_unique_undying", 58 | "special_bonus_unique_undying_2", 59 | "undying_flesh_golem", 60 | "undying_decay", 61 | "undying_tombstone", 62 | "undying_soul_rip", 63 | } 64 | 65 | ---------------------------------------------------------------------------------------------------- 66 | 67 | function ItemPurchaseThink() 68 | 69 | local npcBot = GetBot(); 70 | local buildTable = tableItemsToBuy; 71 | 72 | Helper.AbilityUpgrade(npcBot, abilities); 73 | Helper.PurchaseBootsAndTP(npcBot); 74 | Helper.PurchaseItems(npcBot, buildTable); 75 | 76 | end 77 | 78 | 79 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /item_purchase_shadow_shaman.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | local tableItemsToBuy = { 14 | 15 | "item_courier", 16 | "item_tango", 17 | "item_tango", 18 | 19 | "item_boots", 20 | "item_energy_booster", 21 | 22 | "item_flying_courier", 23 | 24 | "item_point_booster", 25 | "item_staff_of_wizardry", 26 | "item_ogre_axe", 27 | "item_blade_of_alacrity", 28 | 29 | "item_belt_of_strength", 30 | "item_staff_of_wizardry", 31 | "item_recipe_necronomicon", 32 | "item_recipe_necronomicon", 33 | "item_recipe_necronomicon", 34 | 35 | "item_ring_of_health", 36 | "item_cloak", 37 | "item_ring_of_regen", 38 | 39 | "item_ring_of_regen", 40 | "item_branches", 41 | "item_recipe_headdress", 42 | "item_recipe_pipe", 43 | 44 | "item_boots", 45 | "item_recipe_travel_boots", 46 | 47 | "item_void_stone", 48 | "item_ultimate_orb", 49 | "item_mystic_staff", 50 | }; 51 | 52 | local abilities = { 53 | "special_bonus_hp_150", 54 | "special_bonus_cast_range_100", 55 | "special_bonus_respawn_reduction_30", 56 | "special_bonus_unique_shadow_shaman_2", 57 | "shadow_shaman_mass_serpent_ward", 58 | "shadow_shaman_ether_shock", 59 | "shadow_shaman_voodoo", 60 | "shadow_shaman_shackles", 61 | } 62 | 63 | ---------------------------------------------------------------------------------------------------- 64 | 65 | function ItemPurchaseThink() 66 | 67 | local npcBot = GetBot(); 68 | local buildTable = tableItemsToBuy; 69 | 70 | Helper.AbilityUpgrade(npcBot, abilities); 71 | Helper.PurchaseBootsAndTP(npcBot); 72 | Helper.PurchaseItems(npcBot, buildTable); 73 | 74 | end 75 | 76 | 77 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /item_purchase_keeper_of_the_light.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | local tableItemsToBuy = { 14 | 15 | "item_circlet", 16 | "item_tango", 17 | "item_tango", 18 | "item_mantle", 19 | 20 | "item_boots", 21 | "item_blades_of_attack", 22 | "item_blades_of_attack", 23 | 24 | "item_recipe_null_talisman", 25 | 26 | "item_point_booster", 27 | "item_staff_of_wizardry", 28 | "item_ogre_axe", 29 | "item_blade_of_alacrity", 30 | 31 | "item_belt_of_strength", 32 | "item_staff_of_wizardry", 33 | "item_recipe_necronomicon", 34 | "item_recipe_necronomicon", 35 | "item_recipe_necronomicon", 36 | 37 | "item_staff_of_wizardry", 38 | "item_recipe_dagon", 39 | 40 | "item_recipe_dagon", 41 | "item_recipe_dagon", 42 | "item_recipe_dagon", 43 | "item_recipe_dagon", 44 | 45 | "item_platemail", 46 | "item_mystic_staff", 47 | "item_recipe_shivas_guard", 48 | 49 | "item_boots", 50 | "item_recipe_travel_boots", 51 | 52 | "item_vitality_booster", 53 | "item_reaver", 54 | "item_recipe_heart" 55 | }; 56 | 57 | local abilities = { 58 | "special_bonus_strength_6", 59 | "special_bonus_respawn_reduction_25", 60 | "special_bonus_armor_7", 61 | "special_bonus_unique_keeper_of_the_light", 62 | "keeper_of_the_light_chakra_magic", 63 | "keeper_of_the_light_illuminate", 64 | "keeper_of_the_light_mana_leak", 65 | "keeper_of_the_light_spirit_form", 66 | } 67 | 68 | ---------------------------------------------------------------------------------------------------- 69 | 70 | function ItemPurchaseThink() 71 | 72 | local npcBot = GetBot(); 73 | local buildTable = tableItemsToBuy; 74 | 75 | Helper.AbilityUpgrade(npcBot, abilities); 76 | Helper.PurchaseBootsAndTP(npcBot); 77 | Helper.PurchaseItems(npcBot, buildTable); 78 | 79 | end 80 | 81 | 82 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /item_purchase_death_prophet.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | local tableItemsToBuy = { 14 | 15 | "item_circlet", 16 | "item_tango", 17 | "item_tango", 18 | "item_mantle", 19 | 20 | "item_boots", 21 | "item_blades_of_attack", 22 | "item_blades_of_attack", 23 | 24 | "item_recipe_null_talisman", 25 | 26 | "item_vitality_booster", 27 | "item_energy_booster", 28 | "item_point_booster", 29 | "item_sobi_mask", 30 | "item_ring_of_regen", 31 | "item_recipe_soul_ring", 32 | "item_recipe_bloodstone", 33 | 34 | "item_belt_of_strength", 35 | "item_staff_of_wizardry", 36 | "item_recipe_necronomicon", 37 | "item_recipe_necronomicon", 38 | "item_recipe_necronomicon", 39 | 40 | "item_platemail", 41 | "item_mystic_staff", 42 | "item_recipe_shivas_guard", 43 | 44 | "item_vitality_booster", 45 | "item_reaver", 46 | "item_recipe_heart", 47 | 48 | "item_boots", 49 | "item_recipe_travel_boots", 50 | 51 | "item_vitality_booster", 52 | "item_energy_booster", 53 | "item_point_booster", 54 | "item_mystic_staff", 55 | 56 | }; 57 | 58 | local abilities = { 59 | "special_bonus_spell_amplify_4", 60 | "special_bonus_unique_death_prophet_2", 61 | "special_bonus_cooldown_reduction_10", 62 | "special_bonus_hp_400", 63 | "death_prophet_exorcism", 64 | "death_prophet_carrion_swarm", 65 | "death_prophet_spirit_siphon", 66 | "death_prophet_silence", 67 | } 68 | 69 | ---------------------------------------------------------------------------------------------------- 70 | 71 | function ItemPurchaseThink() 72 | 73 | local npcBot = GetBot(); 74 | local buildTable = tableItemsToBuy; 75 | 76 | Helper.AbilityUpgrade(npcBot, abilities); 77 | Helper.PurchaseBootsAndTP(npcBot); 78 | Helper.PurchaseItems(npcBot, buildTable); 79 | 80 | end 81 | 82 | 83 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /item_purchase_venomancer.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | 13 | local tableItemsToBuy = { 14 | 15 | "item_ring_of_protection", 16 | "item_sobi_mask", 17 | "item_tango", 18 | "item_tango", 19 | 20 | "item_boots", 21 | "item_blades_of_attack", 22 | "item_blades_of_attack", 23 | 24 | "item_belt_of_strength", 25 | "item_staff_of_wizardry", 26 | "item_recipe_necronomicon", 27 | "item_recipe_necronomicon", 28 | "item_recipe_necronomicon", 29 | 30 | "item_point_booster", 31 | "item_staff_of_wizardry", 32 | "item_ogre_axe", 33 | "item_blade_of_alacrity", 34 | 35 | "item_platemail", 36 | "item_mystic_staff", 37 | "item_recipe_shivas_guard", 38 | 39 | "item_shadow_amulet", 40 | "item_claymore", 41 | "item_ultimate_orb", 42 | "item_recipe_silver_edge", 43 | 44 | "item_boots", 45 | "item_recipe_travel_boots", 46 | 47 | "item_quarterstaff", 48 | "item_robe", 49 | "item_sobi_mask", 50 | "item_quarterstaff", 51 | "item_robe", 52 | "item_sobi_mask", 53 | "item_recipe_orchid", 54 | 55 | "item_broadsword", 56 | "item_blades_of_attack", 57 | "item_recipe_lesser_crit", 58 | "item_recipe_bloodthorn", 59 | }; 60 | 61 | local abilities = { 62 | "special_bonus_exp_boost_20", 63 | "special_bonus_cast_range_150", 64 | "special_bonus_magic_resistance_15", 65 | "special_bonus_unique_venomancer", 66 | "venomancer_poison_nova", 67 | "venomancer_plague_ward", 68 | "venomancer_poison_sting", 69 | "venomancer_venomous_gale", 70 | } 71 | 72 | ---------------------------------------------------------------------------------------------------- 73 | 74 | function ItemPurchaseThink() 75 | 76 | local npcBot = GetBot(); 77 | local buildTable = tableItemsToBuy; 78 | 79 | Helper.AbilityUpgrade(npcBot, abilities); 80 | Helper.PurchaseBootsAndTP(npcBot); 81 | Helper.PurchaseItems(npcBot, buildTable); 82 | 83 | end 84 | 85 | 86 | ---------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /ability_item_usage_keeper_of_the_light.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)'); 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | require(GetScriptDirectory().."/ability_item_usage_generic"); 13 | 14 | local Abilities = { 15 | "keeper_of_the_light_illuminate", 16 | "keeper_of_the_light_mana_leak", 17 | "keeper_of_the_light_chakra_magic", 18 | "keeper_of_the_light_recall", 19 | "keeper_of_the_light_blinding_light", 20 | "keeper_of_the_light_spirit_form", 21 | "keeper_of_the_light_illuminate_end", 22 | "keeper_of_the_light_spirit_form_illuminate", 23 | "keeper_of_the_light_spirit_form_illuminate_end", 24 | }; 25 | 26 | function AbilityUsageThink() 27 | 28 | local npcBot = GetBot(); 29 | 30 | local wave = npcBot:GetAbilityInSlot(0); 31 | local leak = npcBot:GetAbilityByName(Abilities[2]); 32 | local mana = npcBot:GetAbilityByName(Abilities[3]); 33 | local ult = npcBot:GetAbilityByName(Abilities[6]); 34 | 35 | local creeps = npcBot:GetNearbyLaneCreeps(1500, true); 36 | local enemyHeroes = npcBot:GetNearbyHeroes(600, true, BOT_MODE_NONE); 37 | 38 | local function considerManaLeak() 39 | if leak:IsFullyCastable() and npcBot:GetMana() - leak:GetManaCost() > mana:GetManaCost() then 40 | local enemyHero = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', leak:GetCastRange(), true); 41 | if enemyHero ~= nil then 42 | return npcBot:ActionPush_UseAbilityOnEntity(leak, enemyHero); 43 | end 44 | end 45 | end 46 | 47 | if npcBot:IsChanneling() or (npcBot:IsUsingAbility() and (not wave:IsInAbilityPhase())) then 48 | return; 49 | end 50 | 51 | if mana:IsFullyCastable() then 52 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetMana', mana:GetCastRange(), false); 53 | return npcBot:ActionPush_UseAbilityOnEntity(mana, target); 54 | end 55 | 56 | if #enemyHeroes >= 2 or npcBot:GetActiveMode() == BOT_MODE_RETREAT then 57 | return considerManaLeak(); 58 | end 59 | 60 | if wave:IsFullyCastable() and 61 | npcBot:GetMana() - wave:GetManaCost() > mana:GetManaCost() and 62 | npcBot:GetActiveMode() ~= BOT_MODE_RETREAT and 63 | #creeps >= 3 then 64 | return npcBot:ActionPush_UseAbilityOnLocation(wave, creeps[1]:GetLocation()); 65 | end 66 | 67 | considerManaLeak(); 68 | 69 | -- if ult:IsFullyCastable() and npcBot:GetMana() - ult:GetManaCost() > waveAndManaCombo then 70 | -- npcBot:ActionPush_UseAbility(ult); 71 | -- end 72 | end 73 | 74 | function ItemUsageThink() 75 | ability_item_usage_generic.ItemUsageThink(); 76 | end 77 | 78 | function BuybackUsageThink() 79 | ability_item_usage_generic.BuybackUsageThink(); 80 | end -------------------------------------------------------------------------------- /ability_item_usage_undying.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)'); 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | require(GetScriptDirectory().."/ability_item_usage_generic"); 13 | 14 | local Abilities = { 15 | "undying_decay", 16 | "undying_tombstone", 17 | "undying_soul_rip", 18 | "undying_flesh_golem", 19 | }; 20 | 21 | function AbilityUsageThink() 22 | 23 | local npcBot = GetBot(); 24 | 25 | local decay = npcBot:GetAbilityByName(Abilities[1]); 26 | local tombstone = npcBot:GetAbilityByName(Abilities[2]); 27 | local heal = npcBot:GetAbilityByName(Abilities[3]); 28 | local ult = npcBot:GetAbilityByName(Abilities[4]); 29 | 30 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 31 | return; 32 | end 33 | 34 | local battleMayHappen = #npcBot:GetNearbyHeroes(1500, true, BOT_MODE_NONE) >= 3 or 35 | ( 36 | #npcBot:GetNearbyHeroes(1500, true, BOT_MODE_NONE) >= 2 and 37 | #npcBot:GetNearbyHeroes(1500, false, BOT_MODE_NONE) >= 2 38 | ); 39 | 40 | if tombstone:IsFullyCastable() and battleMayHappen then 41 | return npcBot:ActionPush_UseAbilityOnLocation(tombstone, npcBot:GetLocation()); 42 | end 43 | 44 | if ult:IsFullyCastable() and battleMayHappen then 45 | return npcBot:ActionPush_UseAbility(ult); 46 | end 47 | 48 | if decay:IsFullyCastable() and 49 | npcBot:GetMana() - decay:GetManaCost() > ult:GetManaCost() + tombstone:GetManaCost() then 50 | local aoe = npcBot:FindAoELocation( 51 | true, true, npcBot:GetLocation(), decay:GetCastRange(), 325, decay:GetCastPoint(), 100000); 52 | if aoe.count >= 2 or (aoe.count >= 1 and npcBot:GetActiveMode() == BOT_MODE_ATTACK) then 53 | return npcBot:ActionPush_UseAbilityOnLocation(decay, aoe.targetloc); 54 | end 55 | end 56 | 57 | if heal:IsFullyCastable() and 58 | npcBot:GetMana() - heal:GetManaCost() > ult:GetManaCost() + tombstone:GetManaCost() + 3 * decay:GetManaCost() then 59 | 60 | local enemy = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', heal:GetCastRange(), true); 61 | local friend = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', heal:GetCastRange(), false); 62 | 63 | local target = enemy or friend; 64 | if enemy ~= nil and friend ~= nil then 65 | target = enemy:GetHealth() < friend:GetHealth() and enemy or friend; 66 | end 67 | 68 | if target ~= nil then 69 | return npcBot:ActionPush_UseAbilityOnEntity(heal, target); 70 | end 71 | end 72 | 73 | end 74 | 75 | function ItemUsageThink() 76 | ability_item_usage_generic.ItemUsageThink(); 77 | end 78 | 79 | function BuybackUsageThink() 80 | ability_item_usage_generic.BuybackUsageThink(); 81 | end -------------------------------------------------------------------------------- /ability_item_usage_venomancer.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)'); 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | require(GetScriptDirectory().."/ability_item_usage_generic"); 13 | 14 | local Abilities = { 15 | "venomancer_venomous_gale", 16 | "venomancer_poison_sting", 17 | "venomancer_plague_ward", 18 | "venomancer_poison_nova", 19 | }; 20 | 21 | function AbilityUsageThink() 22 | 23 | local npcBot = GetBot(); 24 | 25 | local gale = npcBot:GetAbilityByName(Abilities[1]); 26 | local ward = npcBot:GetAbilityByName(Abilities[3]); 27 | local nova = npcBot:GetAbilityByName(Abilities[4]); 28 | 29 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 30 | return; 31 | end 32 | 33 | if gale:IsFullyCastable() and 34 | npcBot:GetMana() - gale:GetManaCost() > nova:GetManaCost() and 35 | npcBot:GetActiveMode() == BOT_MODE_ATTACK then 36 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', gale:GetCastRange(), true); 37 | if target ~= nil then 38 | return npcBot:ActionPush_UseAbilityOnLocation(gale, target:GetLocation()); 39 | end 40 | end 41 | 42 | if ward:IsFullyCastable() and 43 | (#npcBot:GetNearbyHeroes(1500, true, BOT_MODE_NONE) > 0 or 44 | #npcBot:GetNearbyTowers(1500, true) > 0 or 45 | #npcBot:GetNearbyBarracks(1500, true) > 0 or 46 | #npcBot:GetNearbyLaneCreeps(1500, true) > 2 or 47 | npcBot:GetManaRegen() > 4 or 48 | npcBot:GetActiveMode() == BOT_MODE_ATTACK or 49 | npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_BOT or 50 | npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_MID or 51 | npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_TOP or 52 | npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY 53 | ) and 54 | npcBot:GetMana() - ward:GetManaCost() > nova:GetManaCost() and 55 | ward:GetLevel() >= 2 then 56 | local target = npcBot:GetNearbyHeroes(ward:GetCastRange(), true, BOT_MODE_NONE)[1]; 57 | if target ~= nil then 58 | return npcBot:ActionPush_UseAbilityOnLocation(ward, target:GetLocation()); 59 | else 60 | return npcBot:ActionPush_UseAbilityOnLocation(ward, npcBot:GetLocation() + Helper.RandomForwardVector(ward:GetCastRange())); 61 | end 62 | end 63 | 64 | local enemyHeroes = npcBot:GetNearbyHeroes(575, true, BOT_MODE_NONE); 65 | if #enemyHeroes >= 3 or (#enemyHeroes >= 2 and npcBot:GetActiveMode() == BOT_MODE_ATTACK) then 66 | return npcBot:ActionPush_UseAbility(nova); 67 | end 68 | 69 | end 70 | 71 | function ItemUsageThink() 72 | ability_item_usage_generic.ItemUsageThink(); 73 | end 74 | 75 | function BuybackUsageThink() 76 | ability_item_usage_generic.BuybackUsageThink(); 77 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExtremePush - Dota 2 Bot Scripts 2 | 3 | The aim of this bot is for extreme [pushing](http://dota2.gamepedia.com/Pushing). You can download the bot with name "ExtremePush" on workshop. 4 | 5 | **You might have to unsubscribe and resubscribe to update your bot scripts to the latest version.** 6 | 7 | ### v.20170218 8 | 9 | - Added Shadow Shaman 10 | - Added Death Prophet 11 | 12 | ### v.20170211 13 | 14 | - Fix broken bots caused by recent game updates. 15 | 16 | ### v.20170202 17 | 18 | **Venomancer** 19 | 20 | - Added Venomancer (item builds, ability usages, etc) 21 | 22 | **General** 23 | 24 | - Tweak the team push logic 25 | 26 | ### v.20170129 27 | 28 | **Keeper of the Light** 29 | 30 | - Improve push mode overrides with a better strategy 31 | - Fix some TP Scroll related issues 32 | - Improve Travel Boots usage 33 | 34 | ### v.20170128 35 | 36 | **Keeper of the Light** 37 | 38 | - Override default bot push mode, now it uses custom push logic. 39 | - Improve item builds for late game. 40 | - Fix crashes caused by default require statemet on Mac (only tested on dev script, probably will not work on Workshop) 41 | 42 | ### v.20170121 43 | 44 | **Keeper of the Light** 45 | 46 | Huge improvements for KOTL, including: 47 | 48 | - Now bots know how to purchase from secret shop. This results in much better item builds. 49 | One of the KOTL will be named as support and will purchase support items: Mekansm and Pipe. 50 | Other bots will purchase Shadow Blade and Shivas Guard. 51 | - Now bots know how to purcahse and use TP Scroll. The TP logic is based on the **default** team desires. So it's more of a double-edged sword: Good decisions result in better results and bad decisions result in bigger mess. 52 | - Now bots will have custom ability upgrade path and will also be able to upgrade talents. 53 | - Chakra Magic will be casted on teammates also. 54 | - Illuminate will end if the casting hero is in danger. Also it will ignore neutral creeps. 55 | - Mana leak will be casted more aggressively. 56 | 57 | ### v.20170117 58 | 59 | **Keeper of the Light** 60 | 61 | - Chakra Magic: cast when available 62 | - Illuminate: cast and channel when there are creeps within range. Cancel channelling when the hero is "threatened". 63 | - Mana leak: cast on the enenmy hero within the range that has lowest HP. 64 | - Illuminate and Mana leak are casted when there is enough mana left for Chakra Magic. 65 | - Other abilities are not casted at all. I found out when Spirit Form is on, the hero will have weird behaviors. 66 | - Phase Boots and Necronomicon: purchase and cast when available. 67 | - Mekansm and Pipe of Insight: purchase and cast when a nearby teammates has HP <= 400. 68 | - Dagon: purcahse and cast on the enenmy hero within the range that has lowest HP. 69 | 70 | For test runs, please see [Issue #1](https://github.com/insraq/dota2bots/issues/1) 71 | 72 | ![Test Run Keeper of the Light](https://cloud.githubusercontent.com/assets/608221/22066488/1eb64e12-ddc8-11e6-8882-fa93c0c07cd3.png) 73 | -------------------------------------------------------------------------------- /ability_item_usage_death_prophet.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)'); 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | require(GetScriptDirectory().."/ability_item_usage_generic"); 13 | 14 | local Abilities = { 15 | "death_prophet_carrion_swarm", 16 | "death_prophet_silence", 17 | "death_prophet_spirit_siphon", 18 | "death_prophet_exorcism", 19 | }; 20 | 21 | function AbilityUsageThink() 22 | 23 | local npcBot = GetBot(); 24 | 25 | local swarm = npcBot:GetAbilityByName(Abilities[1]); 26 | local silence = npcBot:GetAbilityByName(Abilities[2]); 27 | local siphon = npcBot:GetAbilityByName(Abilities[3]); 28 | local ult = npcBot:GetAbilityByName(Abilities[4]); 29 | 30 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 31 | return; 32 | end 33 | 34 | local battleMayHappen = #npcBot:GetNearbyHeroes(1500, true, BOT_MODE_NONE) >= 3 or 35 | ( 36 | #npcBot:GetNearbyHeroes(1500, true, BOT_MODE_NONE) >= 2 and 37 | #npcBot:GetNearbyHeroes(1500, false, BOT_MODE_NONE) >= 2 38 | ); 39 | local isPushing = ( 40 | #npcBot:GetNearbyTowers(700, true) > 0 or #npcBot:GetNearbyBarracks(700, true) > 0 41 | ) and #npcBot:GetNearbyHeroes(700, false, BOT_MODE_NONE) >= 2; 42 | 43 | if ult:IsFullyCastable() and (battleMayHappen or isPushing) then 44 | return npcBot:ActionPush_UseAbility(ult); 45 | end 46 | 47 | if swarm:IsFullyCastable() and 48 | npcBot:GetMana() - swarm:GetManaCost() > ult:GetManaCost() and 49 | swarm:GetLevel() >= 2 then 50 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', swarm:GetCastRange(), true); 51 | 52 | if target == nil and 53 | npcBot:GetMana() > swarm:GetManaCost() + silence:GetManaCost() + siphon:GetManaCost() + ult:GetManaCost() then 54 | target = npcBot:GetNearbyLaneCreeps(swarm:GetCastRange(), true)[1]; 55 | end 56 | 57 | if target ~= nil then 58 | return npcBot:ActionPush_UseAbilityOnEntity(swarm, target); 59 | end 60 | end 61 | 62 | if silence:IsFullyCastable() then 63 | local aoe = npcBot:FindAoELocation(true, true, npcBot:GetLocation(), silence:GetCastRange(), 425, 0.0, 100000); 64 | if aoe.count >= 2 then 65 | return npcBot:ActionPush_UseAbilityOnLocation(silence, aoe.targetloc); 66 | end 67 | end 68 | 69 | if siphon:IsFullyCastable() and swarm:GetLevel() >= 3 and npcBot:GetActiveMode() ~= BOT_MODE_RETREAT then 70 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', siphon:GetCastRange(), true); 71 | if target ~= nil then 72 | npcBot:ActionQueue_MoveToUnit(target); 73 | return npcBot:ActionPush_UseAbilityOnEntity(siphon, target); 74 | end 75 | end 76 | 77 | end 78 | 79 | function ItemUsageThink() 80 | ability_item_usage_generic.ItemUsageThink(); 81 | end 82 | 83 | function BuybackUsageThink() 84 | ability_item_usage_generic.BuybackUsageThink(); 85 | end -------------------------------------------------------------------------------- /ability_item_usage_shadow_shaman.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)'); 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local Helper = require(GetScriptDirectory() .. "/helper"); 12 | require(GetScriptDirectory().."/ability_item_usage_generic"); 13 | 14 | local Abilities = { 15 | "shadow_shaman_ether_shock", 16 | "shadow_shaman_voodoo", 17 | "shadow_shaman_shackles", 18 | "shadow_shaman_mass_serpent_ward", 19 | }; 20 | 21 | function AbilityUsageThink() 22 | 23 | local npcBot = GetBot(); 24 | 25 | local shock = npcBot:GetAbilityByName(Abilities[1]); 26 | local hex = npcBot:GetAbilityByName(Abilities[2]); 27 | local shackles = npcBot:GetAbilityByName(Abilities[3]); 28 | local ult = npcBot:GetAbilityByName(Abilities[4]); 29 | 30 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 31 | return; 32 | end 33 | 34 | local ultTarget = nil; 35 | 36 | local enemyHeroes = npcBot:GetNearbyHeroes(ult:GetCastRange(), true, BOT_MODE_NONE); 37 | if #enemyHeroes >= 2 then 38 | ultTarget = enemyHeroes[1]:GetLocation(); 39 | end 40 | 41 | local towers = npcBot:GetNearbyTowers(ult:GetCastRange(), true); 42 | if #towers > 0 then 43 | ultTarget = towers[1]:GetLocation(); 44 | end 45 | 46 | local barracks = npcBot:GetNearbyBarracks(ult:GetCastRange(), true); 47 | if #barracks > 0 then 48 | ultTarget = barracks[1]:GetLocation(); 49 | end 50 | 51 | if ultTarget ~= nil and ult:IsFullyCastable() then 52 | return npcBot:ActionPush_UseAbilityOnLocation(ult, ultTarget); 53 | end 54 | 55 | if shock:IsFullyCastable() and 56 | npcBot:GetMana() - shock:GetManaCost() > ult:GetManaCost() and 57 | shock:GetLevel() >= 2 then 58 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', shock:GetCastRange(), true); 59 | 60 | if target == nil and 61 | npcBot:GetMana() > shock:GetManaCost() + hex:GetManaCost() + shackles:GetManaCost() + ult:GetManaCost() then 62 | target = npcBot:GetNearbyLaneCreeps(shock:GetCastRange(), true)[1]; 63 | end 64 | 65 | if target ~= nil then 66 | return npcBot:ActionPush_UseAbilityOnEntity(shock, target); 67 | end 68 | end 69 | 70 | if hex:IsFullyCastable() and 71 | npcBot:GetMana() - hex:GetManaCost() > ult:GetManaCost() + shock:GetManaCost() then 72 | local target = Helper.GetHeroWith(npcBot, 'max', 'GetRawOffensivePower', hex:GetCastRange(), true); 73 | if target ~= nil then 74 | return npcBot:ActionPush_UseAbilityOnEntity(hex, target); 75 | end 76 | end 77 | 78 | if shackles:IsFullyCastable() and 79 | #npcBot:GetNearbyHeroes(ult:GetCastRange(), false, BOT_MODE_NONE) >= 2 and 80 | npcBot:GetMana() - shackles:GetManaCost() > ult:GetManaCost() + shock:GetManaCost() then 81 | local target = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', shackles:GetCastRange(), true); 82 | if target ~= nil then 83 | return npcBot:ActionPush_UseAbilityOnEntity(shackles, target); 84 | end 85 | end 86 | 87 | end 88 | 89 | function ItemUsageThink() 90 | ability_item_usage_generic.ItemUsageThink(); 91 | end 92 | 93 | function BuybackUsageThink() 94 | ability_item_usage_generic.BuybackUsageThink(); 95 | end -------------------------------------------------------------------------------- /ability_item_usage_generic.lua: -------------------------------------------------------------------------------- 1 | _G._savedEnv = getfenv() 2 | module("ability_item_usage_generic", package.seeall) 3 | 4 | -- Include this before require to fix Mac 5 | local dir = GetScriptDirectory(); 6 | local function GetScriptDirectory() 7 | if string.sub(dir, 1, 6) == "/Users" then 8 | return string.match(dir, '.*/(.+)') 9 | end 10 | return dir; 11 | end 12 | ----------------------------------------- 13 | 14 | local Helper = require(GetScriptDirectory() .. "/helper"); 15 | 16 | local function considerGlyph(tower) 17 | if tower ~= nil then return end 18 | local recentValues = Helper.GetLastValues('Health:' .. tower:GetUnitName(), tower:GetHealth()); 19 | if recentValues[5][1] - recentValues[1][1] > tower:GetHealth() * 0.5 and 20 | GetGlyphCooldown() == 0 then 21 | GetBot():ActionImmediate_Glyph(); 22 | end 23 | end 24 | 25 | function ItemUsageThink() 26 | 27 | local npcBot = GetBot(); 28 | 29 | considerGlyph(Helper.GetOutermostTower(GetTeam(), LANE_TOP)); 30 | considerGlyph(Helper.GetOutermostTower(GetTeam(), LANE_MID)); 31 | considerGlyph(Helper.GetOutermostTower(GetTeam(), LANE_BOT)); 32 | 33 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 34 | return; 35 | end 36 | 37 | local teammates = npcBot:GetNearbyHeroes(900, false, BOT_MODE_NONE); 38 | local enemies = npcBot:GetNearbyHeroes(900, true, BOT_MODE_NONE); 39 | 40 | for i = 0,5 do 41 | local item = npcBot:GetItemInSlot(i); 42 | 43 | if (item) and string.find(item:GetName(), "item_necronomicon") and item:IsFullyCastable() and #enemies >= 1 then 44 | npcBot:ActionPush_UseAbility(item); 45 | end 46 | 47 | if (item) and string.find(item:GetName(), "item_dagon") and item:IsFullyCastable() and #enemies >= 1 then 48 | local weakestEnemy = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', item:GetCastRange(), true); 49 | if (weakestEnemy ~= nil) then 50 | npcBot:ActionPush_UseAbilityOnEntity(item, weakestEnemy); 51 | end 52 | end 53 | 54 | if (item) and (item:GetName() == "item_tpscroll" or item:GetName() == "item_travel_boots") and item:IsFullyCastable() then 55 | 56 | local target = nil; 57 | 58 | local enemy = Helper.GetEnemyLastSeenInfo(5.0); 59 | 60 | if npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_BOT then 61 | target = Helper.GetOutermostTower(GetTeam(), LANE_BOT):GetLocation(); 62 | elseif npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_MID then 63 | target = Helper.GetOutermostTower(GetTeam(), LANE_MID):GetLocation(); 64 | elseif npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_TOP then 65 | target = Helper.GetOutermostTower(GetTeam(), LANE_TOP):GetLocation(); 66 | elseif npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT and item:GetName() == "item_travel_boots" and #enemy[LANE_BOT] < 3 then 67 | target = GetLaneFrontLocation(GetTeam(), LANE_BOT, 0.0); 68 | elseif npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID and item:GetName() == "item_travel_boots" and #enemy[LANE_MID] < 3 then 69 | target = GetLaneFrontLocation(GetTeam(), LANE_MID, 0.0); 70 | elseif npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP and item:GetName() == "item_travel_boots" and #enemy[LANE_TOP] < 3 then 71 | target = GetLaneFrontLocation(GetTeam(), LANE_TOP, 0.0); 72 | end 73 | 74 | if target ~= nil and #teammates < 2 and 75 | GetUnitToLocationDistance(npcBot, target) > 5000 and 76 | Helper.IsForward(npcBot:GetLocation(), target) then 77 | local offset = Vector(-500, -500); 78 | if (GetTeam() == TEAM_DIRE) then 79 | offset = Vector(500, 500) 80 | end 81 | npcBot:ActionPush_UseAbilityOnLocation(item, target + offset); 82 | end 83 | 84 | end 85 | 86 | if (item) and ( 87 | item:GetName() == "item_mekansm" or 88 | item:GetName() == "item_pipe" or 89 | item:GetName() == "item_guardian_greaves" or 90 | item:GetName() == "item_crimson_guard" 91 | ) and item:IsFullyCastable() and teammates ~= nil and #teammates >=2 then 92 | if npcBot:GetHealth() <= 400 then 93 | npcBot:ActionPush_UseAbility(item); 94 | end 95 | for _, hero in pairs(teammates) do 96 | if (hero:GetHealth() <= 400) then 97 | npcBot:ActionPush_UseAbility(item); 98 | end 99 | end 100 | end 101 | 102 | if (item) and item:GetName() == "item_phase_boots" and item:IsFullyCastable() then 103 | npcBot:ActionPush_UseAbility(item); 104 | end 105 | 106 | if (item) and item:GetName() == "item_arcane_boots" and item:IsFullyCastable() then 107 | npcBot:ActionPush_UseAbility(item); 108 | end 109 | 110 | if (item) and item:GetName() == "item_shivas_guard" and item:IsFullyCastable() and #enemies >= 2 then 111 | npcBot:ActionPush_UseAbility(item); 112 | end 113 | 114 | if (item) and (item:GetName() == "item_invis_sword" or item:GetName() == "item_silver_edge") and 115 | item:IsFullyCastable() and 116 | npcBot:GetActiveMode() == BOT_MODE_RETREAT and 117 | npcBot:DistanceFromFountain() > 1500 then 118 | npcBot:ActionPush_UseAbility(item); 119 | end 120 | 121 | if (item) and 122 | (item:GetName() == "item_orchid" or item:GetName() == "item_recipe_bloodthorn") and 123 | item:IsFullyCastable() then 124 | local weakestEnemy = Helper.GetHeroWith(npcBot, 'min', 'GetHealth', item:GetCastRange(), true); 125 | if (weakestEnemy ~= nil) then 126 | npcBot:ActionPush_UseAbilityOnEntity(item, weakestEnemy); 127 | end 128 | end 129 | 130 | if (item) and 131 | item:GetName() == "item_sheepstick" and 132 | item:IsFullyCastable() then 133 | local target = Helper.GetHeroWith(npcBot, 'max', 'GetRawOffensivePower', item:GetCastRange(), true); 134 | if target ~= nil then 135 | npcBot:ActionPush_UseAbilityOnEntity(item, target); 136 | end 137 | end 138 | 139 | if (item) and 140 | item:GetName() == "item_blade_mail" and 141 | npcBot:WasRecentlyDamagedByAnyHero(2.0) and 142 | item:IsFullyCastable() then 143 | npcBot:ActionPush_UseAbility(item); 144 | end 145 | 146 | if (item) and 147 | npcBot:GetHealth() < npcBot:GetMaxHealth() - 200 and 148 | (not npcBot:HasModifier("modifier_tango_heal")) and 149 | item:GetName() == "item_tango" then 150 | local trees = npcBot:GetNearbyTrees(300); 151 | if (#trees > 0) then 152 | npcBot:Action_UseAbilityOnTree(item, trees[1]); 153 | end 154 | end 155 | 156 | if (item) and item:GetName() == "item_courier" and item:IsFullyCastable() then 157 | npcBot:ActionPush_UseAbility(item); 158 | end 159 | 160 | if (item) and item:GetName() == "item_flask" and item:IsFullyCastable() and npcBot:GetHealth() <= 200 then 161 | npcBot:ActionPush_UseAbilityOnEntity(item, npcBot); 162 | end 163 | 164 | if (item) and item:GetName() == "item_clarity" and item:IsFullyCastable() and npcBot:GetMana() <= 200 then 165 | npcBot:ActionPush_UseAbilityOnEntity(item, npcBot); 166 | end 167 | end 168 | 169 | end 170 | 171 | function BuybackUsageThink() 172 | local npcBot = GetBot(); 173 | if npcBot:HasBuyback() then 174 | print(npcBot:GetUnitName()); 175 | end 176 | if npcBot:IsAlive() or (not npcBot:HasBuyback()) then 177 | return; 178 | end 179 | if GetLaneFrontAmount(GetTeam(), LANE_TOP, false) < 0.2 or 180 | GetLaneFrontAmount(GetTeam(), LANE_MID, false) < 0.2 or 181 | GetLaneFrontAmount(GetTeam(), LANE_BOT, false) < 0.2 then 182 | npcBot:ActionImmediate_Buyback(); 183 | end 184 | end 185 | 186 | function CourierUsageThink() 187 | local npcBot = GetBot(); 188 | 189 | if not IsCourierAvailable() then 190 | return 191 | end 192 | 193 | if GetCourierState(GetCourier(GetNumCouriers() -1)) ~= COURIER_STATE_IDLE 194 | and GetCourierState(GetCourier(GetNumCouriers() -1)) ~= COURIER_STATE_AT_BASE then 195 | return 196 | end 197 | 198 | if GetCourierState(GetCourier(GetNumCouriers() -1)) == COURIER_STATE_IDLE 199 | and GetCourier(GetNumCouriers() -1):DistanceFromFountain() > 200 then 200 | npcBot:ActionImmediate_Courier(GetCourier(GetNumCouriers() -1), COURIER_ACTION_RETURN); 201 | return 202 | end 203 | 204 | local limit = math.min(DotaTime(), 1000); 205 | 206 | if npcBot:IsAlive() and (npcBot:GetStashValue() >= limit or npcBot:GetCourierValue() >= limit) then 207 | npcBot:ActionImmediate_Courier(GetCourier(GetNumCouriers() -1), COURIER_ACTION_TAKE_AND_TRANSFER_ITEMS); 208 | return 209 | end 210 | end 211 | 212 | 213 | for k,v in pairs(ability_item_usage_generic) do _G._savedEnv[k] = v end -------------------------------------------------------------------------------- /inspect.lua: -------------------------------------------------------------------------------- 1 | local inspect ={ 2 | _VERSION = 'inspect.lua 3.1.0', 3 | _URL = 'http://github.com/kikito/inspect.lua', 4 | _DESCRIPTION = 'human-readable representations of tables', 5 | _LICENSE = [[ 6 | MIT LICENSE 7 | 8 | Copyright (c) 2013 Enrique García Cota 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a 11 | copy of this software and associated documentation files (the 12 | "Software"), to deal in the Software without restriction, including 13 | without limitation the rights to use, copy, modify, merge, publish, 14 | distribute, sublicense, and/or sell copies of the Software, and to 15 | permit persons to whom the Software is furnished to do so, subject to 16 | the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included 19 | in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ]] 29 | } 30 | 31 | local tostring = tostring 32 | 33 | inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) 34 | inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) 35 | 36 | -- Apostrophizes the string if it has quotes, but not aphostrophes 37 | -- Otherwise, it returns a regular quoted string 38 | local function smartQuote(str) 39 | if str:match('"') and not str:match("'") then 40 | return "'" .. str .. "'" 41 | end 42 | return '"' .. str:gsub('"', '\\"') .. '"' 43 | end 44 | 45 | -- \a => '\\a', \0 => '\\0', 31 => '\31' 46 | local shortControlCharEscapes = { 47 | ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", 48 | ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" 49 | } 50 | local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 51 | for i=0, 31 do 52 | local ch = string.char(i) 53 | if not shortControlCharEscapes[ch] then 54 | shortControlCharEscapes[ch] = "\\"..i 55 | longControlCharEscapes[ch] = string.format("\\%03d", i) 56 | end 57 | end 58 | 59 | local function escape(str) 60 | return (str:gsub("\\", "\\\\") 61 | :gsub("(%c)%f[0-9]", longControlCharEscapes) 62 | :gsub("%c", shortControlCharEscapes)) 63 | end 64 | 65 | local function isIdentifier(str) 66 | return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) 67 | end 68 | 69 | local function isSequenceKey(k, sequenceLength) 70 | return type(k) == 'number' 71 | and 1 <= k 72 | and k <= sequenceLength 73 | and math.floor(k) == k 74 | end 75 | 76 | local defaultTypeOrders = { 77 | ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, 78 | ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 79 | } 80 | 81 | local function sortKeys(a, b) 82 | local ta, tb = type(a), type(b) 83 | 84 | -- strings and numbers are sorted numerically/alphabetically 85 | if ta == tb and (ta == 'string' or ta == 'number') then return a < b end 86 | 87 | local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] 88 | -- Two default types are compared according to the defaultTypeOrders table 89 | if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] 90 | elseif dta then return true -- default types before custom ones 91 | elseif dtb then return false -- custom types after default ones 92 | end 93 | 94 | -- custom types are sorted out alphabetically 95 | return ta < tb 96 | end 97 | 98 | -- For implementation reasons, the behavior of rawlen & # is "undefined" when 99 | -- tables aren't pure sequences. So we implement our own # operator. 100 | local function getSequenceLength(t) 101 | local len = 1 102 | local v = rawget(t,len) 103 | while v ~= nil do 104 | len = len + 1 105 | v = rawget(t,len) 106 | end 107 | return len - 1 108 | end 109 | 110 | local function getNonSequentialKeys(t) 111 | local keys = {} 112 | local sequenceLength = getSequenceLength(t) 113 | for k,_ in pairs(t) do 114 | if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end 115 | end 116 | table.sort(keys, sortKeys) 117 | return keys, sequenceLength 118 | end 119 | 120 | local function getToStringResultSafely(t, mt) 121 | local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') 122 | local str, ok 123 | if type(__tostring) == 'function' then 124 | ok, str = pcall(__tostring, t) 125 | str = ok and str or 'error: ' .. tostring(str) 126 | end 127 | if type(str) == 'string' and #str > 0 then return str end 128 | end 129 | 130 | local function countTableAppearances(t, tableAppearances) 131 | tableAppearances = tableAppearances or {} 132 | 133 | if type(t) == 'table' then 134 | if not tableAppearances[t] then 135 | tableAppearances[t] = 1 136 | for k,v in pairs(t) do 137 | countTableAppearances(k, tableAppearances) 138 | countTableAppearances(v, tableAppearances) 139 | end 140 | countTableAppearances(getmetatable(t), tableAppearances) 141 | else 142 | tableAppearances[t] = tableAppearances[t] + 1 143 | end 144 | end 145 | 146 | return tableAppearances 147 | end 148 | 149 | local copySequence = function(s) 150 | local copy, len = {}, #s 151 | for i=1, len do copy[i] = s[i] end 152 | return copy, len 153 | end 154 | 155 | local function makePath(path, ...) 156 | local keys = {...} 157 | local newPath, len = copySequence(path) 158 | for i=1, #keys do 159 | newPath[len + i] = keys[i] 160 | end 161 | return newPath 162 | end 163 | 164 | local function processRecursive(process, item, path, visited) 165 | 166 | if item == nil then return nil end 167 | if visited[item] then return visited[item] end 168 | 169 | local processed = process(item, path) 170 | if type(processed) == 'table' then 171 | local processedCopy = {} 172 | visited[item] = processedCopy 173 | local processedKey 174 | 175 | for k,v in pairs(processed) do 176 | processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) 177 | if processedKey ~= nil then 178 | processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) 179 | end 180 | end 181 | 182 | local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) 183 | setmetatable(processedCopy, mt) 184 | processed = processedCopy 185 | end 186 | return processed 187 | end 188 | 189 | 190 | 191 | ------------------------------------------------------------------- 192 | 193 | local Inspector = {} 194 | local Inspector_mt = {__index = Inspector} 195 | 196 | function Inspector:puts(...) 197 | local args = {...} 198 | local buffer = self.buffer 199 | local len = #buffer 200 | for i=1, #args do 201 | len = len + 1 202 | buffer[len] = args[i] 203 | end 204 | end 205 | 206 | function Inspector:down(f) 207 | self.level = self.level + 1 208 | f() 209 | self.level = self.level - 1 210 | end 211 | 212 | function Inspector:tabify() 213 | self:puts(self.newline, string.rep(self.indent, self.level)) 214 | end 215 | 216 | function Inspector:alreadyVisited(v) 217 | return self.ids[v] ~= nil 218 | end 219 | 220 | function Inspector:getId(v) 221 | local id = self.ids[v] 222 | if not id then 223 | local tv = type(v) 224 | id = (self.maxIds[tv] or 0) + 1 225 | self.maxIds[tv] = id 226 | self.ids[v] = id 227 | end 228 | return tostring(id) 229 | end 230 | 231 | function Inspector:putKey(k) 232 | if isIdentifier(k) then return self:puts(k) end 233 | self:puts("[") 234 | self:putValue(k) 235 | self:puts("]") 236 | end 237 | 238 | function Inspector:putTable(t) 239 | if t == inspect.KEY or t == inspect.METATABLE then 240 | self:puts(tostring(t)) 241 | elseif self:alreadyVisited(t) then 242 | self:puts('') 243 | elseif self.level >= self.depth then 244 | self:puts('{...}') 245 | else 246 | if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end 247 | 248 | local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t) 249 | local mt = getmetatable(t) 250 | local toStringResult = getToStringResultSafely(t, mt) 251 | 252 | self:puts('{') 253 | self:down(function() 254 | if toStringResult then 255 | self:puts(' -- ', escape(toStringResult)) 256 | if sequenceLength >= 1 then self:tabify() end 257 | end 258 | 259 | local count = 0 260 | for i=1, sequenceLength do 261 | if count > 0 then self:puts(',') end 262 | self:puts(' ') 263 | self:putValue(t[i]) 264 | count = count + 1 265 | end 266 | 267 | for _,k in ipairs(nonSequentialKeys) do 268 | if count > 0 then self:puts(',') end 269 | self:tabify() 270 | self:putKey(k) 271 | self:puts(' = ') 272 | self:putValue(t[k]) 273 | count = count + 1 274 | end 275 | 276 | if mt then 277 | if count > 0 then self:puts(',') end 278 | self:tabify() 279 | self:puts(' = ') 280 | self:putValue(mt) 281 | end 282 | end) 283 | 284 | if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing } 285 | self:tabify() 286 | elseif sequenceLength > 0 then -- array tables have one extra space before closing } 287 | self:puts(' ') 288 | end 289 | 290 | self:puts('}') 291 | end 292 | end 293 | 294 | function Inspector:putValue(v) 295 | local tv = type(v) 296 | 297 | if tv == 'string' then 298 | self:puts(smartQuote(escape(v))) 299 | elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then 300 | self:puts(tostring(v)) 301 | elseif tv == 'table' then 302 | self:putTable(v) 303 | else 304 | self:puts('<',tv,' ',self:getId(v),'>') 305 | end 306 | end 307 | 308 | ------------------------------------------------------------------- 309 | 310 | function inspect.inspect(root, options) 311 | options = options or {} 312 | 313 | local depth = options.depth or math.huge 314 | local newline = options.newline or '\n' 315 | local indent = options.indent or ' ' 316 | local process = options.process 317 | 318 | if process then 319 | root = processRecursive(process, root, {}, {}) 320 | end 321 | 322 | local inspector = setmetatable({ 323 | depth = depth, 324 | level = 0, 325 | buffer = {}, 326 | ids = {}, 327 | maxIds = {}, 328 | newline = newline, 329 | indent = indent, 330 | tableAppearances = countTableAppearances(root) 331 | }, Inspector_mt) 332 | 333 | inspector:putValue(root) 334 | 335 | return table.concat(inspector.buffer) 336 | end 337 | 338 | setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) 339 | 340 | return inspect 341 | -------------------------------------------------------------------------------- /helper.lua: -------------------------------------------------------------------------------- 1 | -- Include this before require to fix Mac 2 | local dir = GetScriptDirectory(); 3 | local function GetScriptDirectory() 4 | if string.sub(dir, 1, 6) == "/Users" then 5 | return string.match(dir, '.*/(.+)') 6 | end 7 | return dir; 8 | end 9 | ----------------------------------------- 10 | 11 | local VERSION = 'v20170320'; 12 | 13 | local inspect = require(GetScriptDirectory() .. "/inspect"); 14 | 15 | local Helper = {}; 16 | 17 | Helper.Locations = { 18 | RadiantShop = Vector(-4739,1263), 19 | DireShop = Vector(4559,-1554), 20 | BotShop = Vector(7253,-4128), 21 | TopShop = Vector(-7236,4444), 22 | } 23 | 24 | function Helper.PP(val) 25 | print(inspect(val)) 26 | end 27 | 28 | function Helper.AbilityUpgrade(npcBot, abilities) 29 | 30 | if npcBot:GetAbilityPoints() == 0 or DotaTime() < 0 or #abilities == 0 then 31 | return; 32 | end 33 | 34 | for i, ability in pairs(abilities) do 35 | 36 | local handle = npcBot:GetAbilityByName(ability); 37 | if handle ~= nil then 38 | if handle:GetLevel() == handle:GetMaxLevel() then 39 | table.remove(abilities, i); 40 | elseif handle:CanAbilityBeUpgraded() then 41 | npcBot:ActionImmediate_LevelAbility(ability); 42 | end 43 | end 44 | 45 | end 46 | end 47 | 48 | function Helper.PurchaseBootsAndTP(npcBot) 49 | 50 | local hasTravelBoots = false; 51 | local tpScroll = nil; 52 | local otherBoots = nil; 53 | 54 | for i = 0, 14 do 55 | local item = npcBot:GetItemInSlot(i); 56 | if (item) and item:GetName() == "item_travel_boots" then 57 | hasTravelBoots = true; 58 | end 59 | if (item) and item:GetName() == "item_tpscroll" then 60 | tpScroll = item; 61 | end 62 | if (item) and (item:GetName() == "item_phase_boots" or item:GetName() == "item_arcane_boots") then 63 | otherBoots = item; 64 | end 65 | end 66 | 67 | if hasTravelBoots then 68 | if tpScroll ~= nil then 69 | npcBot:ActionImmediate_SellItem(tpScroll); 70 | end 71 | if otherBoots ~= nil then 72 | npcBot:ActionImmediate_SellItem(otherBoots); 73 | end 74 | else 75 | Helper.PurchaseTP(npcBot); 76 | end 77 | 78 | end 79 | 80 | function Helper.WasDamagedFor(entity, seconds) 81 | 82 | end 83 | 84 | function Helper.PurchaseItems(npcBot, buildTable) 85 | if ( #buildTable == 0 ) then 86 | npcBot:SetNextItemPurchaseValue(0); 87 | return; 88 | end 89 | 90 | local sNextItem = buildTable[1]; 91 | npcBot:SetNextItemPurchaseValue(GetItemCost(sNextItem)); 92 | 93 | local function PurchaseItem(who) 94 | who:ActionImmediate_PurchaseItem( sNextItem ); 95 | print(npcBot:GetUnitName() .. " purchased " .. sNextItem) 96 | table.remove(buildTable, 1); 97 | end 98 | 99 | if (npcBot:GetGold() >= GetItemCost(sNextItem)) then 100 | if (IsItemPurchasedFromSecretShop(sNextItem)) then 101 | if GetCourier(GetNumCouriers() -1):DistanceFromSecretShop() < 300 then 102 | PurchaseItem(GetCourier(GetNumCouriers() -1)); 103 | npcBot:ActionImmediate_Courier(GetCourier(GetNumCouriers() -1), COURIER_ACTION_TRANSFER_ITEMS); 104 | elseif IsCourierAvailable() then 105 | npcBot:ActionImmediate_Courier(GetCourier(GetNumCouriers() -1), COURIER_ACTION_SECRET_SHOP); 106 | end 107 | else 108 | PurchaseItem(npcBot); 109 | end 110 | end 111 | end 112 | 113 | function Helper.PurchaseTP(npcBot) 114 | 115 | if npcBot:GetGold() < GetItemCost("item_tpscroll") * 2 then 116 | return; 117 | end 118 | 119 | for i = 0, 14 do 120 | local item = npcBot:GetItemInSlot(i); 121 | if (item) and item:GetName() == "item_tpscroll" then 122 | return; 123 | end 124 | end 125 | npcBot:ActionImmediate_PurchaseItem("item_tpscroll"); 126 | npcBot:ActionImmediate_PurchaseItem("item_tpscroll"); 127 | end 128 | 129 | local cache0 = {}; 130 | 131 | function Helper.OnValueChanged(key, value, callback) 132 | if cache0[key] ~= nil and cache0[key] ~= value then 133 | callback(value, cache0[key]) 134 | end 135 | cache0[key] = value; 136 | end 137 | 138 | local cache1 = {}; 139 | 140 | function Helper.GetLastValues(key, value) 141 | if cache1[key] == nil then 142 | cache1[key] = { 143 | {value, DotaTime()}, 144 | {value, DotaTime()}, 145 | {value, DotaTime()}, 146 | {value, DotaTime()}, 147 | {value, DotaTime()}, 148 | {value, DotaTime()}, 149 | {value, DotaTime()}, 150 | {value, DotaTime()}, 151 | {value, DotaTime()}, 152 | {value, DotaTime()}, 153 | }; 154 | end 155 | 156 | if DotaTime() - cache1[key][1][2] >= 1 then 157 | table.remove(cache1[key]) 158 | table.insert(cache1[key], 1, {value, DotaTime()}) 159 | end 160 | 161 | return cache1[key]; 162 | end 163 | 164 | local last = ""; 165 | function Helper.ChatIfChanged(text) 166 | if (last == text) then 167 | return; 168 | end 169 | last = text; 170 | GetBot():ActionImmediate_Chat(text, true); 171 | end 172 | 173 | local botId = nil; 174 | 175 | function Helper.GetFirstBot() 176 | if botId == nil then 177 | local players = GetTeamPlayers(GetTeam()); 178 | for _, v in pairs(players) do 179 | if IsPlayerBot(v) then 180 | botId = v; 181 | break; 182 | end 183 | end 184 | end 185 | return GetTeamMember(botId); 186 | end 187 | 188 | function Helper.GetEnemyLastSeenInfo(maxTime) 189 | local IDs = GetTeamPlayers(GetOpposingTeam()); 190 | local result = { 191 | [LANE_TOP] = {}, 192 | [LANE_MID] = {}, 193 | [LANE_BOT] = {}, 194 | }; 195 | for _,id in pairs(IDs) do 196 | local info = GetHeroLastSeenInfo(id); 197 | if IsHeroAlive(id) and info.time <= maxTime then 198 | local lane = LANE_TOP; 199 | if GetAmountAlongLane(LANE_MID, info.location).distance < GetAmountAlongLane(lane, info.location).distance then 200 | lane = LANE_MID; 201 | end 202 | if GetAmountAlongLane(LANE_BOT, info.location).distance < GetAmountAlongLane(lane, info.location).distance then 203 | lane = LANE_BOT; 204 | end 205 | local laneInfo = GetAmountAlongLane(lane, info.location); 206 | info.laneFrontDistance = laneInfo.distance; 207 | info.laneFrontAmount = laneInfo.amount; 208 | table.insert(result[lane], info); 209 | end 210 | end 211 | return result; 212 | end 213 | 214 | function Helper.TeamPushLane() 215 | 216 | local team = TEAM_RADIANT; 217 | if GetTeam() == TEAM_RADIANT then 218 | team = TEAM_DIRE; 219 | end 220 | 221 | if GetTower(team, TOWER_MID_1) ~= nil then 222 | return LANE_MID; 223 | end 224 | if GetTower(team, TOWER_BOT_1) ~= nil then 225 | return LANE_BOT; 226 | end 227 | if GetTower(team, TOWER_TOP_1) ~= nil then 228 | return LANE_TOP; 229 | end 230 | 231 | if GetTower(team, TOWER_MID_2) ~= nil then 232 | return LANE_MID; 233 | end 234 | if GetTower(team, TOWER_BOT_2) ~= nil then 235 | return LANE_BOT; 236 | end 237 | if GetTower(team, TOWER_TOP_2) ~= nil then 238 | return LANE_TOP; 239 | end 240 | 241 | if GetTower(team, TOWER_MID_3) ~= nil or 242 | GetBarracks(team, BARRACKS_MID_MELEE) ~= nil or 243 | GetBarracks(team, BARRACKS_MID_RANGED) ~= nil then 244 | return LANE_MID; 245 | end 246 | if GetTower(team, TOWER_BOT_3) ~= nil or 247 | GetBarracks(team, BARRACKS_BOT_MELEE) ~= nil or 248 | GetBarracks(team, BARRACKS_BOT_RANGED) ~= nil then 249 | return LANE_BOT; 250 | end 251 | if GetTower(team, TOWER_TOP_3) ~= nil or 252 | GetBarracks(team, BARRACKS_TOP_MELEE) ~= nil or 253 | GetBarracks(team, BARRACKS_TOP_RANGED) ~= nil then 254 | return LANE_TOP; 255 | end 256 | 257 | return LANE_MID; 258 | 259 | end 260 | 261 | local TeamLocation = {}; 262 | 263 | function Helper.WhichLaneToPush(npcBot) 264 | 265 | TeamLocation[npcBot:GetPlayerID()] = npcBot:GetLocation(); 266 | 267 | local distanceToTop = 0; 268 | local distanceToMid = 0; 269 | local distanceToBot = 0; 270 | 271 | local IDs = GetTeamPlayers(GetTeam()); 272 | 273 | for _,id in pairs(IDs) do 274 | if TeamLocation[id] ~= nil then 275 | if IsHeroAlive(id) then 276 | distanceToTop = math.max(distanceToTop, #(GetLaneFrontLocation(GetTeam(), LANE_TOP, 0.0) - TeamLocation[id])); 277 | distanceToMid = math.max(distanceToMid, #(GetLaneFrontLocation(GetTeam(), LANE_MID, 0.0) - TeamLocation[id])); 278 | distanceToBot = math.max(distanceToBot, #(GetLaneFrontLocation(GetTeam(), LANE_BOT, 0.0) - TeamLocation[id])); 279 | end 280 | else 281 | return Helper.TeamPushLane(); 282 | end 283 | end 284 | 285 | if distanceToBot < distanceToTop and 286 | distanceToBot < distanceToMid and 287 | (GetBarracks(GetOpposingTeam(), BARRACKS_BOT_MELEE) ~= nil or GetBarracks(GetOpposingTeam(), BARRACKS_BOT_RANGED) ~= nil) then 288 | -- Helper.ChatIfChanged("Pushing Bot"); 289 | return LANE_BOT; 290 | end 291 | 292 | if distanceToTop < distanceToMid and 293 | distanceToTop < distanceToBot and 294 | (GetBarracks(GetOpposingTeam(), BARRACKS_TOP_MELEE) ~= nil or GetBarracks(GetOpposingTeam(), BARRACKS_TOP_RANGED) ~= nil) then 295 | -- Helper.ChatIfChanged("Pushing Top"); 296 | return LANE_TOP; 297 | end 298 | 299 | if distanceToMid < distanceToTop and 300 | distanceToMid < distanceToBot and 301 | (GetBarracks(GetOpposingTeam(), BARRACKS_MID_MELEE) ~= nil or GetBarracks(GetOpposingTeam(), BARRACKS_MID_RANGED) ~= nil) then 302 | -- Helper.ChatIfChanged("Pushing Mid"); 303 | return LANE_MID; 304 | end 305 | 306 | return Helper.TeamPushLane(); 307 | 308 | end 309 | 310 | function Helper.TeamAlive() 311 | local radiantAlive = 0; 312 | local direAlive = 0; 313 | 314 | local IDs = GetTeamPlayers(TEAM_RADIANT); 315 | for _,id in pairs(IDs) do 316 | if IsHeroAlive(id) then 317 | radiantAlive = radiantAlive + 1; 318 | end 319 | end 320 | 321 | IDs = GetTeamPlayers(TEAM_DIRE); 322 | for _,id in pairs(IDs) do 323 | if IsHeroAlive(id) then 324 | direAlive = direAlive + 1; 325 | end 326 | end 327 | 328 | if GetTeam() == TEAM_RADIANT then 329 | return radiantAlive, direAlive; 330 | end 331 | 332 | return direAlive, radiantAlive; 333 | 334 | end 335 | 336 | function Helper.GetPushDesire(npcBot, lane) 337 | 338 | if DotaTime() >= 0 and DotaTime() < 10 then 339 | Helper.ChatIfChanged("Hi there, this is ExtremePush " .. VERSION); 340 | end 341 | if DotaTime() >= 10 and DotaTime() < 20 then 342 | Helper.ChatIfChanged("If this is older than the version in Workshop, try unsubscribe and resubscribe to force update"); 343 | end 344 | if DotaTime() >= 20 and DotaTime() < 30 then 345 | Helper.ChatIfChanged("Please report any bugs to @insraq (Twitter) or hi@ruoyusun.com (Email)"); 346 | end 347 | if DotaTime() >= 30 and DotaTime() < 40 then 348 | Helper.ChatIfChanged("Last but not least, goold luck and have fun :-)"); 349 | end 350 | 351 | local IDs = GetTeamPlayers(GetTeam()); 352 | for _,id in pairs(IDs) do 353 | if GetHeroLevel(id) < 6 and DotaTime() < 60 * 10 then 354 | return 0.1 355 | end 356 | end 357 | 358 | if Helper.WhichLaneToPush(npcBot) == lane then 359 | 360 | if #npcBot:GetNearbyHeroes(900, true, BOT_MODE_NONE) > 0 then 361 | return 0.1; 362 | end 363 | 364 | if npcBot:GetHealth() < 400 or 365 | GetLaneFrontAmount(GetTeam(), LANE_TOP, false) < 0.2 or 366 | GetLaneFrontAmount(GetTeam(), LANE_MID, false) < 0.2 or 367 | GetLaneFrontAmount(GetTeam(), LANE_BOT, false) < 0.2 then 368 | return 0.25; 369 | end 370 | 371 | local max = 0.95; 372 | if GetDefendLaneDesire(LANE_TOP) > 0.75 or GetDefendLaneDesire(LANE_MID) > 0.75 or GetDefendLaneDesire(LANE_BOT) > 0.75 then 373 | max = 0.75; 374 | end 375 | 376 | return Clamp(GetLaneFrontAmount(GetTeam(), lane, false) + 0.1, 0.25, max); 377 | 378 | end 379 | 380 | return 0.1; 381 | end 382 | 383 | function Helper.PushThink(npcBot, lane) 384 | 385 | if npcBot:IsChanneling() or npcBot:IsUsingAbility() then 386 | return; 387 | end 388 | 389 | local team, enemyTeam = Helper.TeamAlive(); 390 | 391 | local enemyIds = GetTeamPlayers(GetOpposingTeam()); 392 | local teammateIds = GetTeamPlayers(GetTeam()); 393 | 394 | local laneFrontLocation = GetLaneFrontLocation(GetTeam(), lane, 0); 395 | local enemyDistance = 0; 396 | local enemyAlive = 0; 397 | local teammateDistance = 0; 398 | local teammateAlive = 0; 399 | 400 | for _,id in pairs(enemyIds) do 401 | if IsHeroAlive(id) then 402 | local info = GetHeroLastSeenInfo(id); 403 | -- Take an aggressive guess 404 | enemyDistance = enemyDistance + math.max(#(info.location - laneFrontLocation) - info.time * 400, 400); 405 | enemyAlive = enemyAlive + 1; 406 | end 407 | end 408 | 409 | for _,id in pairs(teammateIds) do 410 | if IsHeroAlive(id) then 411 | local info = GetHeroLastSeenInfo(id); 412 | teammateDistance = teammateDistance + #(info.location - laneFrontLocation); 413 | teammateAlive = teammateAlive + 1; 414 | end 415 | end 416 | 417 | local offset = -math.max(teammateDistance / teammateAlive - enemyDistance / enemyAlive, 0); 418 | 419 | if Helper.WeAreStronger(npcBot, 1600) and #npcBot:GetNearbyHeroes(900, false, BOT_MODE_NONE) >= enemyAlive then 420 | offset = 0; 421 | end 422 | 423 | npcBot:ActionPush_MoveToLocation( 424 | GetLaneFrontLocation(GetTeam(), lane, offset) - Helper.RandomForwardVector(npcBot:GetAttackRange() * 0.8) 425 | ); 426 | 427 | local towers = npcBot:GetNearbyTowers(600, true); 428 | if #towers > 0 then 429 | return npcBot:ActionPush_AttackUnit(towers[1], true) 430 | end 431 | 432 | local barracks = npcBot:GetNearbyBarracks(600, true); 433 | if #barracks > 0 then 434 | return npcBot:ActionPush_AttackUnit(barracks[1], true) 435 | end 436 | 437 | local creeps = npcBot:GetNearbyLaneCreeps(600, true); 438 | if #creeps > 0 then 439 | return npcBot:ActionPush_AttackUnit(creeps[1], true) 440 | end 441 | 442 | end 443 | 444 | function Helper.WeAreStronger(npcBot, radius) 445 | 446 | local mates = npcBot:GetNearbyHeroes(radius, false, BOT_MODE_NONE); 447 | local enemies = npcBot:GetNearbyHeroes(radius, true, BOT_MODE_NONE); 448 | 449 | local ourPower = 0; 450 | local enemyPower = 0; 451 | 452 | for _, h in pairs(mates) do 453 | ourPower = ourPower + h:GetOffensivePower(); 454 | end 455 | 456 | for _, h in pairs(enemies) do 457 | enemyPower = enemyPower + h:GetRawOffensivePower(); 458 | end 459 | 460 | return #mates > #enemies and ourPower > enemyPower; 461 | 462 | end 463 | 464 | function Helper.GetOutermostTower(team, lane) 465 | 466 | if lane == LANE_TOP then 467 | if GetTower(team, TOWER_TOP_1) ~= nil then 468 | return GetTower(team, TOWER_TOP_1); 469 | end 470 | if GetTower(team, TOWER_TOP_2) ~= nil then 471 | return GetTower(team, TOWER_TOP_2); 472 | end 473 | if GetTower(team, TOWER_TOP_3) ~= nil then 474 | return GetTower(team, TOWER_TOP_3); 475 | end 476 | end 477 | 478 | if lane == LANE_MID then 479 | if GetTower(team, TOWER_MID_1) ~= nil then 480 | return GetTower(team, TOWER_MID_1); 481 | end 482 | if GetTower(team, TOWER_MID_2) ~= nil then 483 | return GetTower(team, TOWER_MID_2); 484 | end 485 | if GetTower(team, TOWER_MID_3) ~= nil then 486 | return GetTower(team, TOWER_MID_3); 487 | end 488 | if GetTower(team, TOWER_BASE_1) ~= nil then 489 | return GetTower(team, TOWER_BASE_1); 490 | end 491 | if GetTower(team, TOWER_BASE_2) ~= nil then 492 | return GetTower(team, TOWER_BASE_2); 493 | end 494 | end 495 | 496 | if lane == LANE_BOT then 497 | if GetTower(team, TOWER_BOT_1) ~= nil then 498 | return GetTower(team, TOWER_BOT_1); 499 | end 500 | if GetTower(team, TOWER_BOT_2) ~= nil then 501 | return GetTower(team, TOWER_BOT_2); 502 | end 503 | if GetTower(team, TOWER_BOT_3) ~= nil then 504 | return GetTower(team, TOWER_BOT_3); 505 | end 506 | end 507 | 508 | return GetAncient(team); 509 | 510 | end 511 | 512 | function Helper.RandomForwardVector(length) 513 | local offset = RandomVector(length); 514 | if GetTeam() == TEAM_RADIANT then 515 | offset.x = offset.x > 0 and offset.x or -offset.x; 516 | offset.y = offset.y > 0 and offset.y or -offset.y; 517 | end 518 | if GetTeam() == TEAM_DIRE then 519 | offset.x = offset.x < 0 and offset.x or -offset.x; 520 | offset.y = offset.y < 0 and offset.y or -offset.y; 521 | end 522 | return offset; 523 | end 524 | 525 | function Helper.IsForward(from, to) 526 | if GetTeam() == TEAM_RADIANT then 527 | return to.x - from.x > 0 or to.y - from.y > 0; 528 | end 529 | return to.x - from.x < 0 or to.y - from.y < 0; 530 | end 531 | 532 | function Helper.GetHeroWith(npcBot, comparison, attr, radius, enemy) 533 | 534 | local heroes = npcBot:GetNearbyHeroes(radius, enemy, BOT_MODE_NONE); 535 | local hero = npcBot; 536 | local value = npcBot[attr](npcBot); 537 | 538 | if enemy then 539 | hero = nil; 540 | value = 10000; 541 | end 542 | 543 | if comparison == 'max' then 544 | value = 0; 545 | end 546 | 547 | if heroes == nil or #heroes == 0 then 548 | return hero, value; 549 | end 550 | 551 | for _, h in pairs(heroes) do 552 | 553 | if h ~= nil and h:IsAlive() then 554 | 555 | local valueToCompare = h[attr](h); 556 | local success = valueToCompare < value; 557 | 558 | if comparison == 'max' then 559 | success = valueToCompare > value; 560 | end 561 | 562 | if success then 563 | value = valueToCompare; 564 | hero = h; 565 | end 566 | 567 | end 568 | end 569 | 570 | return hero, value; 571 | 572 | end 573 | 574 | return Helper; --------------------------------------------------------------------------------