├── bot_sven.lua ├── bot_antimage.lua ├── bot_nevermore.lua ├── bot_undying.lua ├── bot_crystal_maiden.lua ├── bot_earthshaker.lua ├── bot_juggernaut.lua ├── dev ├── constant_each_side.lua └── ability_item_usage_lina.lua ├── bot_mirana.lua ├── .gitignore ├── ability_item_usage_sven.lua ├── ability_item_usage_generic.lua ├── item_purchase_mirana.lua ├── item_purchase_shredder.lua ├── hero_selection.lua ├── notes └── note.txt ├── README.md ├── team_desires.lua ├── item_purchase_tinker.lua ├── item_purchase_lina.lua ├── item_purchase_zuus.lua ├── mode_defend_ally_lina.lua ├── mode_defend_ally_generic.lua ├── utility.lua ├── bot_zuus.lua ├── bot_shredder.lua ├── bot_lina.lua └── bot_tinker.lua /bot_sven.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_antimage.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_nevermore.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_undying.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_crystal_maiden.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_earthshaker.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /bot_juggernaut.lua: -------------------------------------------------------------------------------- 1 | function Think( ) 2 | --dummy function 3 | end -------------------------------------------------------------------------------- /dev/constant_each_side.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.HomePosition() 4 | if ( GetTeam() == TEAM_RADIANT ) then 5 | return Vector(-7000,-7000); 6 | elseif ( GetTeam() == TEAM_DIRE ) then 7 | return Vector(7200,6500); 8 | end 9 | end 10 | 11 | return M; -------------------------------------------------------------------------------- /bot_mirana.lua: -------------------------------------------------------------------------------- 1 | local DotaBotUtility = require(GetScriptDirectory().."/utility"); 2 | 3 | function Think( ) 4 | --dummy function 5 | local courier = DotaBotUtility.IsItemAvailable("item_courier"); 6 | if(courier ~= nil) then 7 | GetBot():Action_UseAbility(courier); 8 | end 9 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | *.dmp 10 | 11 | # Object files 12 | *.o 13 | *.os 14 | *.ko 15 | *.obj 16 | *.elf 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | *.def 28 | *.exp 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | -------------------------------------------------------------------------------- /ability_item_usage_sven.lua: -------------------------------------------------------------------------------- 1 | 2 | require( GetScriptDirectory().."/ability_item_usage_generic" ) 3 | 4 | ---------------------------------------------------------------------------------------------------- 5 | 6 | function AbilityUsageThink() 7 | 8 | --print( "Sven.AbilityUsageThink" ); 9 | 10 | ability_item_usage_generic.AbilityUsageThink(); 11 | 12 | end 13 | 14 | ---------------------------------------------------------------------------------------------------- 15 | 16 | function ItemUsageThink() 17 | 18 | --print( "Sven.ItemUsageThink" ); 19 | 20 | ability_item_usage_generic.ItemUsageThink(); 21 | 22 | end 23 | 24 | ---------------------------------------------------------------------------------------------------- 25 | -------------------------------------------------------------------------------- /ability_item_usage_generic.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | ---------------------------------------------------------------------------------------------------- 4 | 5 | _G._savedEnv = getfenv() 6 | module( "ability_item_usage_generic", package.seeall ) 7 | 8 | ---------------------------------------------------------------------------------------------------- 9 | 10 | function AbilityUsageThink() 11 | 12 | --print( "Generic.AbilityUsageThink" ); 13 | 14 | end 15 | 16 | ---------------------------------------------------------------------------------------------------- 17 | 18 | function ItemUsageThink() 19 | 20 | --print( "Generic.ItemUsageThink" ); 21 | 22 | end 23 | 24 | ---------------------------------------------------------------------------------------------------- 25 | 26 | 27 | for k,v in pairs( ability_item_usage_generic ) do _G._savedEnv[k] = v end 28 | -------------------------------------------------------------------------------- /item_purchase_mirana.lua: -------------------------------------------------------------------------------- 1 | 2 | --"item_flask" == heal flask 3 | 4 | local tableItemsToBuy = { 5 | "item_courier", 6 | "item_flying_courier" 7 | }; 8 | 9 | 10 | ---------------------------------------------------------------------------------------------------- 11 | 12 | function ItemPurchaseThink() 13 | local npcBot = GetBot(); 14 | if ( #tableItemsToBuy == 0 ) 15 | then 16 | npcBot:SetNextItemPurchaseValue( 0 ); 17 | return; 18 | end 19 | 20 | local sNextItem = tableItemsToBuy[1]; 21 | 22 | 23 | npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) ); 24 | 25 | if ( npcBot:GetGold() >= GetItemCost( sNextItem ) ) 26 | then 27 | npcBot:Action_PurchaseItem( sNextItem ); 28 | table.remove( tableItemsToBuy, 1 ); 29 | end 30 | 31 | end 32 | 33 | ---------------------------------------------------------------------------------------------------- 34 | -------------------------------------------------------------------------------- /item_purchase_shredder.lua: -------------------------------------------------------------------------------- 1 | 2 | --"item_flask" == heal flask 3 | 4 | local tableItemsToBuy = { 5 | "item_quelling_blade", 6 | "item_stout_shield", 7 | "item_boots" 8 | }; 9 | 10 | 11 | ---------------------------------------------------------------------------------------------------- 12 | 13 | function ItemPurchaseThink() 14 | local npcBot = GetBot(); 15 | if ( #tableItemsToBuy == 0 ) 16 | then 17 | npcBot:SetNextItemPurchaseValue( 0 ); 18 | return; 19 | end 20 | 21 | local sNextItem = tableItemsToBuy[1]; 22 | 23 | 24 | npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) ); 25 | 26 | if ( npcBot:GetGold() >= GetItemCost( sNextItem ) ) 27 | then 28 | npcBot:Action_PurchaseItem( sNextItem ); 29 | table.remove( tableItemsToBuy, 1 ); 30 | end 31 | 32 | end 33 | 34 | ---------------------------------------------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /hero_selection.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | ---------------------------------------------------------------------------------------------------- 4 | offset = 0; 5 | function Think() 6 | 7 | 8 | if ( GetTeam() == TEAM_RADIANT ) 9 | then 10 | print( "selecting radiant" ); 11 | SelectHero( 0 + offset, "npc_dota_hero_antimage" ); 12 | SelectHero( 1 + offset, "npc_dota_hero_drow_ranger" ); 13 | SelectHero( 2 + offset, "npc_dota_hero_sven" ); 14 | SelectHero( 3 + offset, "npc_dota_hero_undying" ); 15 | SelectHero( 4 + offset, "npc_dota_hero_crystal_maiden" ); 16 | elseif ( GetTeam() == TEAM_DIRE ) 17 | then 18 | print( "selecting dire" ); 19 | SelectHero( 5 + offset, "npc_dota_hero_lina" ); 20 | SelectHero( 6 + offset, "npc_dota_hero_zuus" ); 21 | SelectHero( 7 + offset, "npc_dota_hero_mirana" ); 22 | SelectHero( 8 + offset, "npc_dota_hero_shredder" ); 23 | SelectHero( 9 + offset, "npc_dota_hero_tinker" ); 24 | end 25 | 26 | end 27 | 28 | ---------------------------------------------------------------------------------------------------- 29 | -------------------------------------------------------------------------------- /notes/note.txt: -------------------------------------------------------------------------------- 1 | local cyclone = IsItemAvailable("item_cyclone"); 2 | if cyclone then 3 | print("cyclone damage: " .. cyclone:GetAbilityDamage()); 4 | damage is 0 5 | print("cyclone cast range: "..cyclone:GetCastRange()); 6 | cast range 575, fine. 7 | end 8 | 9 | Vector MetaTable 10 | 11 | function: 0x00216460 12 | Length2D 13 | function: 0x002165d8 14 | __unm 15 | function: 0x00216630 16 | Dot 17 | function: 0x00216808 18 | Normalized 19 | function: 0x00216898 20 | Length 21 | function: 0x00216590 22 | Cross 23 | function: 0x00216850 24 | __mul 25 | function: 0x002164d8 26 | __newindex 27 | function: 0x00216488 28 | __len 29 | function: 0x00216658 30 | __add 31 | function: 0x00216428 32 | __eq 33 | function: 0x00216568 34 | __sub 35 | function: 0x00216540 36 | __div 37 | function: 0x00216500 38 | __tostring 39 | function: 0x002164b0 40 | __index 41 | function: 0x00216460 42 | 43 | print((Vector(300,400,200)-Vector(200,300,0)):Length()); 3D Length 44 | print((Vector(300,400,200)-Vector(200,300,0)):Length2D()); 2D Length 45 | 46 | cheats: 47 | 48 | dota_bot_give_gold 10000 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dota2LinaBot 2 | 3 | ## if you want to play with lina 4 | ## now lina is in the Dire by default, you just stay in radiant's first slot and dont need to modify anything. 5 | ### if you want to be in Dire, 6 | ### modify hero_selection.lua : line 4: set offset = 4 and you must be in the third slot in dire. 7 | ### if you want to be ally with lina,you dont need to modify anything. 8 | ### if you want to play against lina,swap lina with a radiant hero in hero_selection.lua 9 | ### start and enjoy your match! 10 | 11 | completely take over control of lina by bot_lina.lua 12 | 13 | now lina can push mid, and try to farm, if lina's hp/mp is low,she will return to the base. 14 | If lina is attacked,she will try to kill enemy until enemy is dead or missing. 15 | 16 | Lina will never try to go into the tower's range.But if lina is angry,she will ignore anything. 17 | 18 | Now lina got cyclone LightStrikeArray combo! 19 | 20 | 21 | TODO: 22 | better interaction with tower. 23 | better last hit and deny timing . 24 | 25 | Now Working on Zuus 26 | 27 | 28 | start learing lua since 2016-12-13. 29 | -------------------------------------------------------------------------------- /team_desires.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | ---------------------------------------------------------------------------------------------------- 4 | 5 | function UpdatePushLaneDesires() 6 | 7 | return { 0.0, 1.0, 0.0 }; 8 | 9 | end 10 | 11 | ---------------------------------------------------------------------------------------------------- 12 | 13 | function UpdateDefendLaneDesires() 14 | 15 | return { 0.1, 1.0, 0.0 }; 16 | 17 | end 18 | 19 | ---------------------------------------------------------------------------------------------------- 20 | 21 | function UpdateFarmLaneDesires() 22 | 23 | return { 0.0, 1.0, 0.0 }; 24 | 25 | end 26 | 27 | ---------------------------------------------------------------------------------------------------- 28 | 29 | function UpdateRoamDesire() 30 | 31 | return { 0.5, GetTeamMember( TEAM_RADIANT, 1 ) }; 32 | 33 | end 34 | 35 | ---------------------------------------------------------------------------------------------------- 36 | 37 | function UpdateRoshanDesire() 38 | 39 | return 0.0; 40 | 41 | end 42 | 43 | ---------------------------------------------------------------------------------------------------- 44 | -------------------------------------------------------------------------------- /item_purchase_tinker.lua: -------------------------------------------------------------------------------- 1 | 2 | --"item_flask" == heal flask 3 | 4 | local tableItemsToBuy = { 5 | "item_mantle", 6 | "item_circlet", 7 | "item_recipe_null_talisman", 8 | "item_bottle", 9 | "item_boots", 10 | "item_sobi_mask", 11 | "item_ring_of_regen", 12 | "item_recipe_soul_ring", 13 | "item_recipe_travel_boots", 14 | "item_staff_of_wizardry", 15 | "item_recipe_dagon", 16 | "item_staff_of_wizardry", 17 | "item_wind_lace", 18 | "item_void_stone", 19 | "item_recipe_cyclone" 20 | }; 21 | 22 | 23 | ---------------------------------------------------------------------------------------------------- 24 | 25 | function ItemPurchaseThink() 26 | local npcBot = GetBot(); 27 | if ( #tableItemsToBuy == 0 ) 28 | then 29 | npcBot:SetNextItemPurchaseValue( 0 ); 30 | return; 31 | end 32 | 33 | local sNextItem = tableItemsToBuy[1]; 34 | 35 | 36 | npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) ); 37 | 38 | if ( npcBot:GetGold() >= GetItemCost( sNextItem ) ) 39 | then 40 | npcBot:Action_PurchaseItem( sNextItem ); 41 | table.remove( tableItemsToBuy, 1 ); 42 | end 43 | 44 | end 45 | 46 | ---------------------------------------------------------------------------------------------------- 47 | -------------------------------------------------------------------------------- /item_purchase_lina.lua: -------------------------------------------------------------------------------- 1 | 2 | --"item_flask" == heal flask 3 | 4 | local tableItemsToBuy = { 5 | "item_mantle", 6 | "item_circlet", 7 | "item_recipe_null_talisman", 8 | "item_blight_stone", 9 | "item_boots", 10 | "item_staff_of_wizardry", 11 | "item_wind_lace", 12 | "item_void_stone", 13 | "item_recipe_cyclone", 14 | "item_robe", 15 | "item_gloves", 16 | "item_gloves", 17 | "item_branches", 18 | "item_ring_of_regen", 19 | "item_recipe_headdress", 20 | "item_recipe_helm_of_the_dominator", 21 | "item_gloves", 22 | "item_mithril_hammer", 23 | "item_recipe_maelstrom", 24 | "item_mithril_hammer", 25 | "item_mithril_hammer" 26 | }; 27 | 28 | 29 | ---------------------------------------------------------------------------------------------------- 30 | 31 | function ItemPurchaseThink() 32 | local npcBot = GetBot(); 33 | if ( #tableItemsToBuy == 0 ) 34 | then 35 | npcBot:SetNextItemPurchaseValue( 0 ); 36 | return; 37 | end 38 | 39 | local sNextItem = tableItemsToBuy[1]; 40 | 41 | 42 | npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) ); 43 | 44 | if ( npcBot:GetGold() >= GetItemCost( sNextItem ) ) 45 | then 46 | npcBot:Action_PurchaseItem( sNextItem ); 47 | table.remove( tableItemsToBuy, 1 ); 48 | end 49 | 50 | end 51 | 52 | ---------------------------------------------------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /item_purchase_zuus.lua: -------------------------------------------------------------------------------- 1 | 2 | --"item_flask" == heal flask 3 | 4 | local tableItemsToBuy = { 5 | "item_mantle", 6 | "item_circlet", 7 | "item_recipe_null_talisman", 8 | "item_boots", 9 | "item_sobi_mask", 10 | "item_ring_of_protection", 11 | "item_branches", 12 | "item_ring_of_regen", 13 | "item_recipe_headdress", 14 | "item_lifesteal", 15 | "item_staff_of_wizardry", 16 | "item_wind_lace", 17 | "item_void_stone", 18 | "item_recipe_cyclone", 19 | "item_robe", 20 | "item_gloves", 21 | "item_staff_of_wizardry", 22 | "item_wind_lace", 23 | "item_void_stone", 24 | "item_recipe_cyclone", 25 | "item_staff_of_wizardry", 26 | "item_wind_lace", 27 | "item_void_stone", 28 | "item_recipe_cyclone", 29 | "item_staff_of_wizardry", 30 | "item_wind_lace", 31 | "item_void_stone", 32 | "item_recipe_cyclone" 33 | }; 34 | 35 | 36 | ---------------------------------------------------------------------------------------------------- 37 | 38 | function ItemPurchaseThink() 39 | local npcBot = GetBot(); 40 | if ( #tableItemsToBuy == 0 ) 41 | then 42 | npcBot:SetNextItemPurchaseValue( 0 ); 43 | return; 44 | end 45 | 46 | local sNextItem = tableItemsToBuy[1]; 47 | 48 | 49 | npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) ); 50 | 51 | if ( npcBot:GetGold() >= GetItemCost( sNextItem ) ) 52 | then 53 | npcBot:Action_PurchaseItem( sNextItem ); 54 | table.remove( tableItemsToBuy, 1 ); 55 | end 56 | 57 | end 58 | 59 | ---------------------------------------------------------------------------------------------------- 60 | -------------------------------------------------------------------------------- /mode_defend_ally_lina.lua: -------------------------------------------------------------------------------- 1 | 2 | require( GetScriptDirectory().."/mode_defend_ally_generic" ) 3 | 4 | ---------------------------------------------------------------------------------------------------- 5 | 6 | function OnStart() 7 | -- Do the standard OnStart 8 | mode_generic_defend_ally.OnStart(); 9 | end 10 | 11 | ---------------------------------------------------------------------------------------------------- 12 | 13 | function OnEnd() 14 | -- Do the standard OnEnd 15 | mode_generic_defend_ally.OnEnd(); 16 | end 17 | 18 | ---------------------------------------------------------------------------------------------------- 19 | 20 | function Think() 21 | 22 | local npcBot = GetBot(); 23 | 24 | -- Do the standard Think 25 | mode_generic_defend_ally.Think() 26 | 27 | -- Check if we're already using an ability 28 | if ( npcBot:IsUsingAbility() ) then return end; 29 | 30 | -- If we have a target and can cast LSA on them, do so 31 | if ( npcBot:GetTarget() ~= nil ) then 32 | abilityLSA = npcBot:GetAbilityByName( "lina_light_strike_array" ); 33 | if ( abilityLSA:IsFullyCastable() ) 34 | then 35 | npcBot:Action_UseAbilityOnLocation( abilityLSA, npcBot:GetTarget():GetLocation() ); 36 | end 37 | end 38 | end 39 | 40 | ---------------------------------------------------------------------------------------------------- 41 | 42 | function GetDesire() 43 | 44 | local npcBot = GetBot(); 45 | local fBonus = 0.0; 46 | 47 | -- If we have a target and can cast Light Strike Array, our desire to help defend should be higher than normal 48 | if ( npcBot:GetTarget() ~= nil ) 49 | then 50 | abilityLSA = npcBot:GetAbilityByName( "lina_light_strike_array" ); 51 | if ( abilityLSA:IsFullyCastable() ) 52 | then 53 | fBonus = 0.25; 54 | end 55 | end 56 | 57 | return Clamp( mode_generic_defend_ally.GetDesire() + fBonus, BOT_MODE_DESIRE_NONE, BOT_MODE_DESIRE_ABSOLUTE ); 58 | end 59 | 60 | ---------------------------------------------------------------------------------------------------- 61 | 62 | -------------------------------------------------------------------------------- /mode_defend_ally_generic.lua: -------------------------------------------------------------------------------- 1 | 2 | ---------------------------------------------------------------------------------------------------- 3 | 4 | _G._savedEnv = getfenv() 5 | module( "mode_generic_defend_ally", package.seeall ) 6 | 7 | ---------------------------------------------------------------------------------------------------- 8 | 9 | function OnStart() 10 | --print( "mode_generic_defend_ally.OnStart" ); 11 | end 12 | 13 | ---------------------------------------------------------------------------------------------------- 14 | 15 | function OnEnd() 16 | --print( "mode_generic_defend_ally.OnEnd" ); 17 | end 18 | 19 | ---------------------------------------------------------------------------------------------------- 20 | 21 | function Think() 22 | --print( "mode_generic_defend_ally.Think" ); 23 | end 24 | 25 | ---------------------------------------------------------------------------------------------------- 26 | 27 | function GetDesire() 28 | 29 | local npcBot = GetBot(); 30 | 31 | local fBestDefendScore = 0; 32 | local npcBestDefendableAlly = nil; 33 | local npcBestAttackingEnemy = nil; 34 | 35 | local tableNearbyRetreatingAlliedHeroes = npcBot:GetNearbyHeroes( 1000, false, BOT_MODE_RETREAT ); 36 | if ( #tableNearbyRetreatingAlliedHeroes == 0 ) 37 | then 38 | return BOT_MODE_DESIRE_NONE; 39 | end 40 | 41 | local tableNearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1000, true, BOT_MODE_NONE ); 42 | 43 | -- Is there a good enemy to attack, who's scary to our ally and not too scary to us? 44 | for _,npcAlly in pairs( tableNearbyRetreatingAlliedHeroes ) 45 | do 46 | local fDefendScore, npcEnemy = GetDefendScore( npcAlly, tableNearbyEnemyHeroes ); 47 | 48 | if ( fDefendScore > fBestDefendScore ) 49 | then 50 | fBestDefendScore = fDefendScore; 51 | npcBestDefendableAlly = npcAlly; 52 | npcBestAttackingEnemy = npcEnemy; 53 | end 54 | end 55 | 56 | return RemapValClamped( fBestDefendScore, 0.0, 1.0, BOT_MODE_DESIRE_NONE, BOT_MODE_DESIRE_HIGH ); 57 | end 58 | 59 | ---------------------------------------------------------------------------------------------------- 60 | 61 | function GetDefendScore( npcAlly, tableNearbyEnemyHeroes ) 62 | 63 | local nTotalEstimatedDamageToAlly = 0; 64 | local nTotalEstimatedDamageToMe = 0; 65 | local nMostEstimatedDamage = 0; 66 | local npcMostDangerousEnemy = nil; 67 | 68 | for _,npcEnemy in pairs( tableNearbyEnemyHeroes ) 69 | do 70 | local nEstimatedDamageToAlly = npcEnemy:GetEstimatedDamageToTarget( false, npcAlly, 3.0, DAMAGE_TYPE_ALL ); 71 | local nEstimatedDamageToMe = npcEnemy:GetEstimatedDamageToTarget( false, GetBot(), 3.0, DAMAGE_TYPE_ALL ); 72 | 73 | nTotalEstimatedDamageToAlly = nTotalEstimatedDamageToAlly + nEstimatedDamageToAlly; 74 | nTotalEstimatedDamageToMe = nTotalEstimatedDamageToMe + nEstimatedDamageToMe; 75 | 76 | if ( nEstimatedDamageToAlly > nMostEstimatedDamage ) 77 | then 78 | nMostEstimatedDamage = nEstimatedDamageToAlly; 79 | npcMostDangerousEnemy = npcEnemy; 80 | end 81 | end 82 | 83 | if ( npcMostDangerousEnemy ~= nil ) 84 | then 85 | local fDefendAllyDesire = RemapValClamped( nTotalEstimatedDamageToAlly / npcAlly:GetHealth(), 0.5, 1.5, 0.0, 1.0 ); 86 | local fSelfPreservationDesire = RemapValClamped( nTotalEstimatedDamageToMe / npcAlly:GetHealth(), 0.5, 1.5, 1.0, 0.0 ); 87 | 88 | return 0.5 * fDefendAllyDesire * fSelfPreservationDesire, npcMostDangerousEnemy; 89 | end 90 | 91 | return 0, nil; 92 | end 93 | 94 | ---------------------------------------------------------------------------------------------------- 95 | 96 | for k,v in pairs( mode_generic_defend_ally ) do _G._savedEnv[k] = v end 97 | 98 | ---------------------------------------------------------------------------------------------------- 99 | -------------------------------------------------------------------------------- /utility.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M["PointsOnLane"] = {} 4 | 5 | local function InitPointsOnLane(PointsOnLane) 6 | for i = 1, 3, 1 do 7 | PointsOnLane[i] = {}; 8 | for j = 0, 100, 1 do 9 | PointsOnLane[i][j] = GetLocationAlongLane(i,j / 100.0); 10 | end 11 | end 12 | end 13 | 14 | InitPointsOnLane(M["PointsOnLane"]); 15 | 16 | 17 | function M.NilOrDead(Unit) 18 | return Unit == nil or not Unit:IsAlive(); 19 | end 20 | 21 | function M.AbilityOutOfRange4Unit(Ability,Unit) 22 | return GetUnitToUnitDistance(GetBot(),Unit) > Ability:GetCastRange(); 23 | end 24 | 25 | function M.AbilityOutOfRange4Location(Ability,Location) 26 | return GetUnitToLocationDistance(GetBot(),Location) > Ability:GetCastRange(); 27 | end 28 | 29 | function M:GetNearByPrecursorPointOnLane(Lane,Location) 30 | local npcBot = GetBot(); 31 | local Pos = npcBot:GetLocation(); 32 | if Location ~= nil then 33 | Pos = Location; 34 | end 35 | 36 | local PointsOnLane = self["PointsOnLane"][Lane]; 37 | local prevDist = (Pos - PointsOnLane[0]):Length2D(); 38 | for i = 1,100,1 do 39 | local d = (Pos - PointsOnLane[i]):Length2D(); 40 | if(d > prevDist) then 41 | if i >= 4 then 42 | return PointsOnLane[i - 4] + RandomVector(50); 43 | else 44 | return PointsOnLane[i - 1]; 45 | end 46 | else 47 | prevDist = d; 48 | end 49 | end 50 | 51 | return PointsOnLane[100]; 52 | end 53 | 54 | function M:GetNearBySuccessorPointOnLane(Lane,Location) 55 | local npcBot = GetBot(); 56 | local Pos = npcBot:GetLocation(); 57 | if Location ~= nil then 58 | Pos = Location; 59 | end 60 | 61 | local PointsOnLane = self["PointsOnLane"][Lane]; 62 | local prevDist = (Pos - PointsOnLane[100]):Length2D(); 63 | for i = 100,0,-1 do 64 | local d = (Pos - PointsOnLane[i]):Length2D(); 65 | if(d > prevDist) then 66 | if i <= 96 then 67 | return PointsOnLane[i + 4] + RandomVector(100); 68 | else 69 | return PointsOnLane[i + 1]; 70 | end 71 | else 72 | prevDist = d; 73 | end 74 | end 75 | 76 | return PointsOnLane[0]; 77 | end 78 | 79 | function M.IsItemAvailable(item_name) 80 | local npcBot = GetBot(); 81 | -- query item code by Hewdraw 82 | for i = 0, 5, 1 do 83 | local item = npcBot:GetItemInSlot(i); 84 | if(item and item:GetName() == item_name) then 85 | return item; 86 | end 87 | end 88 | return nil; 89 | end 90 | 91 | function M:GetComfortPoint(creeps,LANE) 92 | local npcBot = GetBot(); 93 | local mypos = npcBot:GetLocation(); 94 | local x_pos_sum = 0; 95 | local y_pos_sum = 0; 96 | local count = 0; 97 | local meele_coefficient = 5;-- Consider meele creeps first 98 | local coefficient = 1; 99 | for creep_k,creep in pairs(creeps) 100 | do 101 | local creep_name = creep:GetUnitName(); 102 | local meleepos = string.find( creep_name,"melee"); 103 | if(meleepos ~= nil) then 104 | coefficient = meele_coefficient; 105 | else 106 | coefficient = 1; 107 | end 108 | 109 | creep_pos = creep:GetLocation(); 110 | x_pos_sum = x_pos_sum + coefficient * creep_pos[1]; 111 | y_pos_sum = y_pos_sum + coefficient * creep_pos[2]; 112 | count = count + coefficient; 113 | end 114 | 115 | local avg_pos_x = x_pos_sum / count; 116 | local avg_pos_y = y_pos_sum / count; 117 | 118 | if(count > 0) then 119 | return self:GetNearByPrecursorPointOnLane(LANE,Vector(avg_pos_x,avg_pos_y)) + RandomVector(20); 120 | else 121 | return nil; 122 | end; 123 | end 124 | 125 | function M:HasEmptySlot() 126 | local npcBot = GetBot(); 127 | -- query item code by Hewdraw 128 | for i = 0, 5, 1 do 129 | local item = npcBot:GetItemInSlot(i); 130 | if(item == nil) then 131 | return true; 132 | end 133 | end 134 | return false; 135 | end 136 | 137 | function M:CourierThink() 138 | local npcBot = GetBot(); 139 | for i = 9, 15, 1 do 140 | local item = npcBot:GetItemInSlot(i); 141 | if((item ~= nil or npcBot:GetCourierValue() > 0) and IsCourierAvailable()) then 142 | --print("got item"); 143 | npcBot:Action_CourierDeliver(); 144 | return; 145 | end 146 | end 147 | end 148 | 149 | function M:GetFrontTowerAt(LANE) 150 | local T1 = -1; 151 | local T2 = -1; 152 | local T3 = -1; 153 | 154 | if(LANE == LANE_TOP) then 155 | T1 = TOWER_TOP_1; 156 | T2 = TOWER_TOP_2; 157 | T3 = TOWER_TOP_3; 158 | elseif(LANE == LANE_MID) then 159 | T1 = TOWER_MID_1; 160 | T2 = TOWER_MID_2; 161 | T3 = TOWER_MID_3; 162 | elseif(LANE == LANE_BOT) then 163 | T1 = TOWER_BOT_1; 164 | T2 = TOWER_BOT_2; 165 | T3 = TOWER_BOT_3; 166 | end 167 | 168 | local tower = GetTower(GetTeam(),T1); 169 | if(tower ~= nil and tower:IsAlive())then 170 | return tower; 171 | end 172 | 173 | tower = GetTower(GetTeam(),T2); 174 | if(tower ~= nil and tower:IsAlive())then 175 | return tower; 176 | end 177 | 178 | tower = GetTower(GetTeam(),T3); 179 | if(tower ~= nil and tower:IsAlive())then 180 | return tower; 181 | end 182 | return nil; 183 | end 184 | 185 | function M:CreepGC() 186 | -- does it works? i don't know 187 | print("CreepGC"); 188 | local swp_table = {} 189 | for handle,time_health in pairs(self["creeps"]) 190 | do 191 | local rm = false; 192 | for t,_ in pairs(time_health) 193 | do 194 | if(GameTime() - t > 60) then 195 | rm = true; 196 | end 197 | break; 198 | end 199 | if not rm then 200 | swp_table[handle] = time_health; 201 | end 202 | end 203 | 204 | self["creeps"] = swp_table; 205 | end 206 | 207 | function M:UpdateCreepHealth(creep) 208 | if self["creeps"] == nil then 209 | self["creeps"] = {}; 210 | end 211 | 212 | if(self["creeps"][creep] == nil) then 213 | self["creeps"][creep] = {}; 214 | end 215 | if(creep:GetHealth() < creep:GetMaxHealth()) then 216 | self["creeps"][creep][GameTime()] = creep:GetHealth(); 217 | end 218 | 219 | if(#self["creeps"] > 1000) then 220 | self:CreepGC(); 221 | end 222 | end 223 | 224 | function pairsByKeys(t, f) 225 | local a = {} 226 | for n in pairs(t) do table.insert(a, n) end 227 | table.sort(a, f) 228 | local i = 0 -- iterator variable 229 | local iter = function () -- iterator function 230 | i = i + 1 231 | if a[i] == nil then return nil 232 | else return a[i], t[a[i]] 233 | end 234 | end 235 | return iter 236 | end 237 | 238 | function sortFunc(a , b) 239 | if a < b then 240 | return true 241 | end 242 | end 243 | 244 | function M:GetCreepHealthDeltaPerSec(creep) 245 | if self["creeps"] == nil then 246 | self["creeps"] = {}; 247 | end 248 | 249 | if(self["creeps"][creep] == nil) then 250 | return 10000000; 251 | else 252 | for _time,_health in pairsByKeys(self["creeps"][creep],sortFunc) 253 | do 254 | -- only Consider very recent datas 255 | if(GameTime() - _time < 3) then 256 | local e = (_health - creep:GetHealth()) / (GameTime() - _time); 257 | return e; 258 | end 259 | end 260 | return 10000000; 261 | end 262 | 263 | end 264 | 265 | function M:ConsiderRunAway() 266 | local npcBot = GetBot(); 267 | local enemy = npcBot:GetNearbyHeroes( 600, true, BOT_MODE_NONE ); 268 | if(#enemy > 1 and DotaTime() > 360) then 269 | return true; 270 | end 271 | return false; 272 | end 273 | 274 | function M:GetEnemyBots() 275 | local myteam = GetTeam(); 276 | if myteam == TEAM_DIRE then 277 | return GetTeamPlayers(TEAM_RADIANT); 278 | else 279 | return GetTeamPlayers(TEAM_DIRE); 280 | end 281 | end 282 | 283 | function M:GetEnemyTeam() 284 | local myteam = GetTeam(); 285 | if myteam == TEAM_DIRE then 286 | return TEAM_RADIANT; 287 | else 288 | return TEAM_DIRE; 289 | end 290 | end 291 | 292 | return M; -------------------------------------------------------------------------------- /dev/ability_item_usage_lina.lua: -------------------------------------------------------------------------------- 1 | 2 | ---------------------------------------------------------------------------------------------------- 3 | 4 | 5 | local function AbilityUsageThink() 6 | 7 | local npcBot = GetBot(); 8 | 9 | local LocationMetaTable = getmetatable(npcBot:GetLocation()); 10 | 11 | --[[ 12 | for k,v in pairs(LocationMetaTable) 13 | do 14 | print(k); 15 | end 16 | print("---------------------"); 17 | 18 | --[[ 19 | Length2D 20 | unm 21 | Dot 22 | Normalized 23 | Length 24 | Cross 25 | mul 26 | newindex 27 | len 28 | add 29 | eq 30 | sub 31 | div 32 | tostring 33 | index 34 | ]] 35 | 36 | --print(npcBot:GetLocation()[1].." " .. npcBot:GetLocation()[2]) 37 | 38 | local LocationAlongLaneMetatable = getmetatable(GetLocationAlongLane(3,2)); 39 | 40 | for k,v in pairs(LocationAlongLaneMetatable) 41 | do 42 | print(k); 43 | end 44 | print(GetLocationAlongLane(2,10)); 45 | print("---------------------"); 46 | 47 | 48 | 49 | -- Check if we're already using an ability 50 | if ( npcBot:IsUsingAbility() ) then return end; 51 | 52 | abilityLSA = npcBot:GetAbilityByName( "lina_light_strike_array" ); 53 | abilityDS = npcBot:GetAbilityByName( "lina_dragon_slave" ); 54 | abilityLB = npcBot:GetAbilityByName( "lina_laguna_blade" ); 55 | 56 | -- Consider using each ability 57 | castLBDesire, castLBTarget = ConsiderLagunaBlade(); 58 | castLSADesire, castLSALocation = ConsiderLightStrikeArray(); 59 | castDSDesire, castDSLocation = ConsiderDragonSlave(); 60 | 61 | if ( castLBDesire > castLSADesire and castLBDesire > castDSDesire ) 62 | then 63 | npcBot:Action_UseAbilityOnEntity( abilityLB, castLBTarget ); 64 | return; 65 | end 66 | 67 | if ( castLSADesire > 0 ) 68 | then 69 | npcBot:Action_UseAbilityOnLocation( abilityLSA, castLSALocation ); 70 | return; 71 | end 72 | 73 | if ( castDSDesire > 0 ) 74 | then 75 | npcBot:Action_UseAbilityOnLocation( abilityDS, castDSLocation ); 76 | return; 77 | end 78 | 79 | middle_point = npcBot:GetLocation(); 80 | middle_point[1] = 0.0; 81 | middle_point[2] = 0.0; 82 | 83 | 84 | npcBot:Action_AttackMove(middle_point); 85 | 86 | 87 | end 88 | 89 | ---------------------------------------------------------------------------------------------------- 90 | 91 | function CanCastLightStrikeArrayOnTarget( npcTarget ) 92 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 93 | end 94 | 95 | 96 | function CanCastDragonSlaveOnTarget( npcTarget ) 97 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 98 | end 99 | 100 | 101 | function CanCastLagunaBladeOnTarget( npcTarget ) 102 | return npcTarget:CanBeSeen() and npcTarget:IsHero() and ( GetBot():HasScepter() or not npcTarget:IsMagicImmune() ) and not npcTarget:IsInvulnerable(); 103 | end 104 | 105 | function CanCastCycloneOnTarget( npcTarget ) 106 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 107 | end 108 | 109 | ---------------------------------------------------------------------------------------------------- 110 | 111 | function ConsiderCyclone(cyclone,enemy) 112 | local npcBot = GetBot(); 113 | 114 | -- Make sure it's castable 115 | if ( not cyclone:IsFullyCastable() ) then 116 | return false; 117 | end 118 | 119 | local nCastRange = cyclone:GetCastRange(); 120 | local EnemyLocation = predictPosition(enemy,1); 121 | local d = GetUnitToLocationDistance(npcBot,EnemyLocation); 122 | 123 | if (d < nCastRange and CanCastCycloneOnTarget( enemy ) ) 124 | then 125 | return true; 126 | end 127 | 128 | 129 | end 130 | 131 | function ConsiderLightStrikeArrayFighting(abilityLSA,enemy) 132 | local npcBot = GetBot(); 133 | 134 | if ( not abilityLSA:IsFullyCastable() ) 135 | then 136 | return BOT_ACTION_DESIRE_NONE, 0; 137 | end; 138 | 139 | local nCastRange = abilityLSA:GetCastRange(); 140 | 141 | local EnemyLocation = predictPosition(enemy,1); 142 | 143 | local d = GetUnitToLocationDistance(npcBot,EnemyLocation); 144 | 145 | if (d < nCastRange and CanCastLightStrikeArrayOnTarget( enemy ) ) 146 | then 147 | return BOT_ACTION_DESIRE_MODERATE, EnemyLocation; 148 | end 149 | return BOT_ACTION_DESIRE_NONE, 0; 150 | end 151 | 152 | 153 | function ConsiderLightStrikeArray(abilityLSA) 154 | 155 | local npcBot = GetBot(); 156 | 157 | -- Make sure it's castable 158 | if ( not abilityLSA:IsFullyCastable() ) 159 | then 160 | return BOT_ACTION_DESIRE_NONE, 0; 161 | end; 162 | 163 | 164 | -- Get some of its values 165 | local nRadius = abilityLSA:GetSpecialValueInt( "light_strike_array_aoe" ); 166 | local nCastRange = abilityLSA:GetCastRange(); 167 | local nDamage = abilityLSA:GetAbilityDamage(); 168 | 169 | -------------------------------------- 170 | -- Global high-priorty usage 171 | -------------------------------------- 172 | 173 | -- Check for a channeling enemy 174 | local tableNearbyEnemyHeroes = npcBot:GetNearbyHeroes( nCastRange + nRadius + 200, true, BOT_MODE_NONE ); 175 | for _,npcEnemy in pairs( tableNearbyEnemyHeroes ) 176 | do 177 | if ( npcEnemy:IsChanneling() ) 178 | then 179 | return BOT_ACTION_DESIRE_HIGH, npcEnemy:GetLocation(); 180 | end 181 | end 182 | 183 | -------------------------------------- 184 | -- Mode based usage 185 | -------------------------------------- 186 | 187 | -- If we're farming and can kill 3+ creeps with LSA 188 | if (true) then 189 | local locationAoE = npcBot:FindAoELocation( true, false, npcBot:GetLocation(), nCastRange, nRadius, 0, nDamage ); 190 | 191 | if ( locationAoE.count >= 3 ) then 192 | return BOT_ACTION_DESIRE_LOW, locationAoE.targetloc; 193 | end 194 | end 195 | 196 | -- If we're seriously retreating, see if we can land a stun on someone who's damaged us recently 197 | if (true ) 198 | then 199 | local tableNearbyEnemyHeroes = npcBot:GetNearbyHeroes( nCastRange + nRadius + 200, true, BOT_MODE_NONE ); 200 | for _,npcEnemy in pairs( tableNearbyEnemyHeroes ) 201 | do 202 | if ( npcBot:WasRecentlyDamagedByHero( npcEnemy, 2.0 ) ) 203 | then 204 | if ( CanCastLightStrikeArrayOnTarget( npcEnemy ) ) 205 | then 206 | return BOT_ACTION_DESIRE_MODERATE, npcEnemy:GetLocation(); 207 | end 208 | end 209 | end 210 | end 211 | 212 | -- If we're going after someone 213 | if ( true) 214 | then 215 | local npcTarget = npcBot:GetTarget(); 216 | 217 | if ( npcTarget ~= nil ) 218 | then 219 | if ( CanCastLightStrikeArrayOnTarget( npcTarget ) ) 220 | then 221 | return BOT_ACTION_DESIRE_HIGH, npcTarget:GetLocation(); 222 | end 223 | end 224 | end 225 | 226 | return BOT_ACTION_DESIRE_NONE, 0; 227 | end 228 | 229 | ---------------------------------------------------------------------------------------------------- 230 | 231 | function ConsiderDragonSlaveFighting(abilityDS,enemy) 232 | local npcBot = GetBot(); 233 | 234 | if ( not abilityDS:IsFullyCastable() ) then 235 | return BOT_ACTION_DESIRE_NONE, 0; 236 | end; 237 | 238 | local nCastRange = abilityDS:GetCastRange(); 239 | 240 | local d = GetUnitToUnitDistance(npcBot,enemy); 241 | 242 | if(d < nCastRange and CanCastDragonSlaveOnTarget(enemy))then 243 | return BOT_ACTION_DESIRE_MODERATE, enemy:GetLocation(); 244 | end 245 | 246 | return BOT_ACTION_DESIRE_NONE, 0; 247 | end 248 | 249 | function ConsiderDragonSlave(abilityDS) 250 | 251 | local npcBot = GetBot(); 252 | 253 | 254 | if ( not abilityDS:IsFullyCastable() ) then 255 | return BOT_ACTION_DESIRE_NONE, 0; 256 | end; 257 | 258 | 259 | 260 | -- Get some of its values 261 | local nRadius = abilityDS:GetSpecialValueInt( "dragon_slave_width_end" ); 262 | local nCastRange = abilityDS:GetCastRange(); 263 | local nDamage = abilityDS:GetAbilityDamage(); 264 | 265 | -------------------------------------- 266 | -- Mode based usage 267 | -------------------------------------- 268 | 269 | -- If we're farming and can kill 2+ creeps with LSA when we have plenty mana 270 | if ( true) then 271 | local locationAoE = npcBot:FindAoELocation( true, false, npcBot:GetLocation(), nCastRange, nRadius, 0, nDamage ); 272 | 273 | if ( locationAoE.count >= 2 ) then 274 | return BOT_ACTION_DESIRE_LOW, locationAoE.targetloc; 275 | end 276 | end 277 | 278 | -- If we're pushing or defending a lane and can hit 4+ creeps, go for it 279 | -- wasting mana banned! 280 | if (nDamage > 300) 281 | then 282 | local locationAoE = npcBot:FindAoELocation( true, false, npcBot:GetLocation(), nCastRange, nRadius, 0, 0 ); 283 | 284 | if ( locationAoE.count >= 4 ) 285 | then 286 | return BOT_ACTION_DESIRE_LOW, locationAoE.targetloc; 287 | end 288 | end 289 | 290 | -- If we're going after someone 291 | if (true) 292 | then 293 | local npcTarget = npcBot:GetTarget(); 294 | 295 | if ( npcTarget ~= nil ) 296 | then 297 | if ( CanCastDragonSlaveOnTarget( npcTarget ) ) 298 | then 299 | return BOT_ACTION_DESIRE_MODERATE, npcEnemy:GetLocation(); 300 | end 301 | end 302 | end 303 | 304 | -- If we have plenty mana and high level DS 305 | if(npcBot:GetMana() / npcBot:GetMaxMana() > 0.6 and nDamage > 300) then 306 | local locationAoE = npcBot:FindAoELocation( true, true, npcBot:GetLocation(), nCastRange, nRadius, 0, 0 ); 307 | 308 | -- hit heros 309 | if ( locationAoE.count >= 1 ) 310 | then 311 | return BOT_ACTION_DESIRE_LOW, locationAoE.targetloc; 312 | end 313 | end 314 | 315 | return BOT_ACTION_DESIRE_NONE, 0; 316 | end 317 | 318 | 319 | ---------------------------------------------------------------------------------------------------- 320 | 321 | function ConsiderLagunaBlade(abilityLB) 322 | 323 | local npcBot = GetBot(); 324 | 325 | -- Make sure it's castable 326 | if ( not abilityLB:IsFullyCastable() ) then 327 | return BOT_ACTION_DESIRE_NONE, 0; 328 | end 329 | 330 | 331 | -- Get some of its values 332 | local nCastRange = abilityLB:GetCastRange(); 333 | local nDamage = abilityLB:GetSpecialValueInt( "damage" ); 334 | local eDamageType = npcBot:HasScepter() and DAMAGE_TYPE_PURE or DAMAGE_TYPE_MAGICAL; 335 | 336 | -- If a mode has set a target, and we can kill them, do it 337 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( nCastRange + 200, true, BOT_MODE_NONE ); 338 | if(NearbyEnemyHeroes ~= nil) then 339 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 340 | do 341 | if (CanCastLagunaBladeOnTarget( npcEnemy ) ) 342 | then 343 | if ( npcEnemy:GetActualDamage( nDamage, eDamageType ) > npcEnemy:GetHealth() ) 344 | then 345 | return BOT_ACTION_DESIRE_HIGH, npcEnemy; 346 | end 347 | end 348 | end 349 | end 350 | 351 | --[[ 352 | -- If we're in a teamfight, use it on the scariest enemy 353 | local tableNearbyAttackingAlliedHeroes = npcBot:GetNearbyHeroes( 1000, false, BOT_MODE_ATTACK ); 354 | if ( #tableNearbyAttackingAlliedHeroes >= 2 ) 355 | then 356 | 357 | local npcMostDangerousEnemy = nil; 358 | local nMostDangerousDamage = 0; 359 | 360 | local tableNearbyEnemyHeroes = npcBot:GetNearbyHeroes( nCastRange, true, BOT_MODE_NONE ); 361 | for _,npcEnemy in pairs( tableNearbyEnemyHeroes ) 362 | do 363 | if ( CanCastLagunaBladeOnTarget( npcEnemy ) ) 364 | then 365 | local nDamage = npcEnemy:GetEstimatedDamageToTarget( false, npcBot, 3.0, DAMAGE_TYPE_ALL ); 366 | if ( nDamage > nMostDangerousDamage ) 367 | then 368 | nMostDangerousDamage = nDamage; 369 | npcMostDangerousEnemy = npcEnemy; 370 | end 371 | end 372 | end 373 | 374 | if ( npcMostDangerousEnemy ~= nil ) 375 | then 376 | return BOT_ACTION_DESIRE_HIGH, npcMostDangerousEnemy; 377 | end 378 | end 379 | ]] 380 | 381 | return BOT_ACTION_DESIRE_NONE, 0; 382 | 383 | end 384 | 385 | function predictPosition(hero,t) 386 | -- prediction by hero:GetVelocity(); is not good 387 | local ret = hero:GetLocation(); 388 | local v = hero:GetVelocity(); 389 | ret[1] = ret[1] + t * v[1]; 390 | ret[2] = ret[2] + t * v[2]; 391 | return hero:GetLocation(); 392 | end 393 | 394 | -------------------------------------------------------------------------------- /bot_zuus.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StateMachine is a table 3 | the key "STATE" stores the STATE of Lina 4 | other key value pairs: key is the string of state value is the function of the State. 5 | 6 | each frame DOTA2 will call Think() 7 | Then Think() will call the function of current state. 8 | ]] 9 | local Constant = require(GetScriptDirectory().."/dev/constant_each_side"); 10 | local DotaBotUtility = require(GetScriptDirectory().."/utility"); 11 | 12 | local STATE_IDLE = "STATE_IDLE"; 13 | local STATE_ATTACKING_CREEP = "STATE_ATTACKING_CREEP"; 14 | local STATE_KILL = "STATE_KILL"; 15 | local STATE_RETREAT = "STATE_RETREAT"; 16 | local STATE_FARMING = "STATE_FARMING"; 17 | local STATE_GOTO_COMFORT_POINT = "STATE_GOTO_COMFORT_POINT"; 18 | local STATE_FIGHTING = "STATE_FIGHTING"; 19 | local STATE_RUN_AWAY = "STATE_RUN_AWAY"; 20 | 21 | local RetreatHPThreshold = 0.3; 22 | local RetreatMPThreshold = 0.2; 23 | 24 | local STATE = STATE_IDLE; 25 | 26 | LANE = LANE_TOP 27 | 28 | function CanCastALOnTarget( npcTarget ) 29 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 30 | end 31 | 32 | function CanCastLBOnTarget( npcTarget ) 33 | return not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 34 | end 35 | 36 | function CanCastTGWOnTarget( npcTarget ) 37 | return not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 38 | end 39 | 40 | ----------------- local utility functions reordered for lua local visibility-------- 41 | --Perry's code from http://dev.dota2.com/showthread.php?t=274837 42 | function PerryGetHeroLevel() 43 | local npcBot = GetBot(); 44 | local respawnTable = {8, 10, 12, 14, 16, 26, 28, 30, 32, 34, 36, 46, 48, 50, 52, 54, 56, 66, 70, 74, 78, 82, 86, 90, 100}; 45 | local nRespawnTime = npcBot:GetRespawnTime() +1 -- It gives 1 second lower values. 46 | for k,v in pairs (respawnTable) do 47 | if v == nRespawnTime then 48 | return k 49 | end 50 | end 51 | end 52 | 53 | function GetTGWdamage() 54 | local HeroLevel = PerryGetHeroLevel(); 55 | if(HeroLevel < 6) then 56 | return 0; 57 | elseif(HeroLevel < 12) then 58 | return 225; 59 | elseif(HeroLevel < 18) then 60 | return 325; 61 | else 62 | return 425; 63 | end 64 | end 65 | 66 | local function ConsiderFighting(StateMachine) 67 | local ShouldFight = false; 68 | local npcBot = GetBot(); 69 | 70 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1000, true, BOT_MODE_NONE ); 71 | if(NearbyEnemyHeroes ~= nil) then 72 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 73 | do 74 | if(npcBot:WasRecentlyDamagedByHero(npcEnemy,1)) then 75 | -- got the enemy who attacks me, kill him!-- 76 | StateMachine["EnemyToKill"] = npcEnemy; 77 | ShouldFight = true; 78 | break; 79 | elseif(GetUnitToUnitDistance(npcBot,npcEnemy) < 500) then 80 | StateMachine["EnemyToKill"] = npcEnemy; 81 | ShouldFight = true; 82 | break; 83 | end 84 | end 85 | end 86 | return ShouldFight; 87 | end 88 | 89 | 90 | local function ConsiderAttackCreeps() 91 | -- there are creeps try to attack them -- 92 | --print("ConsiderAttackCreeps"); 93 | local npcBot = GetBot(); 94 | 95 | local EnemyCreeps = npcBot:GetNearbyCreeps(1000,true); 96 | 97 | -- Check if we're already using an ability 98 | if ( npcBot:IsUsingAbility() ) then return end; 99 | 100 | local abilityAL = npcBot:GetAbilityByName( "zuus_arc_lightning" ); 101 | local abilityLB = npcBot:GetAbilityByName( "zuus_lightning_bolt" ); 102 | local abilityTGW = npcBot:GetAbilityByName( "zuus_thundergods_wrath" ); 103 | 104 | local ALdamage = abilityAL:GetAbilityDamage(); 105 | local ALcastRange = abilityAL:GetCastRange(); 106 | 107 | local LBdamage = abilityLB:GetAbilityDamage(); 108 | local LBcastRange = abilityLB:GetCastRange(); 109 | 110 | local TGWdamage = GetTGWdamage(); 111 | 112 | if(abilityAL:IsFullyCastable()) then 113 | for _,creep in pairs(EnemyCreeps) 114 | do 115 | --npcBot:GetEstimatedDamageToTarget 116 | local creep_name = creep:GetUnitName(); 117 | local siege_pos = string.find(creep_name,"siege"); 118 | if(creep:IsAlive() and siege_pos == nil) then 119 | local creep_hp = creep:GetHealth(); 120 | if(creep:GetActualDamage(ALdamage,DAMAGE_TYPE_MAGICAL) > creep_hp)then 121 | npcBot:Action_UseAbilityOnEntity(abilityAL,creep); 122 | return; 123 | end 124 | end 125 | end 126 | end 127 | 128 | -- nothing to do , try to attack heros 129 | 130 | --[[ 131 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( LBcastRange, true, BOT_MODE_NONE ); 132 | if(NearbyEnemyHeroes ~= nil) then 133 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 134 | do 135 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget())) then 136 | npcBot:Action_AttackUnit(npcEnemy,false); 137 | return; 138 | end 139 | end 140 | end 141 | ]] 142 | 143 | 144 | end 145 | 146 | local function ShouldRetreat() 147 | local npcBot = GetBot(); 148 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 149 | < RetreatHPThreshold or npcBot:GetMana()/npcBot:GetMaxMana() 150 | < RetreatMPThreshold; 151 | end 152 | 153 | local function IsTowerAttackingMe() 154 | local npcBot = GetBot(); 155 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 156 | if(#NearbyTowers > 0) then 157 | for _,tower in pairs( NearbyTowers) 158 | do 159 | if(GetUnitToUnitDistance(tower,npcBot) < 900 and tower:IsAlive()) then 160 | print("Attacked by tower"); 161 | return true; 162 | end 163 | end 164 | else 165 | return false; 166 | end 167 | end 168 | 169 | -------------------local states----------------------------------------------------- 170 | 171 | local function StateIdle(StateMachine) 172 | local npcBot = GetBot(); 173 | if(npcBot:IsAlive() == false) then 174 | return; 175 | end 176 | 177 | local creeps = npcBot:GetNearbyCreeps(1000,true); 178 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 179 | 180 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 181 | 182 | if(ShouldRetreat()) then 183 | StateMachine.State = STATE_RETREAT; 184 | return; 185 | elseif(IsTowerAttackingMe()) then 186 | StateMachine.State = STATE_RUN_AWAY; 187 | return; 188 | elseif(npcBot:GetAttackTarget() ~= nil) then 189 | if(npcBot:GetAttackTarget():IsHero()) then 190 | StateMachine["EnemyToKill"] = npcBot:GetAttackTarget(); 191 | print("auto attacking: "..npcBot:GetAttackTarget():GetUnitName()); 192 | StateMachine.State = STATE_FIGHTING; 193 | return; 194 | end 195 | elseif(ConsiderFighting(StateMachine)) then 196 | StateMachine.State = STATE_FIGHTING; 197 | return; 198 | 199 | elseif(#creeps > 0 and pt ~= nil) then 200 | local mypos = npcBot:GetLocation(); 201 | 202 | local d = GetUnitToLocationDistance(npcBot,pt); 203 | if(d > 250) then 204 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 205 | else 206 | StateMachine.State = STATE_ATTACKING_CREEP; 207 | end 208 | return; 209 | end 210 | 211 | -- buy a tp and get out 212 | if(npcBot:DistanceFromFountain() < 100 and DotaTime() > 0) then 213 | local tpscroll = DotaBotUtility.IsItemAvailable("item_tpscroll"); 214 | if(tpscroll == nil and DotaBotUtility:HasEmptySlot() and npcBot:GetGold() >= GetItemCost("item_tpscroll")) then 215 | print("buying tp"); 216 | npcBot:Action_PurchaseItem("item_tpscroll"); 217 | return; 218 | elseif(tpscroll ~= nil and tpscroll:IsFullyCastable()) then 219 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 220 | if(tower ~= nil) then 221 | npcBot:Action_UseAbilityOnEntity(tpscroll,tower); 222 | return; 223 | end 224 | end 225 | end 226 | 227 | if(DotaTime() < 20) then 228 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 229 | npcBot:Action_MoveToLocation(tower:GetLocation()); 230 | return; 231 | else 232 | target = DotaBotUtility:GetNearBySuccessorPointOnLane(LANE); 233 | npcBot:Action_AttackMove(target); 234 | return; 235 | end 236 | 237 | 238 | end 239 | 240 | local function StateAttackingCreep(StateMachine) 241 | local npcBot = GetBot(); 242 | if(npcBot:IsAlive() == false) then 243 | StateMachine.State = STATE_IDLE; 244 | return; 245 | end 246 | 247 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 248 | 249 | local creeps = npcBot:GetNearbyCreeps(1000,true); 250 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 251 | 252 | if(ShouldRetreat()) then 253 | StateMachine.State = STATE_RETREAT; 254 | return; 255 | elseif(IsTowerAttackingMe()) then 256 | StateMachine.State = STATE_RUN_AWAY; 257 | elseif(ConsiderFighting(StateMachine)) then 258 | StateMachine.State = STATE_FIGHTING; 259 | return; 260 | elseif(#creeps > 0 and pt ~= nil) then 261 | local mypos = npcBot:GetLocation(); 262 | local d = GetUnitToLocationDistance(npcBot,pt); 263 | if(d > 250) then 264 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 265 | else 266 | ConsiderAttackCreeps(); 267 | end 268 | return; 269 | else 270 | StateMachine.State = STATE_IDLE; 271 | return; 272 | end 273 | end 274 | 275 | local function StateRetreat(StateMachine) 276 | local npcBot = GetBot(); 277 | if(npcBot:IsAlive() == false) then 278 | StateMachine.State = STATE_IDLE; 279 | return; 280 | end 281 | 282 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 283 | 284 | npcBot:Action_MoveToLocation(Constant.HomePosition()); 285 | 286 | if(npcBot:GetHealth() == npcBot:GetMaxHealth() and npcBot:GetMana() == npcBot:GetMaxMana()) then 287 | StateMachine.State = STATE_IDLE; 288 | return; 289 | end 290 | end 291 | 292 | local function StateGotoComfortPoint(StateMachine) 293 | local npcBot = GetBot(); 294 | if(npcBot:IsAlive() == false) then 295 | StateMachine.State = STATE_IDLE; 296 | return; 297 | end 298 | 299 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 300 | 301 | local creeps = npcBot:GetNearbyCreeps(1000,true); 302 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 303 | 304 | 305 | if(ShouldRetreat()) then 306 | StateMachine.State = STATE_RETREAT; 307 | return; 308 | elseif(IsTowerAttackingMe()) then 309 | StateMachine.State = STATE_RUN_AWAY; 310 | elseif(ConsiderFighting(StateMachine)) then 311 | StateMachine.State = STATE_FIGHTING; 312 | return; 313 | elseif(#creeps > 0 and pt ~= nil) then 314 | local mypos = npcBot:GetLocation(); 315 | --pt[3] = npcBot:GetLocation()[3]; 316 | 317 | --local d = GetUnitToLocationDistance(npcBot,pt); 318 | local d = (npcBot:GetLocation() - pt):Length2D(); 319 | 320 | if (d < 200) then 321 | StateMachine.State = STATE_ATTACKING_CREEP; 322 | else 323 | npcBot:Action_MoveToLocation(pt); 324 | end 325 | return; 326 | else 327 | StateMachine.State = STATE_IDLE; 328 | return; 329 | end 330 | 331 | end 332 | 333 | local function StateFighting(StateMachine) 334 | local npcBot = GetBot(); 335 | if(npcBot:IsAlive() == false) then 336 | StateMachine.State = STATE_IDLE; 337 | return; 338 | end 339 | 340 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 341 | 342 | if(IsTowerAttackingMe()) then 343 | StateMachine.State = STATE_RUN_AWAY; 344 | elseif(not StateMachine["EnemyToKill"]:CanBeSeen() or not StateMachine["EnemyToKill"]:IsAlive()) then 345 | -- lost enemy 346 | print("lost enemy"); 347 | StateMachine.State = STATE_IDLE; 348 | return; 349 | else 350 | if ( npcBot:IsUsingAbility() ) then return end; 351 | 352 | local abilityAL = npcBot:GetAbilityByName( "zuus_arc_lightning" ); 353 | local abilityLB = npcBot:GetAbilityByName( "zuus_lightning_bolt" ); 354 | local abilityTGW = npcBot:GetAbilityByName( "zuus_thundergods_wrath" ); 355 | 356 | local ALdamage = abilityAL:GetAbilityDamage(); 357 | local ALcastRange = abilityAL:GetCastRange(); 358 | 359 | local LBdamage = abilityLB:GetAbilityDamage(); 360 | local LBcastRange = abilityLB:GetCastRange(); 361 | 362 | local TGWdamage = GetTGWdamage(); 363 | 364 | if(abilityAL:IsFullyCastable() and CanCastALOnTarget(StateMachine["EnemyToKill"])) then 365 | npcBot:Action_UseAbilityOnEntity(abilityAL,StateMachine["EnemyToKill"]); 366 | return; 367 | end 368 | 369 | if(abilityLB:IsFullyCastable() and CanCastLBOnTarget(StateMachine["EnemyToKill"])) then 370 | npcBot:Action_UseAbilityOnLocation(abilityLB,StateMachine["EnemyToKill"]:GetLocation()); 371 | return; 372 | end 373 | 374 | if(abilityTGW:IsFullyCastable() and CanCastTGWOnTarget(StateMachine["EnemyToKill"]) and 375 | StateMachine["EnemyToKill"]:GetActualDamage(TGWdamage,DAMAGE_TYPE_MAGICAL) >= StateMachine["EnemyToKill"]:GetHealth()) then 376 | npcBot:Action_UseAbility(abilityTGW); 377 | return; 378 | end 379 | 380 | if(not abilityAL:IsFullyCastable() and 381 | not abilityLB:IsFullyCastable() or StateMachine["EnemyToKill"]:IsMagicImmune()) then 382 | local extraHP = 0; 383 | if(abilityTGW:IsFullyCastable()) then 384 | extraHP = StateMachine["EnemyToKill"]:GetActualDamage(TGWdamage,DAMAGE_TYPE_MAGICAL); 385 | end 386 | 387 | if(StateMachine["EnemyToKill"]:GetHealth() - extraHP > npcBot:GetHealth()) then 388 | StateMachine.State = STATE_RUN_AWAY; 389 | return; 390 | end 391 | end 392 | 393 | StateMachine.State = STATE_RUN_AWAY; 394 | return; 395 | 396 | end 397 | end 398 | 399 | local function StateRunAway(StateMachine) 400 | local npcBot = GetBot(); 401 | 402 | if(npcBot:IsAlive() == false) then 403 | StateMachine.State = STATE_IDLE; 404 | StateMachine["RunAwayFromLocation"] = nil; 405 | return; 406 | end 407 | 408 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 409 | 410 | if(ShouldRetreat()) then 411 | StateMachine.State = STATE_RETREAT; 412 | StateMachine["RunAwayFromLocation"] = nil; 413 | return; 414 | end 415 | 416 | local mypos = npcBot:GetLocation(); 417 | 418 | if(StateMachine["RunAwayFromLocation"] == nil) then 419 | --set the target to go back 420 | StateMachine["RunAwayFromLocation"] = npcBot:GetLocation(); 421 | --npcBot:Action_MoveToLocation(Constant.HomePosition()); 422 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 423 | return; 424 | else 425 | if(GetUnitToLocationDistance(npcBot,StateMachine["RunAwayFromLocation"]) > 400) then 426 | -- we are far enough from tower,return to normal state. 427 | StateMachine["RunAwayFromLocation"] = nil; 428 | StateMachine.State = STATE_IDLE; 429 | return; 430 | else 431 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 432 | return; 433 | end 434 | end 435 | end 436 | 437 | -- useless now ignore it 438 | local function StateFarming(StateMachine) 439 | local npcBot = GetBot(); 440 | if(npcBot:IsAlive() == false) then 441 | StateMachine.State = STATE_IDLE; 442 | return; 443 | end 444 | end 445 | 446 | local StateMachine = {}; 447 | StateMachine["State"] = STATE_IDLE; 448 | StateMachine[STATE_IDLE] = StateIdle; 449 | StateMachine[STATE_ATTACKING_CREEP] = StateAttackingCreep; 450 | StateMachine[STATE_RETREAT] = StateRetreat; 451 | StateMachine[STATE_GOTO_COMFORT_POINT] = StateGotoComfortPoint; 452 | StateMachine[STATE_FIGHTING] = StateFighting; 453 | StateMachine[STATE_RUN_AWAY] = StateRunAway; 454 | StateMachine["totalLevelOfAbilities"] = 0; 455 | 456 | 457 | local ZuusAbilityMap = { 458 | [1] = "zuus_arc_lightning", 459 | [2] = "zuus_static_field", 460 | [3] = "zuus_lightning_bolt", 461 | [4] = "zuus_lightning_bolt", 462 | [5] = "zuus_lightning_bolt", 463 | [6] = "zuus_thundergods_wrath", 464 | [7] = "zuus_lightning_bolt", 465 | [8] = "zuus_static_field", 466 | [9] = "zuus_static_field", 467 | [10] = "special_bonus_mp_regen_2", 468 | [11] = "zuus_static_field", 469 | [12] = "zuus_thundergods_wrath", 470 | [13] = "zuus_arc_lightning", 471 | [14] = "zuus_arc_lightning", 472 | [15] = "special_bonus_armor_5", 473 | [16] = "zuus_arc_lightning", 474 | [18] = "zuus_thundergods_wrath", 475 | [20] = "special_bonus_movement_speed_35", 476 | [25] = "special_bonus_cast_range_200" 477 | }; 478 | 479 | local ZuusDoneLvlupAbility = {}; 480 | 481 | for lvl,_ in pairs(ZuusAbilityMap) 482 | do 483 | ZuusDoneLvlupAbility[lvl] = false; 484 | end 485 | 486 | local function ThinkLvlupAbility(StateMachine) 487 | -- Is there a bug? http://dev.dota2.com/showthread.php?t=274436 488 | local npcBot = GetBot(); 489 | 490 | 491 | local HeroLevel = PerryGetHeroLevel(); 492 | if(ZuusDoneLvlupAbility[HeroLevel] == false) then 493 | npcBot:Action_LevelAbility(ZuusAbilityMap[HeroLevel]); 494 | --keep trying lvlup skill since 12/22 495 | --ZuusDoneLvlupAbility[HeroLevel] = true; 496 | end 497 | end 498 | 499 | local PrevState = "none"; 500 | 501 | function Think( ) 502 | -- Think this item( ... ) 503 | --update 504 | 505 | local npcBot = GetBot(); 506 | DotaBotUtility:CourierThink(); 507 | ThinkLvlupAbility(StateMachine); 508 | 509 | local EnemyBots = DotaBotUtility:GetEnemyBots(); 510 | local EnemyTeam = DotaBotUtility:GetEnemyTeam(); 511 | local abilityTGW = npcBot:GetAbilityByName( "zuus_thundergods_wrath" ); 512 | local TGWdamage = GetTGWdamage(); 513 | 514 | if(abilityTGW:IsFullyCastable() 515 | and not ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) 516 | )then 517 | for _,idx in pairs(EnemyBots) 518 | do 519 | local BotHandle = GetTeamMember(EnemyTeam,idx); 520 | if(BotHandle ~= nil and BotHandle:IsAlive() and BotHandle:CanBeSeen() 521 | and BotHandle:GetActualDamage(TGWdamage,DAMAGE_TYPE_MAGICAL) 522 | > BotHandle:GetHealth()) then 523 | npcBot:Action_UseAbility(abilityTGW); 524 | return; 525 | end 526 | end 527 | end 528 | 529 | StateMachine[StateMachine.State](StateMachine); 530 | 531 | if(PrevState ~= StateMachine.State) then 532 | print("Zuus bot STATE: "..StateMachine.State); 533 | PrevState = StateMachine.State; 534 | end 535 | 536 | if(DotaTime() > 600) then 537 | LANE = LANE_MID; 538 | end 539 | 540 | end 541 | -------------------------------------------------------------------------------- /bot_shredder.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StateMachine is a table 3 | the key "STATE" stores the STATE of Lina 4 | other key value pairs: key is the string of state value is the function of the State. 5 | 6 | each frame DOTA2 will call Think() 7 | Then Think() will call the function of current state. 8 | ]] 9 | 10 | local Constant = require(GetScriptDirectory().."/dev/constant_each_side"); 11 | local DotaBotUtility = require(GetScriptDirectory().."/utility"); 12 | 13 | local STATE_IDLE = "STATE_IDLE"; 14 | local STATE_ATTACKING_CREEP = "STATE_ATTACKING_CREEP"; 15 | local STATE_KILL = "STATE_KILL"; 16 | local STATE_RETREAT = "STATE_RETREAT"; 17 | local STATE_FARMING = "STATE_FARMING"; 18 | local STATE_GOTO_COMFORT_POINT = "STATE_GOTO_COMFORT_POINT"; 19 | local STATE_FIGHTING = "STATE_FIGHTING"; 20 | local STATE_RUN_AWAY = "STATE_RUN_AWAY"; 21 | local STATE_TEAM_FIGHTING = "STATE_TEAM_FIGHTING"; 22 | 23 | local TimberSawRetreatHPThreshold = 0.3; 24 | local TimberSawRetreatMPThreshold = 0.2; 25 | 26 | local STATE = STATE_IDLE; 27 | 28 | LANE = LANE_TOP; 29 | 30 | local function TimberSawIsBusy() 31 | local npcBot = GetBot(); 32 | 33 | local busy = npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:GetCurrentActionType() == BOT_ACTION_TYPE_USE_ABILITY; 34 | 35 | return busy; 36 | end 37 | 38 | local function CanCastOnTarget( npcTarget ) 39 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 40 | end 41 | 42 | local function IsInTeamFight() 43 | return false; 44 | end 45 | 46 | local function GetTRange() 47 | local npcBot = GetBot(); 48 | local abilityT = npcBot:GetAbilityByName( "shredder_timber_chain" ); 49 | 50 | local lvl = abilityT:GetLevel(); 51 | return 600 + lvl * 200; 52 | end 53 | 54 | ----------------- local utility functions reordered for lua local visibility-------- 55 | --Perry's code from http://dev.dota2.com/showthread.php?t=274837 56 | local function PerryGetHeroLevel() 57 | local npcBot = GetBot(); 58 | local respawnTable = {8, 10, 12, 14, 16, 26, 28, 30, 32, 34, 36, 46, 48, 50, 52, 54, 56, 66, 70, 74, 78, 82, 86, 90, 100}; 59 | local nRespawnTime = npcBot:GetRespawnTime() +1 -- It gives 1 second lower values. 60 | for k,v in pairs (respawnTable) do 61 | if v == nRespawnTime then 62 | return k 63 | end 64 | end 65 | end 66 | 67 | local function ConsiderFighting(StateMachine) 68 | local ShouldFight = false; 69 | local npcBot = GetBot(); 70 | 71 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1000, true, BOT_MODE_NONE ); 72 | if(NearbyEnemyHeroes ~= nil) then 73 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 74 | do 75 | if(npcBot:WasRecentlyDamagedByHero(npcEnemy,1)) then 76 | -- got the enemy who attacks me, kill him!-- 77 | StateMachine["EnemyToKill"] = npcEnemy; 78 | ShouldFight = true; 79 | break; 80 | elseif(GetUnitToUnitDistance(npcBot,npcEnemy) < 400) then 81 | StateMachine["EnemyToKill"] = npcEnemy; 82 | ShouldFight = true; 83 | break; 84 | end 85 | end 86 | end 87 | --return ShouldFight; 88 | return false; 89 | end 90 | 91 | 92 | local function ConsiderAttackCreeps(StateMachine) 93 | local npcBot = GetBot(); 94 | 95 | local EnemyCreeps = npcBot:GetNearbyCreeps(1000,true); 96 | local AllyCreeps = npcBot:GetNearbyCreeps(1000,false); 97 | 98 | -- Check if we're already using an ability 99 | if ( TimberSawIsBusy()) then return end; 100 | 101 | local abilityD = npcBot:GetAbilityByName( "shredder_whirling_death" ); 102 | local abilityT = npcBot:GetAbilityByName( "shredder_timber_chain" ); 103 | local abilityC = npcBot:GetAbilityByName( "shredder_chakram" ); 104 | local abilityC2 = npcBot:GetAbilityByName( "shredder_return_chakram" ); 105 | local abilityF = npcBot:GetAbilityByName( "shredder_chakram_2" ); 106 | local abilityF2 = npcBot:GetAbilityByName( "shredder_return_chakram_2" ); 107 | 108 | local DDamage = abilityD:GetAbilityDamage(); 109 | local DRange = 300; 110 | 111 | local TDamage = abilityT:GetAbilityDamage(); 112 | local TCastRange = GetTRange(); 113 | 114 | local CDamage = abilityC:GetAbilityDamage(); 115 | 116 | --If we dont cast ability, just try to last hit. 117 | 118 | local lowest_hp = 100000; 119 | local weakest_creep = nil; 120 | for creep_k,creep in pairs(EnemyCreeps) 121 | do 122 | --npcBot:GetEstimatedDamageToTarget 123 | local creep_name = creep:GetUnitName(); 124 | DotaBotUtility:UpdateCreepHealth(creep); 125 | --print(creep_name); 126 | if(creep:IsAlive()) then 127 | local creep_hp = creep:GetHealth(); 128 | if(lowest_hp > creep_hp) then 129 | lowest_hp = creep_hp; 130 | weakest_creep = creep; 131 | end 132 | end 133 | end 134 | 135 | if(weakest_creep ~= nil and weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 136 | --if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and 137 | local DmgPerSec = DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep); 138 | if DmgPerSec < 0 then 139 | DmgPerSec = 0; 140 | end 141 | local ActualDmg = weakest_creep:GetActualDamage(npcBot:GetBaseDamage() + 24, 142 | DAMAGE_TYPE_PHYSICAL); 143 | if(lowest_hp < ActualDmg 144 | + DmgPerSec 145 | * npcBot:GetAttackPoint() / npcBot:GetAttackSpeed()) then 146 | if(npcBot:GetAttackTarget() == nil) then --StateMachine["attcking creep"] 147 | npcBot:Action_AttackUnit(weakest_creep,true); 148 | return; 149 | elseif(weakest_creep ~= StateMachine["attcking creep"]) then 150 | StateMachine["attcking creep"] = weakest_creep; 151 | npcBot:Action_AttackUnit(weakest_creep,true); 152 | return; 153 | end 154 | return; 155 | elseif(lowest_hp < ActualDmg 156 | + DmgPerSec 157 | * (npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() 158 | + GetUnitToUnitDistance(npcBot,weakest_creep) / npcBot:GetCurrentMovementSpeed())) then 159 | npcBot:Action_MoveToLocation(weakest_creep:GetLocation()); 160 | return; 161 | end 162 | weakest_creep = nil; 163 | 164 | end 165 | 166 | --[[ 167 | for creep_k,creep in pairs(AllyCreeps) 168 | do 169 | --npcBot:GetEstimatedDamageToTarget 170 | local creep_name = creep:GetUnitName(); 171 | DotaBotUtility:UpdateCreepHealth(creep); 172 | --print(creep_name); 173 | if(creep:IsAlive()) then 174 | local creep_hp = creep:GetHealth(); 175 | if(lowest_hp > creep_hp) then 176 | lowest_hp = creep_hp; 177 | weakest_creep = creep; 178 | end 179 | end 180 | end 181 | 182 | if(weakest_creep ~= nil) then 183 | -- if creep's hp is lower than 70(because I don't Know how much is my damadge!!), try to last hit it. 184 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and 185 | lowest_hp < weakest_creep:GetActualDamage( 186 | npcBot:GetBaseDamage(),DAMAGE_TYPE_PHYSICAL) + DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep) 187 | * npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() 188 | and 189 | weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 190 | Attacking_creep = weakest_creep; 191 | npcBot:Action_AttackUnit(Attacking_creep,true); 192 | return; 193 | end 194 | weakest_creep = nil; 195 | 196 | end 197 | 198 | ]] 199 | 200 | 201 | 202 | local pt = GetLaneFrontLocation(DotaBotUtility:GetEnemyTeam(),LANE,-700); 203 | npcBot:Action_MoveToLocation(pt); 204 | return; 205 | 206 | end 207 | 208 | local function ShouldRetreat() 209 | local npcBot = GetBot(); 210 | if(DotaTime() > 360) then 211 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 212 | < TimberSawRetreatHPThreshold or npcBot:GetMana()/npcBot:GetMaxMana() 213 | < TimberSawRetreatMPThreshold; 214 | else 215 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 216 | < TimberSawRetreatHPThreshold; 217 | end 218 | end 219 | 220 | local function IsTowerAttackingMe() 221 | local npcBot = GetBot(); 222 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 223 | local AllyCreeps = npcBot:GetNearbyCreeps(650,false); 224 | if(#NearbyTowers > 0) then 225 | for _,tower in pairs( NearbyTowers) 226 | do 227 | if(GetUnitToUnitDistance(tower,npcBot) < 900 and tower:IsAlive() and #AllyCreeps <= 2) then 228 | print("TimberSaw Attacked by tower"); 229 | return true; 230 | end 231 | end 232 | end 233 | return false; 234 | end 235 | 236 | -------------------local states----------------------------------------------------- 237 | 238 | local function StateIdle(StateMachine) 239 | local npcBot = GetBot(); 240 | if(npcBot:IsAlive() == false) then 241 | return; 242 | end 243 | 244 | local creeps = npcBot:GetNearbyCreeps(1000,true); 245 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 246 | 247 | if (TimberSawIsBusy()) then return end; 248 | 249 | if(ShouldRetreat()) then 250 | StateMachine.State = STATE_RETREAT; 251 | return; 252 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 253 | StateMachine.State = STATE_RUN_AWAY; 254 | return; 255 | elseif(IsInTeamFight()) then 256 | StateMachine.State = STATE_TEAM_FIGHTING; 257 | return; 258 | elseif(npcBot:GetAttackTarget() ~= nil) then 259 | if(npcBot:GetAttackTarget():IsHero()) then 260 | StateMachine["EnemyToKill"] = npcBot:GetAttackTarget(); 261 | print("auto attacking: "..npcBot:GetAttackTarget():GetUnitName()); 262 | StateMachine.State = STATE_FIGHTING; 263 | return; 264 | end 265 | elseif(ConsiderFighting(StateMachine)) then 266 | StateMachine.State = STATE_FIGHTING; 267 | return; 268 | elseif(#creeps > 0 and pt ~= nil) then 269 | StateMachine.State = STATE_ATTACKING_CREEP; 270 | return; 271 | end 272 | 273 | -- buy a tp and get out 274 | if(npcBot:DistanceFromFountain() < 100 and DotaTime() > 0) then 275 | local tpscroll = DotaBotUtility.IsItemAvailable("item_tpscroll"); 276 | if(tpscroll == nil and DotaBotUtility:HasEmptySlot() and npcBot:GetGold() >= GetItemCost("item_tpscroll")) then 277 | print("TimberSaw buying tp"); 278 | npcBot:Action_PurchaseItem("item_tpscroll"); 279 | return; 280 | elseif(tpscroll ~= nil and tpscroll:IsFullyCastable()) then 281 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 282 | if(tower ~= nil) then 283 | npcBot:Action_UseAbilityOnEntity(tpscroll,tower); 284 | return; 285 | end 286 | end 287 | end 288 | 289 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 290 | local AllyCreeps = npcBot:GetNearbyCreeps(800,false); 291 | 292 | for _,tower in pairs(NearbyTowers) 293 | do 294 | local myDistanceToTower = GetUnitToUnitDistance(npcBot,tower); 295 | if(tower:IsAlive() and #AllyCreeps >= 1 and #creeps == 0) then 296 | for _,creep in pairs(AllyCreeps) 297 | do 298 | if(myDistanceToTower > GetUnitToUnitDistance(creep,tower) + 300) then 299 | print("Timber attack tower!!!"); 300 | npcBot:Action_AttackUnit(tower,false); 301 | return; 302 | end 303 | end 304 | end 305 | end 306 | 307 | if(DotaTime() < 20) then 308 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 309 | npcBot:Action_MoveToLocation(tower:GetLocation()); 310 | return; 311 | else 312 | target = DotaBotUtility:GetNearBySuccessorPointOnLane(LANE); 313 | npcBot:Action_AttackMove(target); 314 | return; 315 | end 316 | 317 | 318 | end 319 | 320 | local function StateAttackingCreep(StateMachine) 321 | local npcBot = GetBot(); 322 | if(npcBot:IsAlive() == false) then 323 | StateMachine.State = STATE_IDLE; 324 | return; 325 | end 326 | 327 | local creeps = npcBot:GetNearbyCreeps(1000,true); 328 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 329 | 330 | if (TimberSawIsBusy()) then return end; 331 | 332 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 333 | < TimberSawRetreatHPThreshold) then 334 | StateMachine.State = STATE_RETREAT; 335 | return; 336 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 337 | StateMachine.State = STATE_RUN_AWAY; 338 | elseif(IsInTeamFight()) then 339 | StateMachine.State = STATE_TEAM_FIGHTING; 340 | return; 341 | elseif(ConsiderFighting(StateMachine)) then 342 | StateMachine.State = STATE_FIGHTING; 343 | return; 344 | elseif(#creeps > 0 and pt ~= nil) then 345 | ConsiderAttackCreeps(StateMachine); 346 | return; 347 | else 348 | StateMachine.State = STATE_IDLE; 349 | return; 350 | end 351 | end 352 | 353 | local function StateRetreat(StateMachine) 354 | local npcBot = GetBot(); 355 | if(npcBot:IsAlive() == false) then 356 | StateMachine.State = STATE_IDLE; 357 | return; 358 | end 359 | 360 | if (TimberSawIsBusy()) then return end; 361 | 362 | npcBot:Action_MoveToLocation(Constant.HomePosition()); 363 | 364 | if(npcBot:GetHealth() == npcBot:GetMaxHealth() and npcBot:GetMana() == npcBot:GetMaxMana()) then 365 | StateMachine.State = STATE_IDLE; 366 | return; 367 | end 368 | end 369 | 370 | local function StateGotoComfortPoint(StateMachine) 371 | local npcBot = GetBot(); 372 | if(npcBot:IsAlive() == false) then 373 | StateMachine.State = STATE_IDLE; 374 | return; 375 | end 376 | 377 | local creeps = npcBot:GetNearbyCreeps(1000,true); 378 | --local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 379 | local pt = GetLaneFrontLocation(DotaBotUtility:GetEnemyTeam(),LANE,-500); 380 | 381 | if (TimberSawIsBusy()) then return end; 382 | 383 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 384 | < TimberSawRetreatHPThreshold) then 385 | StateMachine.State = STATE_RETREAT; 386 | return; 387 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 388 | StateMachine.State = STATE_RUN_AWAY; 389 | return; 390 | elseif(IsInTeamFight()) then 391 | StateMachine.State = STATE_TEAM_FIGHTING; 392 | return; 393 | elseif(ConsiderFighting(StateMachine)) then 394 | StateMachine.State = STATE_FIGHTING; 395 | return; 396 | elseif(#creeps > 0 and pt ~= nil) then 397 | local mypos = npcBot:GetLocation(); 398 | --pt[3] = npcBot:GetLocation()[3]; 399 | 400 | --local d = GetUnitToLocationDistance(npcBot,pt); 401 | local d = (npcBot:GetLocation() - pt):Length2D(); 402 | 403 | if (d < 100) then 404 | npcBot:Action_ClearActions(true); 405 | StateMachine.State = STATE_ATTACKING_CREEP; 406 | else 407 | npcBot:Action_MoveToLocation(pt); 408 | end 409 | return; 410 | else 411 | StateMachine.State = STATE_IDLE; 412 | return; 413 | end 414 | 415 | end 416 | 417 | local function StateFighting(StateMachine) 418 | local npcBot = GetBot(); 419 | if(npcBot:IsAlive() == false) then 420 | StateMachine.State = STATE_IDLE; 421 | return; 422 | end 423 | 424 | if (TimberSawIsBusy()) then return end; 425 | 426 | if(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 427 | StateMachine.State = STATE_RUN_AWAY; 428 | return; 429 | elseif(IsInTeamFight()) then 430 | StateMachine.State = STATE_TEAM_FIGHTING; 431 | return; 432 | elseif(not StateMachine["EnemyToKill"]:CanBeSeen() or not StateMachine["EnemyToKill"]:IsAlive()) then 433 | -- lost enemy 434 | print("TimberSaw lost enemy"); 435 | StateMachine.State = STATE_IDLE; 436 | return; 437 | else 438 | 439 | if(StateMachine["EnemyToKill"]:GetHealth() > npcBot:GetHealth()) then 440 | StateMachine.State = STATE_RUN_AWAY; 441 | return; 442 | end 443 | 444 | 445 | if(npcBot:GetAttackTarget() ~= StateMachine["EnemyToKill"]) then 446 | npcBot:Action_AttackUnit(StateMachine["EnemyToKill"],false); 447 | return; 448 | end 449 | 450 | end 451 | end 452 | 453 | local function StateRunAway(StateMachine) 454 | local npcBot = GetBot(); 455 | 456 | if (TimberSawIsBusy()) then return end; 457 | 458 | if(npcBot:IsAlive() == false) then 459 | StateMachine.State = STATE_IDLE; 460 | StateMachine["RunAwayFromLocation"] = nil; 461 | return; 462 | end 463 | 464 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 465 | < TimberSawRetreatHPThreshold) then 466 | StateMachine.State = STATE_RETREAT; 467 | StateMachine["RunAwayFromLocation"] = nil; 468 | return; 469 | end 470 | 471 | local mypos = npcBot:GetLocation(); 472 | 473 | if(StateMachine["RunAwayFromLocation"] == nil) then 474 | --set the target to go back 475 | StateMachine["RunAwayFromLocation"] = npcBot:GetLocation(); 476 | --npcBot:Action_MoveToLocation(Constant.HomePosition()); 477 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 478 | return; 479 | else 480 | if(GetUnitToLocationDistance(npcBot,StateMachine["RunAwayFromLocation"]) > 400) then 481 | -- we are far enough from tower,return to normal state. 482 | StateMachine["RunAwayFromLocation"] = nil; 483 | StateMachine.State = STATE_IDLE; 484 | return; 485 | else 486 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 487 | return; 488 | end 489 | end 490 | end 491 | 492 | local function StateTeamFighting(StateMachine) 493 | local npcBot = GetBot(); 494 | if(npcBot:IsAlive() == false) then 495 | StateMachine.State = STATE_IDLE; 496 | return; 497 | end 498 | 499 | if (TimberSawIsBusy()) then return end; 500 | 501 | if(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 502 | StateMachine.State = STATE_RUN_AWAY; 503 | elseif(not StateMachine["EnemyToKill"]:CanBeSeen() or not StateMachine["EnemyToKill"]:IsAlive()) then 504 | -- lost enemy 505 | print("lost enemy"); 506 | StateMachine.State = STATE_IDLE; 507 | return; 508 | else 509 | 510 | StateMachine.State = STATE_IDLE; 511 | return; 512 | 513 | end 514 | end 515 | 516 | local StateMachine = {}; 517 | StateMachine["State"] = STATE_IDLE; 518 | StateMachine[STATE_IDLE] = StateIdle; 519 | StateMachine[STATE_ATTACKING_CREEP] = StateAttackingCreep; 520 | StateMachine[STATE_RETREAT] = StateRetreat; 521 | StateMachine[STATE_GOTO_COMFORT_POINT] = StateGotoComfortPoint; 522 | StateMachine[STATE_FIGHTING] = StateFighting; 523 | StateMachine[STATE_RUN_AWAY] = StateRunAway; 524 | StateMachine[STATE_TEAM_FIGHTING] = StateTeamFighting; 525 | StateMachine["totalLevelOfAbilities"] = 0; 526 | 527 | local TimberSawAbilityMap = { 528 | [1] = "shredder_reactive_armor", 529 | [2] = "shredder_whirling_death", 530 | [3] = "shredder_reactive_armor", 531 | [4] = "shredder_timber_chain", 532 | [5] = "shredder_reactive_armor", 533 | [6] = "shredder_chakram", 534 | [7] = "shredder_reactive_armor", 535 | [8] = "shredder_whirling_death", 536 | [9] = "shredder_whirling_death", 537 | [10] = "special_bonus_hp_150", 538 | [11] = "shredder_whirling_death", 539 | [12] = "shredder_chakram", 540 | [13] = "shredder_timber_chain", 541 | [14] = "shredder_timber_chain", 542 | [15] = "special_bonus_hp_regen_14", 543 | [16] = "shredder_timber_chain", 544 | [18] = "shredder_chakram", 545 | [20] = "special_bonus_spell_amplify_5", 546 | [25] = "special_bonus_strength_20" 547 | }; 548 | 549 | local TimberSawDoneLvlupAbility = {}; 550 | 551 | for lvl,_ in pairs(TimberSawAbilityMap) 552 | do 553 | TimberSawDoneLvlupAbility[lvl] = false; 554 | end 555 | 556 | local function ThinkLvlupAbility(StateMachine) 557 | -- Is there a bug? http://dev.dota2.com/showthread.php?t=274436 558 | local npcBot = GetBot(); 559 | 560 | 561 | local HeroLevel = PerryGetHeroLevel(); 562 | if(TimberSawDoneLvlupAbility[HeroLevel] == false) then 563 | npcBot:Action_LevelAbility(TimberSawAbilityMap[HeroLevel]); 564 | --TimberSawDoneLvlupAbility[HeroLevel] = true; 565 | end 566 | end 567 | 568 | local PrevState = "none"; 569 | 570 | function Think( ) 571 | -- Think this item( ... ) 572 | --update 573 | 574 | local npcBot = GetBot(); 575 | DotaBotUtility:CourierThink(); 576 | ThinkLvlupAbility(StateMachine); 577 | 578 | StateMachine[StateMachine.State](StateMachine); 579 | 580 | if(PrevState ~= StateMachine.State) then 581 | print("TimberSaw bot STATE: "..StateMachine.State); 582 | PrevState = StateMachine.State; 583 | end 584 | 585 | end 586 | -------------------------------------------------------------------------------- /bot_lina.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StateMachine is a table 3 | the key "STATE" stores the STATE of Lina 4 | other key value pairs: key is the string of state value is the function of the State. 5 | 6 | each frame DOTA2 will call Think() 7 | Then Think() will call the function of current state. 8 | ]] 9 | 10 | local ValveAbilityUse = require(GetScriptDirectory().."/dev/ability_item_usage_lina"); 11 | local Constant = require(GetScriptDirectory().."/dev/constant_each_side"); 12 | local DotaBotUtility = require(GetScriptDirectory().."/utility"); 13 | 14 | local STATE_IDLE = "STATE_IDLE"; 15 | local STATE_ATTACKING_CREEP = "STATE_ATTACKING_CREEP"; 16 | local STATE_KILL = "STATE_KILL"; 17 | local STATE_RETREAT = "STATE_RETREAT"; 18 | local STATE_FARMING = "STATE_FARMING"; 19 | local STATE_GOTO_COMFORT_POINT = "STATE_GOTO_COMFORT_POINT"; 20 | local STATE_FIGHTING = "STATE_FIGHTING"; 21 | local STATE_RUN_AWAY = "STATE_RUN_AWAY"; 22 | 23 | local LinaRetreatHPThreshold = 0.3; 24 | local LinaRetreatMPThreshold = 0.2; 25 | 26 | local STATE = STATE_IDLE; 27 | 28 | LANE = LANE_BOT 29 | 30 | ----------------- local utility functions reordered for lua local visibility-------- 31 | --Perry's code from http://dev.dota2.com/showthread.php?t=274837 32 | local function PerryGetHeroLevel() 33 | local npcBot = GetBot(); 34 | local respawnTable = {8, 10, 12, 14, 16, 26, 28, 30, 32, 34, 36, 46, 48, 50, 52, 54, 56, 66, 70, 74, 78, 82, 86, 90, 100}; 35 | local nRespawnTime = npcBot:GetRespawnTime() +1 -- It gives 1 second lower values. 36 | for k,v in pairs (respawnTable) do 37 | if v == nRespawnTime then 38 | return k 39 | end 40 | end 41 | end 42 | 43 | 44 | local function TryToUpgradeAbility(AbilityName) 45 | local npcBot = GetBot(); 46 | local ability = npcBot:GetAbilityByName(AbilityName); 47 | if ability:CanAbilityBeUpgraded() then 48 | ability:UpgradeAbility(); 49 | return true; 50 | end 51 | return false; 52 | end 53 | 54 | local function ConsiderFighting(StateMachine) 55 | local ShouldFight = false; 56 | local npcBot = GetBot(); 57 | 58 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1000, true, BOT_MODE_NONE ); 59 | if(NearbyEnemyHeroes ~= nil) then 60 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 61 | do 62 | if(npcBot:WasRecentlyDamagedByHero(npcEnemy,1)) then 63 | -- got the enemy who attacks me, kill him!-- 64 | StateMachine["EnemyToKill"] = npcEnemy; 65 | ShouldFight = true; 66 | break; 67 | elseif(GetUnitToUnitDistance(npcBot,npcEnemy) < 500) then 68 | StateMachine["EnemyToKill"] = npcEnemy; 69 | ShouldFight = true; 70 | break; 71 | end 72 | end 73 | end 74 | return ShouldFight; 75 | end 76 | 77 | 78 | local function ConsiderAttackCreeps(StateMachine) 79 | -- there are creeps try to attack them -- 80 | --print("ConsiderAttackCreeps"); 81 | local npcBot = GetBot(); 82 | 83 | local EnemyCreeps = npcBot:GetNearbyCreeps(1000,true); 84 | local AllyCreeps = npcBot:GetNearbyCreeps(1000,false); 85 | 86 | -- Check if we're already using an ability 87 | if ( npcBot:IsUsingAbility() ) then return end; 88 | 89 | local abilityLSA = npcBot:GetAbilityByName( "lina_light_strike_array" ); 90 | local abilityDS = npcBot:GetAbilityByName( "lina_dragon_slave" ); 91 | local abilityLB = npcBot:GetAbilityByName( "lina_laguna_blade" ); 92 | 93 | -- Consider using each ability 94 | 95 | local castLBDesire, castLBTarget = ConsiderLagunaBlade(abilityLB); 96 | local castLSADesire, castLSALocation = ConsiderLightStrikeArray(abilityLSA); 97 | local castDSDesire, castDSLocation = ConsiderDragonSlave(abilityDS); 98 | 99 | if ( castLBDesire > castLSADesire and castLBDesire > castDSDesire ) 100 | then 101 | npcBot:Action_UseAbilityOnEntity( abilityLB, castLBTarget ); 102 | return; 103 | end 104 | 105 | if ( castLSADesire > 0 ) 106 | then 107 | npcBot:Action_UseAbilityOnLocation( abilityLSA, castLSALocation ); 108 | return; 109 | end 110 | 111 | if ( castDSDesire > 0 ) 112 | then 113 | npcBot:Action_UseAbilityOnLocation( abilityDS, castDSLocation ); 114 | return; 115 | end 116 | 117 | --print("desires: " .. castLBDesire .. " " .. castLSADesire .. " " .. castDSDesire); 118 | 119 | --If we dont cast ability, just try to last hit. 120 | 121 | local lowest_hp = 100000; 122 | local weakest_creep = nil; 123 | for creep_k,creep in pairs(EnemyCreeps) 124 | do 125 | --npcBot:GetEstimatedDamageToTarget 126 | local creep_name = creep:GetUnitName(); 127 | DotaBotUtility:UpdateCreepHealth(creep); 128 | --print(creep_name); 129 | if(creep:IsAlive()) then 130 | local creep_hp = creep:GetHealth(); 131 | if(lowest_hp > creep_hp) then 132 | lowest_hp = creep_hp; 133 | weakest_creep = creep; 134 | end 135 | end 136 | end 137 | 138 | if(weakest_creep ~= nil and weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 139 | if(lowest_hp < weakest_creep:GetActualDamage( 140 | npcBot:GetBaseDamage(),DAMAGE_TYPE_PHYSICAL) 141 | + DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep) 142 | * (npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() 143 | + GetUnitToUnitDistance(npcBot,weakest_creep) / 1000)) then 144 | if(npcBot:GetAttackTarget() == nil) then --StateMachine["attcking creep"] 145 | npcBot:Action_AttackUnit(weakest_creep,false); 146 | return; 147 | elseif(weakest_creep ~= StateMachine["attcking creep"]) then 148 | StateMachine["attcking creep"] = weakest_creep; 149 | npcBot:Action_AttackUnit(weakest_creep,true); 150 | return; 151 | end 152 | else 153 | -- simulation of human attack and stop 154 | if(npcBot:GetCurrentActionType() == BOT_ACTION_TYPE_ATTACK) then 155 | npcBot:Action_ClearActions(true); 156 | return; 157 | else 158 | npcBot:Action_AttackUnit(weakest_creep,false); 159 | return; 160 | end 161 | end 162 | weakest_creep = nil; 163 | 164 | end 165 | 166 | for creep_k,creep in pairs(AllyCreeps) 167 | do 168 | --npcBot:GetEstimatedDamageToTarget 169 | local creep_name = creep:GetUnitName(); 170 | DotaBotUtility:UpdateCreepHealth(creep); 171 | --print(creep_name); 172 | if(creep:IsAlive()) then 173 | local creep_hp = creep:GetHealth(); 174 | if(lowest_hp > creep_hp) then 175 | lowest_hp = creep_hp; 176 | weakest_creep = creep; 177 | end 178 | end 179 | end 180 | 181 | if(weakest_creep ~= nil) then 182 | -- if creep's hp is lower than 70(because I don't Know how much is my damadge!!), try to last hit it. 183 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and 184 | lowest_hp < weakest_creep:GetActualDamage( 185 | npcBot:GetBaseDamage(),DAMAGE_TYPE_PHYSICAL) + DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep) 186 | * (npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() 187 | + GetUnitToUnitDistance(npcBot,weakest_creep) / 1000) 188 | and 189 | weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 190 | Attacking_creep = weakest_creep; 191 | npcBot:Action_AttackUnit(Attacking_creep,true); 192 | return; 193 | end 194 | weakest_creep = nil; 195 | 196 | end 197 | 198 | -- nothing to do , try to attack heros 199 | 200 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 700, true, BOT_MODE_NONE ); 201 | if(NearbyEnemyHeroes ~= nil) then 202 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 203 | do 204 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget())) then 205 | npcBot:Action_AttackUnit(npcEnemy,false); 206 | return; 207 | end 208 | end 209 | end 210 | 211 | -- hit creeps to push 212 | local TimeNow = DotaTime(); 213 | for creep_k,creep in pairs(EnemyCreeps) 214 | do 215 | local creep_name = creep:GetUnitName(); 216 | --print(creep_name); 217 | if(creep:IsAlive()) then 218 | if(TimeNow > 600) then 219 | npcBot:Action_AttackUnit(creep,false); 220 | return; 221 | end 222 | local creep_hp = creep:GetHealth(); 223 | if(lowest_hp > creep_hp) then 224 | lowest_hp = creep_hp; 225 | weakest_creep = creep; 226 | end 227 | end 228 | end 229 | 230 | end 231 | 232 | local function ShouldRetreat() 233 | local npcBot = GetBot(); 234 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 235 | < LinaRetreatHPThreshold or npcBot:GetMana()/npcBot:GetMaxMana() 236 | < LinaRetreatMPThreshold; 237 | end 238 | 239 | local function IsTowerAttackingMe() 240 | local npcBot = GetBot(); 241 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 242 | local AllyCreeps = npcBot:GetNearbyCreeps(650,false); 243 | if(#NearbyTowers > 0) then 244 | for _,tower in pairs( NearbyTowers) 245 | do 246 | if(GetUnitToUnitDistance(tower,npcBot) < 900 and tower:IsAlive() and #AllyCreeps <= 2) then 247 | print("Lina Attacked by tower"); 248 | return true; 249 | end 250 | end 251 | end 252 | return false; 253 | end 254 | 255 | -------------------local states----------------------------------------------------- 256 | 257 | local function StateIdle(StateMachine) 258 | local npcBot = GetBot(); 259 | if(npcBot:IsAlive() == false) then 260 | return; 261 | end 262 | 263 | local creeps = npcBot:GetNearbyCreeps(1000,true); 264 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 265 | 266 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 267 | 268 | if(ShouldRetreat()) then 269 | StateMachine.State = STATE_RETREAT; 270 | return; 271 | elseif(IsTowerAttackingMe()) then 272 | StateMachine.State = STATE_RUN_AWAY; 273 | return; 274 | elseif(npcBot:GetAttackTarget() ~= nil) then 275 | if(npcBot:GetAttackTarget():IsHero()) then 276 | StateMachine["EnemyToKill"] = npcBot:GetAttackTarget(); 277 | print("auto attacking: "..npcBot:GetAttackTarget():GetUnitName()); 278 | StateMachine.State = STATE_FIGHTING; 279 | return; 280 | end 281 | elseif(ConsiderFighting(StateMachine)) then 282 | StateMachine.State = STATE_FIGHTING; 283 | return; 284 | elseif(#creeps > 0 and pt ~= nil) then 285 | local mypos = npcBot:GetLocation(); 286 | 287 | local d = GetUnitToLocationDistance(npcBot,pt); 288 | if(d > 250) then 289 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 290 | else 291 | StateMachine.State = STATE_ATTACKING_CREEP; 292 | end 293 | return; 294 | end 295 | 296 | -- buy a tp and get out 297 | if(npcBot:DistanceFromFountain() < 100 and DotaTime() > 0) then 298 | local tpscroll = DotaBotUtility.IsItemAvailable("item_tpscroll"); 299 | if(tpscroll == nil and DotaBotUtility:HasEmptySlot() and npcBot:GetGold() >= GetItemCost("item_tpscroll")) then 300 | print("buying tp"); 301 | npcBot:Action_PurchaseItem("item_tpscroll"); 302 | return; 303 | elseif(tpscroll ~= nil and tpscroll:IsFullyCastable()) then 304 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 305 | if(tower ~= nil) then 306 | npcBot:Action_UseAbilityOnEntity(tpscroll,tower); 307 | return; 308 | end 309 | end 310 | end 311 | 312 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 313 | local AllyCreeps = npcBot:GetNearbyCreeps(800,false); 314 | 315 | for _,tower in pairs(NearbyTowers) 316 | do 317 | local myDistanceToTower = GetUnitToUnitDistance(npcBot,tower); 318 | if(tower:IsAlive() and #AllyCreeps >= 1 and #creeps == 0) then 319 | for _,creep in pairs(AllyCreeps) 320 | do 321 | if(myDistanceToTower > GetUnitToUnitDistance(creep,tower) + 300) then 322 | print("Lina attack tower!!!"); 323 | npcBot:Action_AttackUnit(tower,false); 324 | return; 325 | end 326 | end 327 | end 328 | end 329 | 330 | if(DotaTime() < 20) then 331 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 332 | npcBot:Action_MoveToLocation(tower:GetLocation()); 333 | return; 334 | else 335 | target = DotaBotUtility:GetNearBySuccessorPointOnLane(LANE); 336 | npcBot:Action_AttackMove(target); 337 | return; 338 | end 339 | 340 | 341 | end 342 | 343 | local function StateAttackingCreep(StateMachine) 344 | local npcBot = GetBot(); 345 | if(npcBot:IsAlive() == false) then 346 | StateMachine.State = STATE_IDLE; 347 | return; 348 | end 349 | 350 | local creeps = npcBot:GetNearbyCreeps(1000,true); 351 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 352 | 353 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 354 | 355 | if(ShouldRetreat()) then 356 | StateMachine.State = STATE_RETREAT; 357 | return; 358 | elseif(IsTowerAttackingMe()) then 359 | StateMachine.State = STATE_RUN_AWAY; 360 | elseif(ConsiderFighting(StateMachine)) then 361 | StateMachine.State = STATE_FIGHTING; 362 | return; 363 | elseif(#creeps > 0 and pt ~= nil) then 364 | local mypos = npcBot:GetLocation(); 365 | local d = GetUnitToLocationDistance(npcBot,pt); 366 | if(d > 250) then 367 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 368 | else 369 | ConsiderAttackCreeps(StateMachine); 370 | end 371 | return; 372 | else 373 | StateMachine.State = STATE_IDLE; 374 | return; 375 | end 376 | end 377 | 378 | local function StateRetreat(StateMachine) 379 | local npcBot = GetBot(); 380 | if(npcBot:IsAlive() == false) then 381 | StateMachine.State = STATE_IDLE; 382 | return; 383 | end 384 | 385 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 386 | --[[ 387 | I don't know how to Create a object of Location so I borrow one from GetLocation() 388 | 389 | Got Vector from marko.polo at http://dev.dota2.com/showthread.php?t=274301 390 | ]] 391 | npcBot:Action_MoveToLocation(Constant.HomePosition()); 392 | 393 | if(npcBot:GetHealth() == npcBot:GetMaxHealth() and npcBot:GetMana() == npcBot:GetMaxMana()) then 394 | StateMachine.State = STATE_IDLE; 395 | return; 396 | end 397 | end 398 | 399 | local function StateGotoComfortPoint(StateMachine) 400 | local npcBot = GetBot(); 401 | if(npcBot:IsAlive() == false) then 402 | StateMachine.State = STATE_IDLE; 403 | return; 404 | end 405 | 406 | local creeps = npcBot:GetNearbyCreeps(1000,true); 407 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 408 | 409 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 410 | 411 | if(ShouldRetreat()) then 412 | StateMachine.State = STATE_RETREAT; 413 | return; 414 | elseif(IsTowerAttackingMe()) then 415 | StateMachine.State = STATE_RUN_AWAY; 416 | elseif(ConsiderFighting(StateMachine)) then 417 | StateMachine.State = STATE_FIGHTING; 418 | return; 419 | elseif(#creeps > 0 and pt ~= nil) then 420 | local mypos = npcBot:GetLocation(); 421 | --pt[3] = npcBot:GetLocation()[3]; 422 | 423 | --local d = GetUnitToLocationDistance(npcBot,pt); 424 | local d = (npcBot:GetLocation() - pt):Length2D(); 425 | 426 | if (d < 200) then 427 | StateMachine.State = STATE_ATTACKING_CREEP; 428 | else 429 | npcBot:Action_MoveToLocation(pt); 430 | end 431 | return; 432 | else 433 | StateMachine.State = STATE_IDLE; 434 | return; 435 | end 436 | 437 | end 438 | 439 | local function StateFighting(StateMachine) 440 | local npcBot = GetBot(); 441 | if(npcBot:IsAlive() == false) then 442 | StateMachine["cyclone dota time"] = nil; 443 | StateMachine.State = STATE_IDLE; 444 | return; 445 | end 446 | 447 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 448 | 449 | if(IsTowerAttackingMe()) then 450 | StateMachine["cyclone dota time"] = nil; 451 | StateMachine.State = STATE_RUN_AWAY; 452 | elseif(not StateMachine["EnemyToKill"]:CanBeSeen() or not StateMachine["EnemyToKill"]:IsAlive()) then 453 | -- lost enemy 454 | print("lost enemy"); 455 | StateMachine["cyclone dota time"] = nil; 456 | StateMachine.State = STATE_IDLE; 457 | return; 458 | else 459 | 460 | local cyclone = DotaBotUtility.IsItemAvailable("item_cyclone"); 461 | 462 | if(cyclone ~= nil) then 463 | if(ConsiderCyclone(cyclone,StateMachine["EnemyToKill"])) then 464 | npcBot:Action_UseAbilityOnEntity(cyclone,StateMachine["EnemyToKill"]); 465 | StateMachine["cyclone dota time"] = GameTime(); 466 | return; 467 | elseif(cyclone:IsFullyCastable()) then 468 | -- move closer to cast cyclone 469 | npcBot:Action_MoveToLocation(StateMachine["EnemyToKill"]:GetLocation()); 470 | return; 471 | end 472 | end 473 | 474 | local abilityLSA = npcBot:GetAbilityByName( "lina_light_strike_array" ); 475 | local abilityDS = npcBot:GetAbilityByName( "lina_dragon_slave" ); 476 | local abilityLB = npcBot:GetAbilityByName( "lina_laguna_blade" ); 477 | 478 | local Lina_Cyclone_LSA_Combo_Delay = 1.5; 479 | 480 | if(StateMachine["cyclone dota time"] ~= nil) then 481 | -- Consider LSA after cyclone 482 | -- Cast LSA 0.5s before cyclone ends 483 | if(abilityLSA:IsFullyCastable() and GameTime() - StateMachine["cyclone dota time"] > Lina_Cyclone_LSA_Combo_Delay) then 484 | if(DotaBotUtility.AbilityOutOfRange4Unit(abilityLSA,StateMachine["EnemyToKill"])) then 485 | -- move closer to cast LSA 486 | npcBot:Action_MoveToLocation(StateMachine["EnemyToKill"]:GetLocation()); 487 | return; 488 | else 489 | npcBot:Action_UseAbilityOnLocation( abilityLSA, StateMachine["EnemyToKill"]:GetLocation()); 490 | StateMachine["cyclone dota time"] = nil; 491 | return; 492 | end 493 | elseif(abilityLSA:IsFullyCastable() and GameTime() - StateMachine["cyclone dota time"] < Lina_Cyclone_LSA_Combo_Delay) then 494 | if(DotaBotUtility.AbilityOutOfRange4Unit(abilityLSA,StateMachine["EnemyToKill"])) then 495 | -- move closer to cast LSA 496 | npcBot:Action_MoveToLocation(StateMachine["EnemyToKill"]:GetLocation()); 497 | return; 498 | end 499 | end 500 | end 501 | 502 | 503 | -- Consider using each ability 504 | 505 | local castLBDesire, castLBTarget = ConsiderLagunaBlade(abilityLB); 506 | local castLSADesire, castLSALocation = ConsiderLightStrikeArrayFighting(abilityLSA,StateMachine["EnemyToKill"]); 507 | local castDSDesire, castDSLocation = ConsiderDragonSlaveFighting(abilityDS,StateMachine["EnemyToKill"]); 508 | 509 | if ( castLBDesire > 0 ) 510 | then 511 | npcBot:Action_UseAbilityOnEntity( abilityLB, castLBTarget ); 512 | return; 513 | end 514 | 515 | if ( castLSADesire > 0 ) 516 | then 517 | npcBot:Action_UseAbilityOnLocation( abilityLSA, castLSALocation ); 518 | return; 519 | end 520 | 521 | if ( castDSDesire > 0 ) 522 | then 523 | npcBot:Action_UseAbilityOnLocation( abilityDS, castDSLocation ); 524 | return; 525 | end 526 | 527 | -- LSA is castable but out of range, get closer!-- 528 | if(abilityLSA:IsFullyCastable() and CanCastLightStrikeArrayOnTarget(StateMachine["EnemyToKill"])) then 529 | npcBot:Action_MoveToLocation(StateMachine["EnemyToKill"]:GetLocation()); 530 | return; 531 | end 532 | 533 | if(not abilityLSA:IsFullyCastable() and 534 | not abilityDS:IsFullyCastable() or StateMachine["EnemyToKill"]:IsMagicImmune()) then 535 | local extraHP = 0; 536 | if(abilityLB:IsFullyCastable()) then 537 | local LBnDamage = abilityLB:GetSpecialValueInt( "damage" ); 538 | local LBeDamageType = npcBot:HasScepter() and DAMAGE_TYPE_PURE or DAMAGE_TYPE_MAGICAL; 539 | extraHP = StateMachine["EnemyToKill"]:GetActualDamage(LBnDamage,LBeDamageType); 540 | end 541 | 542 | if(StateMachine["EnemyToKill"]:GetHealth() - extraHP > npcBot:GetHealth()) then 543 | StateMachine.State = STATE_RUN_AWAY; 544 | return; 545 | end 546 | end 547 | 548 | 549 | if(npcBot:GetAttackTarget() ~= StateMachine["EnemyToKill"]) then 550 | npcBot:Action_AttackUnit(StateMachine["EnemyToKill"],false); 551 | end 552 | 553 | end 554 | end 555 | 556 | local function StateRunAway(StateMachine) 557 | local npcBot = GetBot(); 558 | 559 | if(npcBot:IsAlive() == false) then 560 | StateMachine.State = STATE_IDLE; 561 | StateMachine["RunAwayFromLocation"] = nil; 562 | return; 563 | end 564 | 565 | if ( npcBot:IsUsingAbility() or npcBot:IsChanneling()) then return end; 566 | 567 | if(ShouldRetreat()) then 568 | StateMachine.State = STATE_RETREAT; 569 | StateMachine["RunAwayFromLocation"] = nil; 570 | return; 571 | end 572 | 573 | local mypos = npcBot:GetLocation(); 574 | 575 | if(StateMachine["RunAwayFromLocation"] == nil) then 576 | --set the target to go back 577 | StateMachine["RunAwayFromLocation"] = npcBot:GetLocation(); 578 | --npcBot:Action_MoveToLocation(Constant.HomePosition()); 579 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 580 | return; 581 | else 582 | if(GetUnitToLocationDistance(npcBot,StateMachine["RunAwayFromLocation"]) > 400) then 583 | -- we are far enough from tower,return to normal state. 584 | StateMachine["RunAwayFromLocation"] = nil; 585 | StateMachine.State = STATE_IDLE; 586 | return; 587 | else 588 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 589 | return; 590 | end 591 | end 592 | end 593 | 594 | -- useless now ignore it 595 | local function StateFarming(StateMachine) 596 | local npcBot = GetBot(); 597 | if(npcBot:IsAlive() == false) then 598 | StateMachine.State = STATE_IDLE; 599 | return; 600 | end 601 | end 602 | 603 | local StateMachine = {}; 604 | StateMachine["State"] = STATE_IDLE; 605 | StateMachine[STATE_IDLE] = StateIdle; 606 | StateMachine[STATE_ATTACKING_CREEP] = StateAttackingCreep; 607 | StateMachine[STATE_RETREAT] = StateRetreat; 608 | StateMachine[STATE_GOTO_COMFORT_POINT] = StateGotoComfortPoint; 609 | StateMachine[STATE_FIGHTING] = StateFighting; 610 | StateMachine[STATE_RUN_AWAY] = StateRunAway; 611 | StateMachine["totalLevelOfAbilities"] = 0; 612 | 613 | 614 | local LinaAbilityMap = { 615 | [1] = "lina_dragon_slave", 616 | [2] = "lina_light_strike_array", 617 | [3] = "lina_dragon_slave", 618 | [4] = "lina_fiery_soul", 619 | [5] = "lina_dragon_slave", 620 | [6] = "lina_laguna_blade", 621 | [7] = "lina_dragon_slave", 622 | [8] = "lina_light_strike_array", 623 | [9] = "lina_light_strike_array", 624 | [10] = "special_bonus_mp_250", 625 | [11] = "lina_light_strike_array", 626 | [12] = "lina_laguna_blade", 627 | [13] = "lina_fiery_soul", 628 | [14] = "lina_fiery_soul", 629 | [15] = "special_bonus_cast_range_125", 630 | [16] = "lina_fiery_soul", 631 | [18] = "lina_laguna_blade", 632 | [20] = "special_bonus_attack_range_150", 633 | [25] = "special_bonus_unique_lina_1" 634 | }; 635 | 636 | local LinaDoneLvlupAbility = {}; 637 | 638 | for lvl,_ in pairs(LinaAbilityMap) 639 | do 640 | LinaDoneLvlupAbility[lvl] = false; 641 | end 642 | 643 | local function ThinkLvlupAbility(StateMachine) 644 | -- Is there a bug? http://dev.dota2.com/showthread.php?t=274436 645 | local npcBot = GetBot(); 646 | 647 | 648 | local HeroLevel = PerryGetHeroLevel(); 649 | if(LinaDoneLvlupAbility[HeroLevel] == false) then 650 | npcBot:Action_LevelAbility(LinaAbilityMap[HeroLevel]); 651 | --LinaDoneLvlupAbility[HeroLevel] = true; 652 | end 653 | end 654 | 655 | local PrevState = "none"; 656 | 657 | function Think( ) 658 | -- Think this item( ... ) 659 | --update 660 | 661 | local npcBot = GetBot(); 662 | DotaBotUtility:CourierThink(); 663 | ThinkLvlupAbility(StateMachine); 664 | StateMachine[StateMachine.State](StateMachine); 665 | 666 | if(PrevState ~= StateMachine.State) then 667 | print("Lina bot STATE: "..StateMachine.State); 668 | PrevState = StateMachine.State; 669 | end 670 | 671 | if(DotaTime() > 600) then 672 | LANE = LANE_MID; 673 | end 674 | 675 | end 676 | -------------------------------------------------------------------------------- /bot_tinker.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StateMachine is a table 3 | the key "STATE" stores the STATE of Lina 4 | other key value pairs: key is the string of state value is the function of the State. 5 | 6 | each frame DOTA2 will call Think() 7 | Then Think() will call the function of current state. 8 | ]] 9 | 10 | local Constant = require(GetScriptDirectory().."/dev/constant_each_side"); 11 | local DotaBotUtility = require(GetScriptDirectory().."/utility"); 12 | 13 | local STATE_IDLE = "STATE_IDLE"; 14 | local STATE_ATTACKING_CREEP = "STATE_ATTACKING_CREEP"; 15 | local STATE_KILL = "STATE_KILL"; 16 | local STATE_RETREAT = "STATE_RETREAT"; 17 | local STATE_FARMING = "STATE_FARMING"; 18 | local STATE_GOTO_COMFORT_POINT = "STATE_GOTO_COMFORT_POINT"; 19 | local STATE_FIGHTING = "STATE_FIGHTING"; 20 | local STATE_RUN_AWAY = "STATE_RUN_AWAY"; 21 | local STATE_TEAM_FIGHTING = "STATE_TEAM_FIGHTING"; 22 | 23 | local TinkerRetreatHPThreshold = 0.3; 24 | local TinkerRetreatMPThreshold = 0.2; 25 | 26 | local STATE = STATE_IDLE; 27 | 28 | MoMradius = 900; 29 | 30 | LANE = LANE_MID; 31 | 32 | local function TinkerIsBusy() 33 | local npcBot = GetBot(); 34 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 35 | 36 | if(LastRearmTime ~= nil) then 37 | if(GameTime() - LastRearmTime < abilityRearm:GetChannelTime() + 0.2) then 38 | return true; 39 | end 40 | end 41 | 42 | local busy = npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:GetCurrentActionType() == BOT_ACTION_TYPE_USE_ABILITY; 43 | 44 | return busy; 45 | end 46 | 47 | local function CanCastOnTarget( npcTarget ) 48 | return npcTarget:CanBeSeen() and not npcTarget:IsMagicImmune() and not npcTarget:IsInvulnerable(); 49 | end 50 | 51 | local function TinkerConsiderRearm() 52 | local npcBot = GetBot(); 53 | local abilityLaser = npcBot:GetAbilityByName( "tinker_laser" ); 54 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 55 | local abilityMoM = npcBot:GetAbilityByName( "tinker_march_of_the_machines" ); 56 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 57 | if(abilityRearm == nil) then 58 | return false; 59 | end 60 | 61 | if(LastRearmTime ~= nil) then 62 | if(GameTime() - LastRearmTime < abilityRearm:GetChannelTime() + 0.2) then 63 | return false; 64 | end 65 | end 66 | 67 | local boot_ready = true; 68 | 69 | local travel_boots = DotaBotUtility.IsItemAvailable("item_travel_boots"); 70 | 71 | if(travel_boots ~= nil) then 72 | boot_ready = travel_boots:IsCooldownReady(); 73 | end 74 | 75 | return not abilityMoM:IsCooldownReady() 76 | or not abilityMissile:IsCooldownReady() 77 | or not abilityLaser:IsCooldownReady() 78 | or not boot_ready; 79 | end 80 | 81 | local function SoulRingReArm() 82 | local npcBot = GetBot(); 83 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 84 | if(TinkerConsiderRearm() and abilityRearm:IsFullyCastable()) then 85 | local soul_ring = DotaBotUtility.IsItemAvailable("item_soul_ring"); 86 | if(soul_ring ~= nil and soul_ring:IsFullyCastable() 87 | and npcBot:GetHealth() / npcBot:GetMaxHealth() > 0.5) then 88 | npcBot:Action_UseAbility(soul_ring); 89 | return; 90 | else 91 | npcBot:Action_UseAbility(abilityRearm); 92 | LastRearmTime = GameTime(); 93 | return; 94 | end 95 | end 96 | end 97 | 98 | local function IsInTeamFight() 99 | local npcBot = GetBot(); 100 | local EnemyCount = 0; 101 | 102 | local EnemyBots = DotaBotUtility:GetEnemyBots(); 103 | local EnemyTeam = DotaBotUtility:GetEnemyTeam(); 104 | 105 | for _,idx in pairs(EnemyBots) 106 | do 107 | local BotHandle = GetTeamMember(EnemyTeam,idx); 108 | if(BotHandle ~= nil and BotHandle:IsAlive() and BotHandle:CanBeSeen() 109 | and GetUnitToUnitDistance(BotHandle,npcBot) < 1000) then 110 | EnemyCount = EnemyCount + 1; 111 | end 112 | end 113 | 114 | if(EnemyCount >= 2)then 115 | return true; 116 | else 117 | return false; 118 | end 119 | end 120 | 121 | ----------------- local utility functions reordered for lua local visibility-------- 122 | --Perry's code from http://dev.dota2.com/showthread.php?t=274837 123 | local function PerryGetHeroLevel() 124 | local npcBot = GetBot(); 125 | local respawnTable = {8, 10, 12, 14, 16, 26, 28, 30, 32, 34, 36, 46, 48, 50, 52, 54, 56, 66, 70, 74, 78, 82, 86, 90, 100}; 126 | local nRespawnTime = npcBot:GetRespawnTime() +1 -- It gives 1 second lower values. 127 | for k,v in pairs (respawnTable) do 128 | if v == nRespawnTime then 129 | return k 130 | end 131 | end 132 | end 133 | 134 | local function ConsiderFighting(StateMachine) 135 | local ShouldFight = false; 136 | local npcBot = GetBot(); 137 | 138 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1000, true, BOT_MODE_NONE ); 139 | if(NearbyEnemyHeroes ~= nil) then 140 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 141 | do 142 | if(npcBot:WasRecentlyDamagedByHero(npcEnemy,1)) then 143 | -- got the enemy who attacks me, kill him!-- 144 | StateMachine["EnemyToKill"] = npcEnemy; 145 | ShouldFight = true; 146 | break; 147 | elseif(GetUnitToUnitDistance(npcBot,npcEnemy) < 400) then 148 | StateMachine["EnemyToKill"] = npcEnemy; 149 | ShouldFight = true; 150 | break; 151 | end 152 | end 153 | end 154 | return ShouldFight; 155 | end 156 | 157 | 158 | local function ConsiderAttackCreeps(StateMachine) 159 | -- there are creeps try to attack them -- 160 | --print("ConsiderAttackCreeps"); 161 | local npcBot = GetBot(); 162 | 163 | local EnemyCreeps = npcBot:GetNearbyCreeps(MoMradius,true); 164 | local AllyCreeps = npcBot:GetNearbyCreeps(1000,false); 165 | 166 | -- Check if we're already using an ability 167 | if ( TinkerIsBusy()) then return end; 168 | 169 | local abilityLaser = npcBot:GetAbilityByName( "tinker_laser" ); 170 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 171 | local abilityMoM = npcBot:GetAbilityByName( "tinker_march_of_the_machines" ); 172 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 173 | 174 | local LaserDamage = abilityLaser:GetAbilityDamage(); 175 | local LaserCastRange = abilityLaser:GetCastRange(); 176 | 177 | local MissileDamage = abilityMissile:GetAbilityDamage(); 178 | local MissileCastRange = abilityMissile:GetCastRange(); 179 | 180 | local MoMDamage = abilityMoM:GetAbilityDamage(); 181 | 182 | --If we dont cast ability, just try to last hit. 183 | 184 | local lowest_hp = 100000; 185 | local weakest_creep = nil; 186 | for creep_k,creep in pairs(EnemyCreeps) 187 | do 188 | --npcBot:GetEstimatedDamageToTarget 189 | local creep_name = creep:GetUnitName(); 190 | DotaBotUtility:UpdateCreepHealth(creep); 191 | --print(creep_name); 192 | if(creep:IsAlive()) then 193 | local creep_hp = creep:GetHealth(); 194 | if(lowest_hp > creep_hp) then 195 | lowest_hp = creep_hp; 196 | weakest_creep = creep; 197 | end 198 | end 199 | end 200 | 201 | if(weakest_creep ~= nil and weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 202 | -- if creep's hp is lower than 70(because I don't Know how much is my damadge!!), try to last hit it. 203 | --if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and 204 | if(lowest_hp < weakest_creep:GetActualDamage( 205 | npcBot:GetBaseDamage(),DAMAGE_TYPE_PHYSICAL) 206 | + DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep) 207 | * (npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() + GetUnitToUnitDistance(npcBot,weakest_creep) / 900)) then 208 | if(npcBot:GetAttackTarget() == nil) then --StateMachine["attcking creep"] 209 | npcBot:Action_AttackUnit(weakest_creep,false); 210 | return; 211 | elseif(weakest_creep ~= StateMachine["attcking creep"]) then 212 | StateMachine["attcking creep"] = weakest_creep; 213 | npcBot:Action_AttackUnit(weakest_creep,true); 214 | return; 215 | end 216 | else 217 | -- simulation of human attack and stop 218 | if(npcBot:GetCurrentActionType() == BOT_ACTION_TYPE_ATTACK) then 219 | npcBot:Action_ClearActions(true); 220 | return; 221 | else 222 | npcBot:Action_AttackUnit(weakest_creep,false); 223 | return; 224 | end 225 | end 226 | weakest_creep = nil; 227 | 228 | end 229 | 230 | for creep_k,creep in pairs(AllyCreeps) 231 | do 232 | --npcBot:GetEstimatedDamageToTarget 233 | local creep_name = creep:GetUnitName(); 234 | DotaBotUtility:UpdateCreepHealth(creep); 235 | --print(creep_name); 236 | if(creep:IsAlive()) then 237 | local creep_hp = creep:GetHealth(); 238 | if(lowest_hp > creep_hp) then 239 | lowest_hp = creep_hp; 240 | weakest_creep = creep; 241 | end 242 | end 243 | end 244 | 245 | if(weakest_creep ~= nil) then 246 | -- if creep's hp is lower than 70(because I don't Know how much is my damadge!!), try to last hit it. 247 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and 248 | lowest_hp < weakest_creep:GetActualDamage( 249 | npcBot:GetBaseDamage(),DAMAGE_TYPE_PHYSICAL) + DotaBotUtility:GetCreepHealthDeltaPerSec(weakest_creep) 250 | * (npcBot:GetAttackPoint() / npcBot:GetAttackSpeed() + GetUnitToUnitDistance(npcBot,weakest_creep) / 900) 251 | and 252 | weakest_creep:GetHealth() / weakest_creep:GetMaxHealth() < 0.5) then 253 | Attacking_creep = weakest_creep; 254 | npcBot:Action_AttackUnit(Attacking_creep,true); 255 | return; 256 | end 257 | weakest_creep = nil; 258 | 259 | end 260 | 261 | -- nothing to do , try to attack heros 262 | 263 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1600, true, BOT_MODE_NONE ); 264 | if(NearbyEnemyHeroes ~= nil) then 265 | if(#NearbyEnemyHeroes > 0 and abilityMissile:IsFullyCastable() and MissileDamage > 300) then 266 | npcBot:Action_UseAbility(abilityMissile); 267 | return; 268 | end 269 | 270 | 271 | for _,npcEnemy in pairs( NearbyEnemyHeroes ) 272 | do 273 | if(CanCastOnTarget(npcEnemy)) then 274 | if(npcEnemy:GetActualDamage(MissileDamage,DAMAGE_TYPE_MAGICAL) >= npcEnemy:GetHealth()) then 275 | npcBot:Action_UseAbility(abilityMissile); 276 | return; 277 | end 278 | 279 | if(npcEnemy:GetActualDamage(LaserDamage,DAMAGE_TYPE_PURE) >= npcEnemy:GetHealth()) then 280 | npcBot:Action_UseAbilityOnEntity(abilityLaser,npcEnemy); 281 | return; 282 | end 283 | 284 | if(LaserDamage > 300 and GetUnitToUnitDistance(npcBot,npcEnemy) <= LaserCastRange) then 285 | npcBot:Action_UseAbilityOnEntity(abilityLaser,npcEnemy); 286 | return; 287 | end 288 | end 289 | 290 | if(DotaBotUtility.NilOrDead(npcBot:GetAttackTarget()) and GetUnitToUnitDistance(npcBot,npcEnemy) <= 600) then 291 | npcBot:Action_AttackUnit(npcEnemy,false); 292 | return; 293 | end 294 | end 295 | end 296 | 297 | -- CastMoM 298 | if(EnemyCreeps ~= nil) then 299 | if(AllyCreeps ~= nil and #AllyCreeps > 0) then 300 | if(#EnemyCreeps >=3 and abilityMoM:IsFullyCastable() and MoMDamage >= 24) then 301 | npcBot:Action_UseAbilityOnLocation(abilityMoM,npcBot:GetLocation() + Vector(50,-50)); 302 | return; 303 | end 304 | else 305 | if(#EnemyCreeps >=0 and abilityMoM:IsFullyCastable() and MoMDamage >= 24) then 306 | npcBot:Action_UseAbilityOnLocation(abilityMoM,npcBot:GetLocation() + Vector(50,-50)); 307 | return; 308 | end 309 | end 310 | end 311 | 312 | SoulRingReArm(); 313 | 314 | -- hit creeps to push 315 | local TimeNow = DotaTime(); 316 | for creep_k,creep in pairs(EnemyCreeps) 317 | do 318 | local creep_name = creep:GetUnitName(); 319 | --print(creep_name); 320 | if(creep:IsAlive()) then 321 | if(TimeNow > 600) then 322 | npcBot:Action_AttackUnit(creep,false); 323 | return; 324 | end 325 | local creep_hp = creep:GetHealth(); 326 | if(lowest_hp > creep_hp) then 327 | lowest_hp = creep_hp; 328 | weakest_creep = creep; 329 | end 330 | end 331 | end 332 | 333 | end 334 | 335 | local function ShouldRetreat() 336 | local npcBot = GetBot(); 337 | if(DotaTime() > 360) then 338 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 339 | < TinkerRetreatHPThreshold or npcBot:GetMana()/npcBot:GetMaxMana() 340 | < TinkerRetreatMPThreshold; 341 | else 342 | return npcBot:GetHealth()/npcBot:GetMaxHealth() 343 | < TinkerRetreatHPThreshold; 344 | end 345 | end 346 | 347 | local function IsTowerAttackingMe() 348 | local npcBot = GetBot(); 349 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 350 | local AllyCreeps = npcBot:GetNearbyCreeps(650,false); 351 | if(#NearbyTowers > 0) then 352 | for _,tower in pairs( NearbyTowers) 353 | do 354 | if(GetUnitToUnitDistance(tower,npcBot) < 900 and tower:IsAlive() and #AllyCreeps <= 2) then 355 | print("Tinker Attacked by tower"); 356 | return true; 357 | end 358 | end 359 | end 360 | return false; 361 | end 362 | 363 | -------------------local states----------------------------------------------------- 364 | 365 | local function StateIdle(StateMachine) 366 | local npcBot = GetBot(); 367 | if(npcBot:IsAlive() == false) then 368 | return; 369 | end 370 | 371 | local creeps = npcBot:GetNearbyCreeps(1000,true); 372 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 373 | 374 | if (TinkerIsBusy()) then return end; 375 | 376 | if(ShouldRetreat()) then 377 | StateMachine.State = STATE_RETREAT; 378 | return; 379 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 380 | StateMachine.State = STATE_RUN_AWAY; 381 | return; 382 | elseif(IsInTeamFight()) then 383 | StateMachine.State = STATE_TEAM_FIGHTING; 384 | return; 385 | elseif(npcBot:GetAttackTarget() ~= nil) then 386 | if(npcBot:GetAttackTarget():IsHero()) then 387 | StateMachine["EnemyToKill"] = npcBot:GetAttackTarget(); 388 | print("auto attacking: "..npcBot:GetAttackTarget():GetUnitName()); 389 | StateMachine.State = STATE_FIGHTING; 390 | return; 391 | end 392 | elseif(ConsiderFighting(StateMachine)) then 393 | StateMachine.State = STATE_FIGHTING; 394 | return; 395 | elseif(#creeps > 0 and pt ~= nil) then 396 | local mypos = npcBot:GetLocation(); 397 | 398 | local d = GetUnitToLocationDistance(npcBot,pt); 399 | if(d > 250) then 400 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 401 | else 402 | StateMachine.State = STATE_ATTACKING_CREEP; 403 | end 404 | return; 405 | end 406 | 407 | -- cast missile 408 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 409 | 410 | local MissileDamage = abilityMissile:GetAbilityDamage(); 411 | local MissileCastRange = abilityMissile:GetCastRange(); 412 | 413 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1600, true, BOT_MODE_NONE ); 414 | if(NearbyEnemyHeroes ~= nil) then 415 | if(#NearbyEnemyHeroes > 0 and abilityMissile:IsFullyCastable() and MissileDamage > 300) then 416 | npcBot:Action_UseAbility(abilityMissile); 417 | return; 418 | end 419 | end 420 | 421 | local travel_boots = DotaBotUtility.IsItemAvailable("item_travel_boots"); 422 | 423 | -- buy a tp and get out 424 | if(travel_boots == nil and npcBot:DistanceFromFountain() < 100 and DotaTime() > 0) then 425 | local tpscroll = DotaBotUtility.IsItemAvailable("item_tpscroll"); 426 | if(tpscroll == nil and DotaBotUtility:HasEmptySlot() and npcBot:GetGold() >= GetItemCost("item_tpscroll")) then 427 | print("buying tp"); 428 | npcBot:Action_PurchaseItem("item_tpscroll"); 429 | return; 430 | elseif(tpscroll ~= nil and tpscroll:IsFullyCastable()) then 431 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 432 | if(tower ~= nil) then 433 | npcBot:Action_UseAbilityOnEntity(tpscroll,tower); 434 | return; 435 | end 436 | elseif(tpscroll ~= nil and not tpscroll:IsCooldownReady()) then 437 | print("refresh tpscroll"); 438 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 439 | if(abilityRearm:IsFullyCastable()) then 440 | npcBot:Action_UseAbility(abilityRearm); 441 | LastRearmTime = GameTime(); 442 | return; 443 | end 444 | end 445 | end 446 | 447 | if(travel_boots ~= nil and travel_boots:IsFullyCastable() and npcBot:DistanceFromFountain() == 0) then 448 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 449 | if(tower ~= nil) then 450 | npcBot:Action_UseAbilityOnEntity(travel_boots,tower); 451 | return; 452 | else 453 | target = DotaBotUtility:GetNearBySuccessorPointOnLane(LANE); 454 | npcBot:Action_AttackMove(target); 455 | return; 456 | end 457 | elseif(travel_boots ~= nil and not travel_boots:IsCooldownReady()) then 458 | --refresh boots 459 | print("refresh boots"); 460 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 461 | if(abilityRearm:IsFullyCastable()) then 462 | npcBot:Action_UseAbility(abilityRearm); 463 | LastRearmTime = GameTime(); 464 | return; 465 | end 466 | else 467 | local NearbyTowers = npcBot:GetNearbyTowers(1000,true); 468 | local AllyCreeps = npcBot:GetNearbyCreeps(800,false); 469 | 470 | for _,tower in pairs(NearbyTowers) 471 | do 472 | local myDistanceToTower = GetUnitToUnitDistance(npcBot,tower); 473 | if(tower:IsAlive() and #AllyCreeps >= 1 and #creeps == 0) then 474 | for _,creep in pairs(AllyCreeps) 475 | do 476 | if(myDistanceToTower > GetUnitToUnitDistance(creep,tower) + 300) then 477 | print("Lina attack tower!!!"); 478 | npcBot:Action_AttackUnit(tower,false); 479 | return; 480 | end 481 | end 482 | end 483 | end 484 | 485 | if(DotaTime() < 20) then 486 | local tower = DotaBotUtility:GetFrontTowerAt(LANE); 487 | npcBot:Action_MoveToLocation(tower:GetLocation()); 488 | return; 489 | else 490 | target = DotaBotUtility:GetNearBySuccessorPointOnLane(LANE); 491 | npcBot:Action_AttackMove(target); 492 | return; 493 | end 494 | end 495 | 496 | 497 | end 498 | 499 | local function StateAttackingCreep(StateMachine) 500 | local npcBot = GetBot(); 501 | if(npcBot:IsAlive() == false) then 502 | StateMachine.State = STATE_IDLE; 503 | return; 504 | end 505 | 506 | local creeps = npcBot:GetNearbyCreeps(1000,true); 507 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 508 | 509 | if (TinkerIsBusy()) then return end; 510 | 511 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 512 | < TinkerRetreatHPThreshold) then 513 | StateMachine.State = STATE_RETREAT; 514 | return; 515 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 516 | StateMachine.State = STATE_RUN_AWAY; 517 | elseif(IsInTeamFight()) then 518 | StateMachine.State = STATE_TEAM_FIGHTING; 519 | return; 520 | elseif(ConsiderFighting(StateMachine)) then 521 | StateMachine.State = STATE_FIGHTING; 522 | return; 523 | elseif(#creeps > 0 and pt ~= nil) then 524 | local mypos = npcBot:GetLocation(); 525 | local d = GetUnitToLocationDistance(npcBot,pt); 526 | if(d > 200) then 527 | if(StateMachine["GotoComfortPointTime"] == nil) then 528 | StateMachine["GotoComfortPointTime"] = GameTime(); 529 | return; 530 | else 531 | if(GameTime() - StateMachine["GotoComfortPointTime"] < 1) then 532 | return; 533 | else 534 | StateMachine.State = STATE_GOTO_COMFORT_POINT; 535 | StateMachine["GotoComfortPointTime"] = nil; 536 | return; 537 | end 538 | end 539 | 540 | else 541 | ConsiderAttackCreeps(StateMachine); 542 | end 543 | return; 544 | else 545 | StateMachine.State = STATE_IDLE; 546 | return; 547 | end 548 | end 549 | 550 | local function StateRetreat(StateMachine) 551 | local npcBot = GetBot(); 552 | if(npcBot:IsAlive() == false) then 553 | StateMachine.State = STATE_IDLE; 554 | return; 555 | end 556 | 557 | --[[ 558 | I don't know how to Create a object of Location so I borrow one from GetLocation() 559 | 560 | Got Vector from marko.polo at http://dev.dota2.com/showthread.php?t=274301 561 | ]] 562 | 563 | if (TinkerIsBusy()) then return end; 564 | 565 | if(npcBot:DistanceFromFountain() > 0) then 566 | local travel_boots = DotaBotUtility.IsItemAvailable("item_travel_boots"); 567 | 568 | if(travel_boots ~= nil and travel_boots:IsFullyCastable()) then 569 | npcBot:Action_UseAbilityOnLocation(travel_boots,Constant.HomePosition()); 570 | return; 571 | else 572 | npcBot:Action_MoveToLocation(Constant.HomePosition()); 573 | return; 574 | end 575 | end 576 | 577 | -- cast missile 578 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 579 | 580 | local MissileDamage = abilityMissile:GetAbilityDamage(); 581 | local MissileCastRange = abilityMissile:GetCastRange(); 582 | 583 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1600, true, BOT_MODE_NONE ); 584 | if(NearbyEnemyHeroes ~= nil) then 585 | if(#NearbyEnemyHeroes > 0 and abilityMissile:IsFullyCastable() and MissileDamage > 300) then 586 | npcBot:Action_UseAbility(abilityMissile); 587 | return; 588 | end 589 | end 590 | 591 | 592 | 593 | if(npcBot:GetHealth() == npcBot:GetMaxHealth() and npcBot:GetMana() == npcBot:GetMaxMana()) then 594 | StateMachine.State = STATE_IDLE; 595 | return; 596 | end 597 | end 598 | 599 | local function StateGotoComfortPoint(StateMachine) 600 | local npcBot = GetBot(); 601 | if(npcBot:IsAlive() == false) then 602 | StateMachine.State = STATE_IDLE; 603 | return; 604 | end 605 | 606 | local creeps = npcBot:GetNearbyCreeps(1000,true); 607 | local pt = DotaBotUtility:GetComfortPoint(creeps,LANE); 608 | 609 | if (TinkerIsBusy()) then return end; 610 | 611 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 612 | < TinkerRetreatHPThreshold) then 613 | StateMachine.State = STATE_RETREAT; 614 | return; 615 | elseif(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 616 | StateMachine.State = STATE_RUN_AWAY; 617 | return; 618 | elseif(IsInTeamFight()) then 619 | StateMachine.State = STATE_TEAM_FIGHTING; 620 | return; 621 | elseif(ConsiderFighting(StateMachine)) then 622 | StateMachine.State = STATE_FIGHTING; 623 | return; 624 | elseif(#creeps > 0 and pt ~= nil) then 625 | local mypos = npcBot:GetLocation(); 626 | --pt[3] = npcBot:GetLocation()[3]; 627 | 628 | --local d = GetUnitToLocationDistance(npcBot,pt); 629 | local d = (npcBot:GetLocation() - pt):Length2D(); 630 | 631 | if (d < 100) then 632 | StateMachine.State = STATE_ATTACKING_CREEP; 633 | else 634 | npcBot:Action_MoveToLocation(pt); 635 | end 636 | return; 637 | else 638 | StateMachine.State = STATE_IDLE; 639 | return; 640 | end 641 | 642 | -- cast missile 643 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 644 | 645 | local MissileDamage = abilityMissile:GetAbilityDamage(); 646 | local MissileCastRange = abilityMissile:GetCastRange(); 647 | 648 | local NearbyEnemyHeroes = npcBot:GetNearbyHeroes( 1600, true, BOT_MODE_NONE ); 649 | if(NearbyEnemyHeroes ~= nil) then 650 | if(#NearbyEnemyHeroes > 0 and abilityMissile:IsFullyCastable() and MissileDamage > 300) then 651 | npcBot:Action_UseAbility(abilityMissile); 652 | return; 653 | end 654 | end 655 | 656 | end 657 | 658 | local function StateFighting(StateMachine) 659 | local npcBot = GetBot(); 660 | if(npcBot:IsAlive() == false) then 661 | StateMachine["cyclone dota time"] = nil; 662 | StateMachine.State = STATE_IDLE; 663 | return; 664 | end 665 | 666 | if (TinkerIsBusy()) then return end; 667 | 668 | if(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 669 | StateMachine["cyclone dota time"] = nil; 670 | StateMachine.State = STATE_RUN_AWAY; 671 | return; 672 | elseif(IsInTeamFight()) then 673 | StateMachine.State = STATE_TEAM_FIGHTING; 674 | return; 675 | elseif(not StateMachine["EnemyToKill"]:CanBeSeen() or not StateMachine["EnemyToKill"]:IsAlive()) then 676 | -- lost enemy 677 | print("lost enemy"); 678 | StateMachine["cyclone dota time"] = nil; 679 | StateMachine.State = STATE_IDLE; 680 | return; 681 | else 682 | local abilityLaser = npcBot:GetAbilityByName( "tinker_laser" ); 683 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 684 | local abilityMoM = npcBot:GetAbilityByName( "tinker_march_of_the_machines" ); 685 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 686 | 687 | local LaserDamage = abilityLaser:GetAbilityDamage(); 688 | local LaserCastRange = abilityLaser:GetCastRange(); 689 | 690 | local MissileDamage = abilityMissile:GetAbilityDamage(); 691 | local MissileCastRange = abilityMissile:GetCastRange(); 692 | 693 | local MoMDamage = abilityMoM:GetAbilityDamage(); 694 | 695 | -- Laser is castable but out of range, get closer!-- 696 | if(CanCastOnTarget(StateMachine["EnemyToKill"])) then 697 | if(abilityLaser:IsFullyCastable()) then 698 | if(GetUnitToUnitDistance(npcBot,StateMachine["EnemyToKill"]) < LaserCastRange) then 699 | npcBot:Action_UseAbilityOnEntity(abilityLaser,StateMachine["EnemyToKill"]); 700 | else 701 | npcBot:Action_MoveToLocation(StateMachine["EnemyToKill"]:GetLocation()); 702 | return; 703 | end 704 | end 705 | 706 | if(abilityMissile:IsFullyCastable()) then 707 | npcBot:Action_UseAbility(abilityMissile); 708 | return; 709 | end 710 | 711 | if(abilityMoM:IsFullyCastable()) then 712 | npcBot:Action_UseAbilityOnLocation(abilityMoM,npcBot:GetLocation() + Vector(50,-50)); 713 | return; 714 | end 715 | end 716 | 717 | if(StateMachine["EnemyToKill"]:GetHealth() > npcBot:GetHealth()) then 718 | StateMachine.State = STATE_RUN_AWAY; 719 | return; 720 | end 721 | 722 | 723 | if(npcBot:GetAttackTarget() ~= StateMachine["EnemyToKill"]) then 724 | npcBot:Action_AttackUnit(StateMachine["EnemyToKill"],false); 725 | return; 726 | end 727 | 728 | SoulRingReArm(); 729 | 730 | end 731 | end 732 | 733 | local function StateRunAway(StateMachine) 734 | local npcBot = GetBot(); 735 | 736 | if (TinkerIsBusy()) then return end; 737 | 738 | if(npcBot:IsAlive() == false) then 739 | StateMachine.State = STATE_IDLE; 740 | StateMachine["RunAwayFromLocation"] = nil; 741 | return; 742 | end 743 | 744 | if(npcBot:GetHealth()/npcBot:GetMaxHealth() 745 | < TinkerRetreatHPThreshold) then 746 | StateMachine.State = STATE_RETREAT; 747 | StateMachine["RunAwayFromLocation"] = nil; 748 | return; 749 | end 750 | 751 | local mypos = npcBot:GetLocation(); 752 | 753 | if(StateMachine["RunAwayFromLocation"] == nil) then 754 | --set the target to go back 755 | StateMachine["RunAwayFromLocation"] = npcBot:GetLocation(); 756 | --npcBot:Action_MoveToLocation(Constant.HomePosition()); 757 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 758 | return; 759 | else 760 | if(GetUnitToLocationDistance(npcBot,StateMachine["RunAwayFromLocation"]) > 400) then 761 | -- we are far enough from tower,return to normal state. 762 | StateMachine["RunAwayFromLocation"] = nil; 763 | StateMachine.State = STATE_IDLE; 764 | return; 765 | else 766 | npcBot:Action_MoveToLocation(DotaBotUtility:GetNearByPrecursorPointOnLane(LANE)); 767 | return; 768 | end 769 | end 770 | end 771 | 772 | local function StateTeamFighting(StateMachine) 773 | local npcBot = GetBot(); 774 | if(npcBot:IsAlive() == false) then 775 | StateMachine.State = STATE_IDLE; 776 | return; 777 | end 778 | 779 | if (TinkerIsBusy()) then return end; 780 | 781 | if(IsTowerAttackingMe() or DotaBotUtility:ConsiderRunAway()) then 782 | StateMachine.State = STATE_RUN_AWAY; 783 | else 784 | local abilityLaser = npcBot:GetAbilityByName( "tinker_laser" ); 785 | local abilityMissile = npcBot:GetAbilityByName( "tinker_heat_seeking_missile" ); 786 | local abilityMoM = npcBot:GetAbilityByName( "tinker_march_of_the_machines" ); 787 | local abilityRearm = npcBot:GetAbilityByName( "tinker_rearm" ); 788 | 789 | local LaserDamage = abilityLaser:GetAbilityDamage(); 790 | local LaserCastRange = abilityLaser:GetCastRange(); 791 | 792 | local MissileDamage = abilityMissile:GetAbilityDamage(); 793 | local MissileCastRange = abilityMissile:GetCastRange(); 794 | 795 | local MoMDamage = abilityMoM:GetAbilityDamage(); 796 | 797 | local EnemyBots = DotaBotUtility:GetEnemyBots(); 798 | local EnemyTeam = DotaBotUtility:GetEnemyTeam(); 799 | 800 | for _,idx in pairs(EnemyBots) 801 | do 802 | local BotHandle = GetTeamMember(EnemyTeam,idx); 803 | if(BotHandle ~= nil and BotHandle:IsAlive() and BotHandle:CanBeSeen()) then 804 | local d = GetUnitToUnitDistance(BotHandle,npcBot); 805 | if(d < LaserCastRange and abilityLaser:IsFullyCastable()) then 806 | npcBot:Action_UseAbilityOnEntity(abilityLaser,BotHandle); 807 | return; 808 | elseif(d < 1000 and abilityMoM:IsFullyCastable()) then 809 | npcBot:Action_UseAbilityOnLocation(abilityMoM,npcBot:GetLocation()); 810 | return; 811 | elseif(d < 2500) then 812 | npcBot:Action_UseAbility(abilityMissile); 813 | return; 814 | end 815 | end 816 | end 817 | 818 | SoulRingReArm(); 819 | StateMachine.State = STATE_IDLE; 820 | return; 821 | 822 | end 823 | end 824 | 825 | local StateMachine = {}; 826 | StateMachine["State"] = STATE_IDLE; 827 | StateMachine[STATE_IDLE] = StateIdle; 828 | StateMachine[STATE_ATTACKING_CREEP] = StateAttackingCreep; 829 | StateMachine[STATE_RETREAT] = StateRetreat; 830 | StateMachine[STATE_GOTO_COMFORT_POINT] = StateGotoComfortPoint; 831 | StateMachine[STATE_FIGHTING] = StateFighting; 832 | StateMachine[STATE_RUN_AWAY] = StateRunAway; 833 | StateMachine[STATE_TEAM_FIGHTING] = StateTeamFighting; 834 | StateMachine["totalLevelOfAbilities"] = 0; 835 | 836 | --[[ 837 | local TinkerAbilityMap = { 838 | [1] = "tinker_laser", 839 | [2] = "tinker_march_of_the_machines", 840 | [3] = "tinker_march_of_the_machines", 841 | [4] = "tinker_heat_seeking_missile", 842 | [5] = "tinker_march_of_the_machines", 843 | [6] = "tinker_rearm", 844 | [7] = "tinker_march_of_the_machines", 845 | [8] = "tinker_heat_seeking_missile", 846 | [9] = "tinker_heat_seeking_missile", 847 | [10] = "special_bonus_intelligence_8", 848 | [11] = "tinker_heat_seeking_missile", 849 | [12] = "tinker_rearm", 850 | [13] = "tinker_laser", 851 | [14] = "tinker_laser", 852 | [15] = "special_bonus_spell_amplify_4", 853 | [16] = "tinker_laser", 854 | [18] = "tinker_rearm", 855 | [20] = "special_bonus_cast_range_75", 856 | [25] = "special_bonus_unique_tinker" 857 | }; 858 | ]] 859 | local TinkerAbilityMap = { 860 | [1] = "tinker_laser", 861 | [2] = "tinker_heat_seeking_missile", 862 | [3] = "tinker_laser", 863 | [4] = "tinker_heat_seeking_missile", 864 | [5] = "tinker_laser", 865 | [6] = "tinker_rearm", 866 | [7] = "tinker_laser", 867 | [8] = "tinker_heat_seeking_missile", 868 | [9] = "tinker_heat_seeking_missile", 869 | [10] = "special_bonus_intelligence_8", 870 | [11] = "tinker_march_of_the_machines", 871 | [12] = "tinker_rearm", 872 | [13] = "tinker_march_of_the_machines", 873 | [14] = "tinker_march_of_the_machines", 874 | [15] = "special_bonus_spell_amplify_4", 875 | [16] = "tinker_march_of_the_machines", 876 | [18] = "tinker_rearm", 877 | [20] = "special_bonus_cast_range_75", 878 | [25] = "special_bonus_unique_tinker" 879 | }; 880 | 881 | local TinkerDoneLvlupAbility = {}; 882 | 883 | for lvl,_ in pairs(TinkerAbilityMap) 884 | do 885 | TinkerDoneLvlupAbility[lvl] = false; 886 | end 887 | 888 | local function ThinkLvlupAbility(StateMachine) 889 | -- Is there a bug? http://dev.dota2.com/showthread.php?t=274436 890 | local npcBot = GetBot(); 891 | 892 | 893 | local HeroLevel = PerryGetHeroLevel(); 894 | if(TinkerDoneLvlupAbility[HeroLevel] == false) then 895 | npcBot:Action_LevelAbility(TinkerAbilityMap[HeroLevel]); 896 | --TinkerDoneLvlupAbility[HeroLevel] = true; 897 | end 898 | end 899 | 900 | local PrevState = "none"; 901 | 902 | function Think( ) 903 | -- Think this item( ... ) 904 | --update 905 | 906 | local npcBot = GetBot(); 907 | DotaBotUtility:CourierThink(); 908 | ThinkLvlupAbility(StateMachine); 909 | 910 | --drinking bottle is a higher level 911 | if(npcBot:GetMaxHealth() - npcBot:GetHealth() > 100 912 | or npcBot:GetMaxMana() - npcBot:GetMana() > 100) then 913 | local bottle = DotaBotUtility.IsItemAvailable("item_bottle"); 914 | 915 | if(bottle ~= nil and 916 | not npcBot:HasModifier("modifier_bottle_regeneration") 917 | and bottle:IsFullyCastable() and bottle:GetCurrentCharges() > 0 918 | and not (npcBot:IsUsingAbility() or npcBot:IsChanneling())) then 919 | npcBot:Action_UseAbility(bottle); 920 | return; 921 | end 922 | end 923 | 924 | StateMachine[StateMachine.State](StateMachine); 925 | 926 | if(PrevState ~= StateMachine.State) then 927 | print("Tink bot STATE: "..StateMachine.State); 928 | PrevState = StateMachine.State; 929 | end 930 | 931 | -- not working! 932 | --local cp = DotaBotUtility:GetFrontTowerAt(LANE):GetNearbyCreeps(1000,false); 933 | 934 | end 935 | --------------------------------------------------------------------------------