diff --git a/ability_item_usage_abaddon.lua b/ability_item_usage_abaddon.lua index 9d85babb..8b06887d 100644 --- a/ability_item_usage_abaddon.lua +++ b/ability_item_usage_abaddon.lua @@ -50,13 +50,13 @@ local TalentTree={ return Talents[1] end, function() - return Talents[4] + return Talents[3] end, function() return Talents[6] end, function() - return Talents[7] + return Talents[8] end } -------------------------------------- diff --git a/ability_item_usage_abyssal_underlord.lua b/ability_item_usage_abyssal_underlord.lua index 9ea99541..3a5756af 100644 --- a/ability_item_usage_abyssal_underlord.lua +++ b/ability_item_usage_abyssal_underlord.lua @@ -27,16 +27,16 @@ local AbilityToLevelUp= Abilities[2], Abilities[1], Abilities[2], - Abilities[4], + Abilities[5], "talent", Abilities[2], - Abilities[4], + Abilities[5], Abilities[3], Abilities[3], "talent", Abilities[3], "nil", - Abilities[4], + Abilities[5], "nil", "talent", "nil", @@ -73,7 +73,7 @@ end -------------------------------------- local cast={} cast.Desire={} cast.Target={} cast.Type={} local Consider ={} -local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,utility.UCanCast} +local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,utility.UCanCast,utility.UCanCast} local enemyDisabled=utility.enemyDisabled function GetComboDamage() @@ -351,9 +351,9 @@ local darkRiftOriginalTarget local trackOriginalTargetPosition local darkRiftChosenTarget -Consider[4]=function() +Consider[5]=function() - local abilityNumber=4 + local abilityNumber=5 -------------------------------------- -- Generic Variable Setting -------------------------------------- @@ -407,8 +407,8 @@ Consider[4]=function() end local darkRiftCancelRange = 1200^2 -Consider[5] = function() - local ability = AbilitiesReal[5] +Consider[6] = function() + local ability = AbilitiesReal[6] if not ability:IsFullyCastable() or ability:IsHidden() then return 0 end diff --git a/ability_item_usage_alchemist.lua b/ability_item_usage_alchemist.lua index 013886b0..b7d546c4 100644 --- a/ability_item_usage_alchemist.lua +++ b/ability_item_usage_alchemist.lua @@ -54,7 +54,7 @@ local TalentTree={ return Talents[4] end, function() - return Talents[6] + return Talents[5] end, function() return Talents[7] @@ -550,15 +550,17 @@ Consider[6]=function() end end - if TimeSinceCast >= 4 or TimeSinceCast >= 3 and enemys[1]:GetStunDuration(true) >= 2 then - return BOT_ACTION_DESIRE_VERYHIGH,enemys[1] - end - if TimeSinceCast >= 2.5 then - local silencer = AbilityExtensions:Count(enemys, function(t) - return AbilityExtensions:MayNotBeIllusion(t) and t:HasSilence() - end) - if silencer > 0 then - return BOT_ACTION_DESIRE_HIGH, enemys[1] + if #enemys > 0 then + if TimeSinceCast >= 4 or TimeSinceCast >= 3 and enemys[1]:GetStunDuration(true) >= 2 then + return BOT_ACTION_DESIRE_VERYHIGH,enemys[1] + end + if TimeSinceCast >= 2.5 then + local silencer = AbilityExtensions:Count(enemys, function(t) + return AbilityExtensions:MayNotBeIllusion(npcBot, t) and t:HasSilence() + end) + if silencer > 0 then + return BOT_ACTION_DESIRE_HIGH, enemys[1] + end end end diff --git a/ability_item_usage_ancient_apparition.lua b/ability_item_usage_ancient_apparition.lua index dcfd6343..96ad7ccc 100644 --- a/ability_item_usage_ancient_apparition.lua +++ b/ability_item_usage_ancient_apparition.lua @@ -52,13 +52,13 @@ local TalentTree={ return Talents[2] end, function() - return Talents[4] + return Talents[3] end, function() - return Talents[6] + return Talents[5] end, function() - return Talents[7] + return Talents[8] end } diff --git a/ability_item_usage_antimage.lua b/ability_item_usage_antimage.lua index 168b7dee..3ffe09f9 100644 --- a/ability_item_usage_antimage.lua +++ b/ability_item_usage_antimage.lua @@ -59,12 +59,12 @@ local AbilityToLevelUp = { local TalentTree = { function() return Talents[1] end, function() return Talents[4] end, - function() return Talents[6] end, - function() return Talents[8] end, - function() return Talents[2] end, - function() return Talents[3] end, function() return Talents[5] end, function() return Talents[7] end, + function() return Talents[2] end, + function() return Talents[3] end, + function() return Talents[6] end, + function() return Talents[8] end, } utility.CheckAbilityBuild(AbilityToLevelUp) diff --git a/ability_item_usage_arc_warden.lua b/ability_item_usage_arc_warden.lua index 1460cc7b..c9a83b35 100644 --- a/ability_item_usage_arc_warden.lua +++ b/ability_item_usage_arc_warden.lua @@ -28,14 +28,14 @@ local AbilityToLevelUp= Abilities[3], Abilities[1], Abilities[1], - Abilities[3], + Abilities[2], Abilities[1], Abilities[4], Abilities[1], Abilities[3], Abilities[3], "talent", - Abilities[2], + Abilities[3], Abilities[4], Abilities[2], Abilities[2], @@ -373,22 +373,10 @@ Consider[3]=function() -- Mode based usage -------------------------------------- local enemys2 = npcBot:GetNearbyHeroes( 400, true, BOT_MODE_NONE ); - --[[ If we're seriously retreating, see if we can land a stun on someone who's damaged us recently - if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH or #enemys2>0) - then - for _,npcEnemy in pairs( enemys ) - do - if ( npcBot:WasRecentlyDamagedByHero( npcEnemy, 2.0 ) and CanCast[abilityNumber]( npcEnemy ) or GetUnitToUnitDistance(npcBot,npcEnemy)<400) - then - return BOT_ACTION_DESIRE_HIGH, npcEnemy:GetExtrapolatedLocation(CastPoint+Delay); - end - end - end]] - - -- If my mana is enough,use it at enemy + if ( npcBot:GetActiveMode() == BOT_MODE_LANING ) then - if(ManaPercentage>0.6) + if ManaPercentage>0.7 or ManaPercentage >= 0.4 and ability:GetLevel() >= 2 then if (WeakestEnemy~=nil) then diff --git a/ability_item_usage_axe.lua b/ability_item_usage_axe.lua index a93d1ac9..e3f19a40 100644 --- a/ability_item_usage_axe.lua +++ b/ability_item_usage_axe.lua @@ -48,7 +48,7 @@ local AbilityToLevelUp= local TalentTree={ function() - return Talents[2] + return Talents[1] end, function() return Talents[3] @@ -377,7 +377,7 @@ Consider[4]=function() end else -- only kill illusions when there are no real heroes local weakEnemy = AbilityExtensions:First(enemys, IsWeak) - if AbilityExtensions:GetHealthPercent(npcBot) <= 0.3 and AbilityExtensions:GetManaPercent(npcBot) >= 0.5 + AbilitiesReal[4]:GetManaCost() and not npcBot:HasModifier("modifier_axe_culling_blade_boost") and weakEnemy then + if AbilityExtensions:GetHealthPercent(npcBot) <= 0.3 and AbilityExtensions:GetManaPercent(npcBot) >= 0.5 + AbilitiesReal[4]:GetManaCost() and not npcBot:HasModifier("modifier_axe_culling_blade_boost") and weakEnemy and npcBot:GetMana() > npcBot:GetMaxMana() * 0.4 + ability:GetManaCost() then return BOT_ACTION_DESIRE_MODERATE, weakEnemy end end @@ -389,28 +389,39 @@ end AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal) +local roarLosingTarget local cullingBladeTarget function AbilityUsageThink() - + if roarLosingTarget ~= nil then + print(roarLosingTarget) + end -- Check if we're already using an ability if ( npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() ) then if npcBot:IsCastingAbility() then if npcBot:GetCurrentActiveAbility() == AbilitiesReal[1] then if not AbilityExtensions:IsFarmingOrPushing(npcBot) then - local nearbyEnemies = AbilityExtensions:GetNearbyHeroes(npcBot, AbilitiesReal[1]:GetAOERadius()) - nearbyEnemies = AbilityExtensions:Filter(nearbyEnemies, CanCast[1]) + local nearbyEnemies = AbilityExtensions:GetNearbyHeroes(npcBot, AbilitiesReal[1]:GetAOERadius()*1.15):Filter(CanCast[1]) if #nearbyEnemies == 0 then - npcBot:Action_ClearActions() + if roarLosingTarget == nil then + roarLosingTarget = DotaTime() + elseif DotaTime() - roarLosingTarget > 0.15 then + npcBot:Action_ClearActions(true) + end + return end end - elseif npcBot:GetCurrentActiveAbility() == AbilitiesReal[4] and cullingBladeTarget then - if cullingBladeTarget:GetHealth() > AbilitiesReal[4]:GetSpecialValueInt("kill_threshold") then - npcBot:Action_ClearActions() + else + if npcBot:GetCurrentActiveAbility() == AbilitiesReal[4] and cullingBladeTarget then + if cullingBladeTarget:CanBeSeen() and cullingBladeTarget:GetHealth() > AbilitiesReal[4]:GetSpecialValueInt("kill_threshold") then + print(cullingBladeTarget:GetHealth()) + npcBot:Action_ClearActions(true) + end end end end + roarLosingTarget = nil return end diff --git a/ability_item_usage_bane.lua b/ability_item_usage_bane.lua index 4f6f6d22..aad5a8d1 100644 --- a/ability_item_usage_bane.lua +++ b/ability_item_usage_bane.lua @@ -429,6 +429,23 @@ Consider[3]=function() end end + local disabledAllies = AbilityExtensions:Filter(allys, function(t) return AbilityExtensions:IsSeverelyDisabled(t) and not t:IsChanneling() and not AbilityExtensions:DontInterruptAlly(t) end) + disabledAllies = AbilityExtensions:SortByMinFirst(disabledAllies, function(t) return t:GetHealth() end) + if #disabledAllies ~= 0 then + return BOT_ACTION_DESIRE_MODERATE, disabledAllies[1] + end + + disabledAllies = AbilityExtensions:Filter(allys, function(t) + local b = t:GetIncomingTrackingProjectiles() + return AbilityExtensions:Any(b, function(s) + return GetUnitToLocationDistance(t, s.location) <= 400 and not s.is_attack + end) + end) + if disabledAllies ~= 0 then + disabledAllies = AbilityExtensions:SortByMinFirst(disabledAllies, function(t) return t:GetHealth() end) + return BOT_ACTION_DESIRE_MODERATE, disabledAllies[1] + end + return BOT_ACTION_DESIRE_NONE, 0; end @@ -551,23 +568,6 @@ Consider[4]=function() end end - local disabledAllies = AbilityExtensions:Filter(allys, function(t) return AbilityExtensions:IsSeverelyDisabled(t) and not t:IsChanneling() end) - disabledAllies = AbilityExtensions:SortByMinFirst(disabledAllies, function(t) return t:GetHealth() end) - if #disabledAllies ~= 0 then - return BOT_ACTION_DESIRE_MODERATE, disabledAllies[1] - end - - disabledAllies = AbilityExtensions:Filter(allys, function(t) - local b = t:GetIncomingTrackingProjectiles() - return AbilityExtensions:Any(b, function(s) - return GetUnitToLocationDistance(t, s.location) <= 400 and not s.is_attack - end) - end) - if disabledAllies ~= 0 then - disabledAllies = AbilityExtensions:SortByMinFirst(disabledAllies, function(t) return t:GetHealth() end) - return BOT_ACTION_DESIRE_MODERATE, disabledAllies[1] - end - return BOT_ACTION_DESIRE_NONE, 0; end diff --git a/ability_item_usage_batrider.lua b/ability_item_usage_batrider.lua index 4ba6812e..bdb30769 100644 --- a/ability_item_usage_batrider.lua +++ b/ability_item_usage_batrider.lua @@ -578,7 +578,7 @@ Consider[4]=function() then local npcEnemy = npcBot:GetTarget(); - if npcEnemy ~= nil and npcEnemy:IsHero() and AbilityExtensions:MayNotBeIllusion(npcEnemy) then + if npcEnemy ~= nil and npcEnemy:IsHero() and AbilityExtensions:MayNotBeIllusion(npcBot, npcEnemy) then if ( CanCast[abilityNumber]( npcEnemy ) and not enemyDisabled(npcEnemy) and GetUnitToUnitDistance(npcBot,npcEnemy)< CastRange + 75*#allys) then return BOT_ACTION_DESIRE_MODERATE, npcEnemy diff --git a/ability_item_usage_beastmaster.lua b/ability_item_usage_beastmaster.lua index 9076c10b..d2cb2482 100644 --- a/ability_item_usage_beastmaster.lua +++ b/ability_item_usage_beastmaster.lua @@ -472,7 +472,7 @@ Consider[6]=function() --Target Ability Example then for _,npcEnemy in pairs( enemys ) do - if ( (npcBot:WasRecentlyDamagedByHero( npcEnemy, 2.0 ) and CanCast[abilityNumber]( npcEnemy )) or GetUnitToUnitDistance(npcBot,npcEnemy)<400) and not AbilityExtensions:HasDamageRetargetModifier(npcEnemy) + if ( (npcBot:WasRecentlyDamagedByHero( npcEnemy, 2.0 ) and CanCast[abilityNumber]( npcEnemy )) or GetUnitToUnitDistance(npcBot,npcEnemy)<400) and not AbilityExtensions:HasAbilityRetargetModifier(npcEnemy) then return BOT_ACTION_DESIRE_HIGH, npcEnemy; end diff --git a/ability_item_usage_bounty_hunter.lua b/ability_item_usage_bounty_hunter.lua index 9d4a6782..dd546865 100644 --- a/ability_item_usage_bounty_hunter.lua +++ b/ability_item_usage_bounty_hunter.lua @@ -51,13 +51,13 @@ local TalentTree={ return Talents[1] end, function() - return Talents[4] + return Talents[3] end, function() return Talents[5] end, function() - return Talents[8] + return Talents[7] end } @@ -303,7 +303,7 @@ Consider[3]=function() end -local EnlargeCastRange = AbilityExtensions:EveryManySeconds(0.5, function() CastRange = AbilitiesReal[4]:GetCastRange() end) +-- local EnlargeCastRange = AbilityExtensions:EveryManySeconds(0.5, function() CastRange = AbilitiesReal[4]:GetCastRange() end) CanCast[4] = function(t) return AbilityExtensions:NormalCanCast(t, false, DAMAGE_TYPE_PHYSICAL, true, true) and t:IsHero() @@ -326,7 +326,7 @@ Consider[4]=function() end local CastRange = Clamp(ability:GetCastRange(), 0, 1599) - local enemys = npcBot:GetNearbyHeroes(CastRange+300,true,BOT_MODE_NONE) + local enemys = npcBot:GetNearbyHeroes(CastRange,true,BOT_MODE_NONE) local realEnemies = AbilityExtensions:GetNearbyNonIllusionHeroes(npcBot, CastRange) realEnemies = AbilityExtensions:Filter(realEnemies, CanCast[4]) realEnemies = AbilityExtensions:Map(realEnemies, function(t) diff --git a/ability_item_usage_brewmaster.lua b/ability_item_usage_brewmaster.lua index 34527d0b..e85a0d1a 100644 --- a/ability_item_usage_brewmaster.lua +++ b/ability_item_usage_brewmaster.lua @@ -509,6 +509,8 @@ Consider[4]=function() end +local slamLosingTarget + AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal) function AbilityUsageThink() @@ -519,12 +521,18 @@ function AbilityUsageThink() if npcBot:GetCurrentActiveAbility() == AbilitiesReal[1] then if not AbilityExtensions:IsFarmingOrPushing(npcBot) then local nearbyEnemies = AbilityExtensions:GetNearbyEnemyUnits(npcBot, AbilitiesReal[1]:GetAOERadius() + 40) - if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) then - npcBot:Action_ClearActions() + if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) == 0 then + if slamLosingTarget == nil then + slamLosingTarget = DotaTime() + return + elseif DotaTime() - slamLosingTarget > 0.15 then + npcBot:Action_ClearActions(false) + end end end end end + slamLosingTarget = nil return end diff --git a/ability_item_usage_centaur.lua b/ability_item_usage_centaur.lua index f0eb0766..2bfb6e29 100644 --- a/ability_item_usage_centaur.lua +++ b/ability_item_usage_centaur.lua @@ -21,17 +21,17 @@ local AbilityToLevelUp= { Abilities[1], Abilities[2], - Abilities[2], + Abilities[1], Abilities[3], - Abilities[2], - Abilities[4], - Abilities[2], Abilities[1], + Abilities[4], Abilities[1], + Abilities[2], + Abilities[3], "talent", - Abilities[1], + Abilities[2], Abilities[4], - Abilities[3], + Abilities[2], Abilities[3], "talent", Abilities[3], @@ -47,7 +47,7 @@ local AbilityToLevelUp= } local TalentTree={ function() - return Talents[2] + return Talents[1] end, function() return Talents[3] @@ -424,21 +424,28 @@ end AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal) +local stompLosingTarget + function AbilityUsageThink() -- Check if we're already using an ability - if ( npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() ) - then + if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then if npcBot:IsCastingAbility() then if npcBot:GetCurrentActiveAbility() == AbilitiesReal[1] then if not AbilityExtensions:IsFarmingOrPushing(npcBot) then local nearbyEnemies = AbilityExtensions:GetNearbyEnemyUnits(npcBot, AbilitiesReal[1]:GetAOERadius() + 40) - if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) then - npcBot:Action_ClearActions() + if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) == 0 then + if stompLosingTarget == nil then + stompLosingTarget = DotaTime() + elseif DotaTime() - stompLosingTarget > 0.15 then + npcBot:Action_ClearActions(true) + end + return end end end end + stompLosingTarget = nil return end diff --git a/ability_item_usage_chaos_knight.lua b/ability_item_usage_chaos_knight.lua index fdf1d0c4..471ee54a 100644 --- a/ability_item_usage_chaos_knight.lua +++ b/ability_item_usage_chaos_knight.lua @@ -50,13 +50,13 @@ local TalentTree={ return Talents[1] end, function() - return Talents[3] + return Talents[4] end, function() - return Talents[6] + return Talents[5] end, function() - return Talents[7] + return Talents[8] end } -------------------------------------- diff --git a/ability_item_usage_clinkz.lua b/ability_item_usage_clinkz.lua index 10e178cc..9b45525a 100644 --- a/ability_item_usage_clinkz.lua +++ b/ability_item_usage_clinkz.lua @@ -197,76 +197,9 @@ Consider[1]=function() end end - return BOT_ACTION_DESIRE_NONE, 0; - + return BOT_ACTION_DESIRE_NONE end ---[[ - -------------------------------------- - -- Global high-priorty usage - -------------------------------------- - --Try to kill enemy hero - if(npcBot:GetActiveMode() ~= BOT_MODE_RETREAT ) - then - if (WeakestEnemy~=nil) - then - if ( CanCast[abilityNumber]( WeakestEnemy ) ) - then - if(HeroHealth<=WeakestEnemy:GetActualIncomingDamage(Damage,DAMAGE_TYPE_MAGICAL) or (HeroHealth<=WeakestEnemy:GetActualIncomingDamage(GetComboDamage(),DAMAGE_TYPE_MAGICAL) and npcBot:GetMana()>ComboMana) and GetUnitToUnitDistance( WeakestEnemy,npcBot ) <= AttackRange+100) - then - return BOT_ACTION_DESIRE_HIGH - end - end - end - end - -------------------------------------- - -- Mode based usage - -------------------------------------- - -- If we're pushing or defending a lane - if ( npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT ) - then - local t=npcBot:GetAttackTarget() - if(t~=nil) - then - if (ManaPercentage>0.4 and t:IsTower() and GetUnitToUnitDistance( t, npcBot ) <= AttackRange+100) - then - return BOT_ACTION_DESIRE_MODERATE - end - end - end - - if ( npcBot:GetActiveMode() == BOT_MODE_ROSHAN ) - then - local npcTarget = npcBot:GetTarget(); - if ( npcTarget ~= nil and GetUnitToUnitDistance( npcTarget, npcBot ) <= AttackRange+100 ) - then - return BOT_ACTION_DESIRE_MODERATE; - end - end - - -- If we're going after someone - if ( npcBot:GetActiveMode() == BOT_MODE_ROAM or - npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or - npcBot:GetActiveMode() == BOT_MODE_ATTACK ) - then - local npcEnemy = npcBot:GetTarget(); - - if ( npcEnemy ~= nil ) - then - if ( CanCast[abilityNumber]( npcEnemy ) and GetUnitToUnitDistance(npcBot,npcEnemy)<= AttackRange+100) - then - return BOT_ACTION_DESIRE_MODERATE - end - end - end - - return BOT_ACTION_DESIRE_NONE, 0; - -end ---]] Consider[2]=function() local abilityNumber=2 diff --git a/ability_item_usage_crystal_maiden.lua b/ability_item_usage_crystal_maiden.lua index 52cff0c0..f550ad49 100644 --- a/ability_item_usage_crystal_maiden.lua +++ b/ability_item_usage_crystal_maiden.lua @@ -53,7 +53,7 @@ local TalentTree={ return Talents[4] end, function() - return Talents[6] + return Talents[5] end, function() return Talents[8] @@ -441,7 +441,7 @@ function AbilityUsageThink() then if npcBot:IsCastingAbility() and npcBot:GetCurrentActiveAbility() == AbilitiesReal[2] then if iceFreezingEnemy ~= nil and AbilityExtensions:HasAbilityRetargetModifier(iceFreezingEnemy) then - npcBot:Action_ClearActions() + npcBot:Action_ClearActions(true) end end if npcBot:IsChanneling() and npcBot:GetCurrentActiveAbility() == AbilitiesReal[4] then diff --git a/ability_item_usage_dazzle.lua b/ability_item_usage_dazzle.lua index 895b9e14..941f11a2 100644 --- a/ability_item_usage_dazzle.lua +++ b/ability_item_usage_dazzle.lua @@ -24,8 +24,8 @@ local AbilityToLevelUp= Abilities[1], Abilities[3], Abilities[1], - Abilities[2], Abilities[4], + Abilities[2], Abilities[3], Abilities[2], "talent", @@ -273,7 +273,7 @@ Consider[2]=function() local WeakestAlly,AllyHealth=utility.GetWeakestUnit(allys) local enemys = npcBot:GetNearbyHeroes(CastRange+300,true,BOT_MODE_NONE) local enemyAxe = AbilityExtensions:First(enemys, function(t) - return AbilityExtensions:MayNotBeIllusion(t) and t:GetUnitName() == "npc_dota_hero_axe" + return AbilityExtensions:MayNotBeIllusion(npcBot, t) and t:GetUnitName() == "npc_dota_hero_axe" end) local canEnemyAxeUseCullingBlade = false if enemyAxe then diff --git a/ability_item_usage_disruptor.lua b/ability_item_usage_disruptor.lua index 4db17a37..2e1b35dc 100644 --- a/ability_item_usage_disruptor.lua +++ b/ability_item_usage_disruptor.lua @@ -548,7 +548,7 @@ Consider[4]=function() if ( npcEnemy ~= nil ) then - if CanCast[abilityNumber]( npcEnemy ) and AbilityExtensions:IsNonIllusionHero(npcEnemy) + if CanCast[abilityNumber]( npcEnemy ) and AbilityExtensions:IsNonIllusionHero(npcBot, npcEnemy) then return BOT_ACTION_DESIRE_HIGH, npcEnemy:GetExtrapolatedLocation(CastPoint); end diff --git a/ability_item_usage_dragon_knight.lua b/ability_item_usage_dragon_knight.lua index 36978e70..3ba06082 100644 --- a/ability_item_usage_dragon_knight.lua +++ b/ability_item_usage_dragon_knight.lua @@ -73,7 +73,7 @@ end -------------------------------------- local cast={} cast.Desire={} cast.Target={} cast.Type={} local Consider ={} -local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,utility.UCanCast} +local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,utility.UCanCast,utility.NCanCast} local enemyDisabled=utility.enemyDisabled function GetComboDamage() diff --git a/ability_item_usage_earthshaker.lua b/ability_item_usage_earthshaker.lua index 62ecf2d7..ec12f487 100644 --- a/ability_item_usage_earthshaker.lua +++ b/ability_item_usage_earthshaker.lua @@ -441,8 +441,7 @@ Consider[4]=function() then if ( CanCast[abilityNumber]( WeakestEnemy ) ) then - if (#enemys > 1 or not WeakestEnemy:WasRecentlyDamagedByAnyHero(1.5)) and (HeroHealth<=WeakestEnemy:GetActualIncomingDamage(Damage,DAMAGE_TYPE_MAGICAL)) - then + if (#enemys > 1 or not WeakestEnemy:WasRecentlyDamagedByAnyHero(1.5) and #allys == 0) and HeroHealth<=WeakestEnemy:GetActualIncomingDamage(Damage,DAMAGE_TYPE_MAGICAL) then return BOT_ACTION_DESIRE_MODERATE end end diff --git a/ability_item_usage_ember_spirit.lua b/ability_item_usage_ember_spirit.lua index ff302dde..a005288e 100644 --- a/ability_item_usage_ember_spirit.lua +++ b/ability_item_usage_ember_spirit.lua @@ -351,7 +351,7 @@ Consider[4] = function() if healthPercent >= 0.75 and manaPercent >= 0.9 and AbilityExtensions:GetDistanceFromAncient(npcBot) <= 1000 and DotaTime() <= 15 * 60 then if AbilityExtensions:IsLaning(npcBot) then local laneFront = GetLaneFrontLocation(GetTeam(), npcBot:GetAssignedLane(), 0) - local remnantUnderTower = AbilityExtensions:Filter(activeRemnants, function(t) return GetUnitToUnitDistance(t, laneFront) <= 1000 end) + local remnantUnderTower = AbilityExtensions:Filter(activeRemnants, function(t) return GetUnitToLocationDistance(t, laneFront) <= 1000 end) if remnantUnderTower[1] then return BOT_ACTION_DESIRE_MODERATE, remnantUnderTower[1]:GetLocation() end @@ -379,7 +379,7 @@ Consider[4] = function() if AbilityExtensions:IsRetreating(npcBot) then local distanceToFountain = AbilityExtensions:Filter(activeRemnants, function(t) local distance = GetUnitToUnitDistance(t, npcBot) - return distance >= npcBot:GetCurrentMovementSpeed() * 2 and distance > AbilityExtensions:GetDistanceFromAncient(t) + 600 + return distance >= npcBot:GetCurrentMovementSpeed() * 2.5 and distance >= 700 and distance > AbilityExtensions:GetDistanceFromAncient(t) + 600 end) distanceToFountain = AbilityExtensions:Map(distanceToFountain, function(t) return { t, AbilityExtensions:GetDistanceFromAncient(t) } end) distanceToFountain = AbilityExtensions:SortByMinFirst(distanceToFountain, function(t) return t[2] end) diff --git a/ability_item_usage_faceless_void.lua b/ability_item_usage_faceless_void.lua index 83469076..aba0b36f 100644 --- a/ability_item_usage_faceless_void.lua +++ b/ability_item_usage_faceless_void.lua @@ -350,26 +350,6 @@ Consider[5]=function() end end - -- If we're pushing or defending a lane and can hit 4+ creeps, go for it - --[[if ( npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_BOT ) - then - local locationAoE = npcBot:FindAoELocation( true, true, npcBot:GetLocation(), CastRange, Radius, CastPoint, 0 ); - if ( locationAoE.count >= 3 ) - then - local TargetLocation=locationAoE.targetloc; - local Allies = utility.GetAlliesNearLocation(TargetLocation,Radius) - if(#Allies<=1) - then - return BOT_ACTION_DESIRE_LOW, TargetLocation - end - end - end]] - -- If we're going after someone if ( npcBot:GetActiveMode() == BOT_MODE_ROAM or npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or diff --git a/ability_item_usage_generic.lua b/ability_item_usage_generic.lua index 872f8308..f3af8bd4 100644 --- a/ability_item_usage_generic.lua +++ b/ability_item_usage_generic.lua @@ -304,7 +304,7 @@ function GetComboMana(AbilitiesReal) local npcBot = GetBot() local tempComboMana = 0 for i, ability in pairs(AbilitiesReal) do - if ability:IsPassive() == false then + if ability and ability:GetLevel() >= 1 and ability:IsPassive() == false and not ability:IsHidden() then if ability:IsUltimate() == false or ability:GetCooldownTimeRemaining() <= 30 then tempComboMana = tempComboMana + ability:GetManaCost() end diff --git a/ability_item_usage_jakiro.lua b/ability_item_usage_jakiro.lua index 6d6fae58..4c0c4c3f 100644 --- a/ability_item_usage_jakiro.lua +++ b/ability_item_usage_jakiro.lua @@ -468,7 +468,7 @@ Consider[4] = function() -------------------------------------- local ability=AbilitiesReal[abilityNumber]; - if not ability:IsFullyCastable() then + if not ability:IsFullyCastable() or ability:IsHidden() then return BOT_ACTION_DESIRE_NONE, 0; end diff --git a/ability_item_usage_kunkka.lua b/ability_item_usage_kunkka.lua index 11a45a8b..edd170c8 100644 --- a/ability_item_usage_kunkka.lua +++ b/ability_item_usage_kunkka.lua @@ -469,7 +469,7 @@ end Consider[4] = function() local ability = AbilitiesReal[4] - if not ability:IsFullyCastable() or ability:GetCurrentCharges() == 0 then + if not ability:IsFullyCastable() or ability:IsHidden() then return 0 end local abilityLevel = ability:GetLevel() @@ -484,7 +484,7 @@ Consider[4] = function() return RemapValClamped(AbilityExtensions:GetEnemyHeroNumber(npcBot, enemies), 2, 4, 0.3, 0.9) end if AbilityExtensions:Contains(targettableEnemies, target) then - return BOT_ACTION_DESIRE_MODERATE, true + return BOT_ACTION_DESIRE_MODERATE end return 0 end diff --git a/ability_item_usage_life_stealer.lua b/ability_item_usage_life_stealer.lua index 7f757385..8d60b0a8 100644 --- a/ability_item_usage_life_stealer.lua +++ b/ability_item_usage_life_stealer.lua @@ -100,7 +100,7 @@ Consider[1]=function() -------------------------------------- local ability=AbilitiesReal[abilityNumber]; - if not ability:IsFullyCastable() then + if not ability:IsFullyCastable() or ability:IsHidden() then -- all abilities are hidden when using ultimate return BOT_ACTION_DESIRE_NONE, 0; end @@ -170,7 +170,7 @@ Consider[4]=function() -------------------------------------- local ability=AbilitiesReal[abilityNumber]; - if not ability:IsFullyCastable() then + if not ability:IsFullyCastable() or ability:IsHidden() then return BOT_ACTION_DESIRE_NONE, 0; end @@ -276,7 +276,7 @@ Consider[5]=function() local ability=AbilitiesReal[abilityNumber]; if not ability:IsFullyCastable() or AbilityExtensions:CannotMove(npcBot) then - return BOT_ACTION_DESIRE_NONE, 0; + return BOT_ACTION_DESIRE_NONE end local CastRange = ability:GetCastRange(); diff --git a/ability_item_usage_lina.lua b/ability_item_usage_lina.lua index dd23a88b..65aab46e 100644 --- a/ability_item_usage_lina.lua +++ b/ability_item_usage_lina.lua @@ -87,7 +87,8 @@ local function GetLagunaBladeDamageType() return AbilityExtensions:HasScepter(npcBot) and DAMAGE_TYPE_PURE or DAMAGE_TYPE_MAGICAL end function CanCast4(t) - local hasShard = false -- AbilityExtensions:HasShard(t) + -- local hasShard = AbilityExtensions:HasShard(npcBot) + local hasShard = false return AbilityExtensions:NormalCanCast(t, false, GetLagunaBladeDamageType(), nil, not hasShard, not hasShard) end local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,CanCast4} @@ -457,14 +458,11 @@ local ShardLagunaBlade = function() end Consider[4] = function() - --[[ - if AbilityExtensions:HasShard(npcBot) then + -- if AbilityExtensions:HasShard(npcBot) then return NormalLagnuaBladeConsider() - else - return ShardLagunaBlade() - end - --]] - return NormalLagnuaBladeConsider() + -- else + -- return ShardLagunaBlade() + -- end end AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal) diff --git a/ability_item_usage_lycan.lua b/ability_item_usage_lycan.lua index beae43d7..5336643d 100644 --- a/ability_item_usage_lycan.lua +++ b/ability_item_usage_lycan.lua @@ -202,63 +202,20 @@ Consider[2]=function() local CastRange = ability:GetCastRange(); local CastPoint = ability:GetCastPoint(); - local allys = npcBot:GetNearbyHeroes( 1200, false, BOT_MODE_NONE ); - local enemys = npcBot:GetNearbyHeroes(CastRange+300,true,BOT_MODE_NONE) - local WeakestEnemy,HeroHealth=utility.GetWeakestUnit(enemys) - local creeps = npcBot:GetNearbyCreeps(CastRange+300,true) - local WeakestCreep,CreepHealth=utility.GetWeakestUnit(creeps) - -------------------------------------- - -- Mode based usage - -------------------------------------- - -- If we're farming and can kill 3+ creeps with LSA - if ( npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT ) - then - local locationAoE = npcBot:FindAoELocation( true, false, npcBot:GetLocation(), 600, 300, 0, 0 ); - if ( locationAoE.count >= 3 and npcBot:GetMana()/npcBot:GetMaxMana() > 0.65 ) then - return BOT_ACTION_DESIRE_LOW; - end - end - - if npcBot:GetActiveMode() == BOT_MODE_FARM - then - local npcTarget = npcBot:GetAttackTarget(); - if npcTarget ~= nil then - return BOT_ACTION_DESIRE_LOW; - end - end - - if ( npcBot:GetActiveMode() == BOT_MODE_ROSHAN ) - then - local npcTarget = npcBot:GetAttackTarget(); - if npcTarget~=nil - then - if string.find(npcTarget:GetUnitName(), "roshan") and GetUnitToUnitDistance(npcBot,npcTarget)< 600 then - return BOT_ACTION_DESIRE_LOW; - end - end + local allys = AbilityExtensions:GetNearbyNonIllusionHeroes(1600, false) + local enemies = AbilityExtensions:GetNearbyNonIllusionHeroes(1600, true) + local radius = 2000 + + if allys:Any(function(t) return AbilityExtensions:GetHealthPercent(t) <= 0.5 and t:WasRecentlyDamagedByAnyHero(1.5) end) then + return BOT_ACTION_DESIRE_MODERATE end - -- If we're going after someone - if ( npcBot:GetActiveMode() == BOT_MODE_ROAM or - npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or - npcBot:GetActiveMode() == BOT_MODE_ATTACK ) - then - local npcEnemy = npcBot:GetTarget(); - - if ( npcEnemy ~= nil ) + if npcBot:GetActiveMode() == BOT_MODE_RETREAT and #enemies > 0 then + if ( npcBot:WasRecentlyDamagedByAnyHero( 2.0 )) then - if ( CanCast[abilityNumber]( npcEnemy ) and GetUnitToUnitDistance(npcBot,npcEnemy)< 300 + 75*#allys ) - then - return BOT_ACTION_DESIRE_MODERATE, npcEnemy - end + return BOT_ACTION_DESIRE_HIGH end end - - return BOT_ACTION_DESIRE_NONE; - end Consider[5]=function() diff --git a/ability_item_usage_phantom_assassin.lua b/ability_item_usage_phantom_assassin.lua index ad303da0..b8d45fe2 100644 --- a/ability_item_usage_phantom_assassin.lua +++ b/ability_item_usage_phantom_assassin.lua @@ -159,10 +159,10 @@ Consider[1]=function() --Target Ability Example end end - -- If my mana is enough,use it at enemy + -- If my mana is enough, use it at enemy if ( npcBot:GetActiveMode() == BOT_MODE_LANING ) then - if((ManaPercentage>0.4 or npcBot:GetMana()>ComboMana) and ability:GetLevel()>=2 and CreepHealth>=150) + if (ManaPercentage>0.4 and ability:GetLevel()>=3 or ManaPercentage>0.7 and ability:GetLevel()>=2) and CreepHealth>=150 then if (WeakestEnemy~=nil) then @@ -175,12 +175,7 @@ Consider[1]=function() --Target Ability Example end -- If we're pushing or defending a lane - if ( npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_TOP or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_MID or - npcBot:GetActiveMode() == BOT_MODE_DEFEND_TOWER_BOT ) + if AbilityExtensions:IsFarmingOrPushing(npcBot) then if ( #enemys>=1) then @@ -333,7 +328,7 @@ Consider[3]=function() local WeakestCreep,CreepHealth=utility.GetWeakestUnit(creeps) local dispellRadius = ability:GetCastRange() - local realEnemiesNearby = AbilityExtensions:GetNearbyNonIllusionHeroes(dispellRadius) + local realEnemiesNearby = AbilityExtensions:GetNearbyNonIllusionHeroes(npcBot, dispellRadius) -------------------------------------- -- Mode based usage diff --git a/ability_item_usage_puck.lua b/ability_item_usage_puck.lua index 4a9886b5..79c23ba3 100644 --- a/ability_item_usage_puck.lua +++ b/ability_item_usage_puck.lua @@ -1,14 +1,11 @@ --- v1.7 template by AaronSong321 -local utility = require(GetScriptDirectory().."/utility") -local ability_item_usage_generic = require(GetScriptDirectory() .. "/ability_item_usage_generic") +local utility = require(GetScriptDirectory().."/utility") +local ability_item_usage_generic = require(GetScriptDirectory().."/ability_item_usage_generic") local AbilityExtensions = require(GetScriptDirectory().."/util/AbilityAbstraction") - local npcBot = GetBot() if npcBot:IsIllusion() then return end -local AbilityNames, Abilities, Talents = AbilityExtensions:InitAbility() - +local AbilityNames,Abilities,Talents = AbilityExtensions:InitAbility() local AbilityToLevelUp = { AbilityNames[1], AbilityNames[3], @@ -36,48 +33,53 @@ local AbilityToLevelUp = { "nil", "talent", } - local TalentTree = { - function() return Talents[2] end, - function() return Talents[3] end, - function() return Talents[6] end, - function() return Talents[8] end, + function() + return Talents[2] + end, + function() + return Talents[3] + end, + function() + return Talents[6] + end, + function() + return Talents[8] + end, } - utility.CheckAbilityBuild(AbilityToLevelUp) - function AbilityLevelUpThink() ability_item_usage_generic.AbilityLevelUpThink2(AbilityToLevelUp, TalentTree) end - --------------------------------------- --- Ability Usage Thinking --------------------------------------- -local cast= {} cast.Desire= {} cast.Target= {} cast.Type= {} +local cast = {} +cast.Desire = {} +cast.Target = {} +cast.Type = {} local CanCast = {} CanCast[1] = function(t) return AbilityExtensions:NormalCanCast(t, false) end CanCast[2] = CanCast[1] CanCast[3] = function(t) return not AbilityExtensions:IsInvulnerable(t) or not AbilityExtensions:ShouldNotBeAttacked(t) end -CanCast[4] = function() return true end +CanCast[4] = function(t) return true end CanCast[5] = CanCast[1] - local attackRange local healthPercent local mana local manaPercent - +local enemies +local realEnemies +local friends +local friendCount +local enemyCreeps +local friendCreeps +local neutralCreeps +local tower local illusoryOrbCastLocation local illusoryOrbMaxTravelDistance - local Consider = {} - local function GetIllusoryOrb() - return AbilityExtensions:First(GetLinearProjectiles(), function(t) - return t.ability and t.ability:GetName() == "puck_illusory_orb" and t.caster == npcBot - end) + return AbilityExtensions:First(GetLinearProjectiles(), function(t) return t.ability and t.ability:GetName() == "puck_illusory_orb" and t.caster == npcBot end) end local illusoryOrb = GetIllusoryOrb() - Consider[1] = function() local ability = Abilities[1] if not ability:IsFullyCastable() then @@ -88,189 +90,54 @@ Consider[1] = function() local castPoint = ability:GetCastPoint() local manaCost = ability:GetManaCost() local duration = ability:GetDuration() - local enemies = AbilityExtensions:GetNearbyHeroes(npcBot, 1599) - if #enemies > 0 then - enemies = AbilityExtensions:GetNearbyHeroes(npcBot, castRange) - end - local realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) - local targettableEnemies = AbilityExtensions:Filter(realEnemies, function(t) return AbilityExtensions:NormalCanCast(t, true) end) - local friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1200, true) - local friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) - local enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, castRange) - local friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) - local neutralCreeps = npcBot:GetNearbyNeutralCreeps(castRange) - local damage = ability:GetAbilityDamage() - local weakestEnemy, enemyHealth = utility.GetWeakestUnit(targettableEnemies) - local weakCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) return t:GetHealth() < t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) end) - local weakestCreep = utility.GetWeakestUnit(weakCreeps) - local tower = AbilityExtensions:GetLaningTower(npcBot) + local damage = ability:GetDamage() local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) - return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) + return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9 + #enemyCreeps * 0.1) or GetUnitToUnitDistance(tower, t) <= 700) end) if #friendCreeps == 0 then forbiddenCreeps = {} end - + if AbilityExtensions:NotRetreating(npcBot) then + do + local target = AbilityExtensions:GetTargetIfGood(npcBot) + if target then + coroutine.yield(BOT_ACTION_DESIRE_HIGH, target) + end + end + end if AbilityExtensions:IsFarmingOrPushing(npcBot) then if #enemyCreeps > 3 and #forbiddenCreeps == 0 and manaPercent > 0.6 + manaCost then - return BOT_ACTION_DESIRE_MODERATE, enemyCreeps[2]:GetLocation() + coroutine.yield(BOT_ACTION_DESIRE_MODERATE, enemyCreeps[2]:GetLocation()) end elseif AbilityExtensions:IsRetreating(npcBot) then - - end -end - -Consider[2] = function() - local ability = Abilities[1] - if not ability:IsFullyCastable() or AbilityExtensions:CannotTeleport(npcBot) then - return 0 - end - local abilityLevel = ability:GetLevel() - local castRange = ability:GetCastRange() - local castPoint = ability:GetCastPoint() - local manaCost = ability:GetManaCost() - local duration = ability:GetDuration() - local enemies = AbilityExtensions:GetNearbyHeroes(npcBot, castRange) - local realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) - local targettableEnemies = AbilityExtensions:Filter(realEnemies, function(t) return AbilityExtensions:NormalCanCast(t, true) end) - local friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1200, true) - local friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) - local enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, castRange) - local friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) - local neutralCreeps = npcBot:GetNearbyNeutralCreeps(castRange) - local damage = ability:GetAbilityDamage() - local weakestEnemy, enemyHealth = utility.GetWeakestUnit(targettableEnemies) - local weakCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) return t:GetHealth() < t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) end) - local weakestCreep = utility.GetWeakestUnit(weakCreeps) - local tower = AbilityExtensions:GetLaningTower(npcBot) - local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) - return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) - end) - if #friendCreeps == 0 then - forbiddenCreeps = {} - end - - -end - -Consider[3] = function() - local ability = Abilities[1] - if not ability:IsFullyCastable() then - return 0 - end - local abilityLevel = ability:GetLevel() - local castRange = ability:GetCastRange() - local castPoint = ability:GetCastPoint() - local manaCost = ability:GetManaCost() - local duration = ability:GetDuration() - local enemies = AbilityExtensions:GetNearbyHeroes(npcBot, castRange) - local realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) - local targettableEnemies = AbilityExtensions:Filter(realEnemies, function(t) return AbilityExtensions:NormalCanCast(t, true) end) - local friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1200, true) - local friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) - local enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, castRange) - local friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) - local neutralCreeps = npcBot:GetNearbyNeutralCreeps(castRange) - local damage = ability:GetAbilityDamage() - local weakestEnemy, enemyHealth = utility.GetWeakestUnit(targettableEnemies) - local weakCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) return t:GetHealth() < t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) end) - local weakestCreep = utility.GetWeakestUnit(weakCreeps) - local tower = AbilityExtensions:GetLaningTower(npcBot) - local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) - return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) - end) - if #friendCreeps == 0 then - forbiddenCreeps = {} end - - end - -Consider[4] = function() - local ability = Abilities[1] - if not ability:IsFullyCastable() or AbilityExtensions:CannotTeleport(npcBot) then - return 0 - end - local abilityLevel = ability:GetLevel() - local castRange = ability:GetCastRange() - local castPoint = ability:GetCastPoint() - local manaCost = ability:GetManaCost() - local duration = ability:GetDuration() - local enemies = AbilityExtensions:GetNearbyHeroes(npcBot, castRange) - local realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) - local targettableEnemies = AbilityExtensions:Filter(realEnemies, function(t) return AbilityExtensions:NormalCanCast(t, true) end) - local friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1200, true) - local friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) - local enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, castRange) - local friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) - local neutralCreeps = npcBot:GetNearbyNeutralCreeps(castRange) - local damage = ability:GetAbilityDamage() - local weakestEnemy, enemyHealth = utility.GetWeakestUnit(targettableEnemies) - local weakCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) return t:GetHealth() < t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) end) - local weakestCreep = utility.GetWeakestUnit(weakCreeps) - local tower = AbilityExtensions:GetLaningTower(npcBot) - local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) - return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) - end) - if #friendCreeps == 0 then - forbiddenCreeps = {} - end - - -end - -Consider[5] = function() - local ability = Abilities[1] - if not ability:IsFullyCastable() then - return 0 - end - local abilityLevel = ability:GetLevel() - local castRange = ability:GetCastRange() - local castPoint = ability:GetCastPoint() - local manaCost = ability:GetManaCost() - local duration = ability:GetDuration() - local enemies = AbilityExtensions:GetNearbyHeroes(npcBot, castRange) - local realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) - local targettableEnemies = AbilityExtensions:Filter(realEnemies, function(t) return AbilityExtensions:NormalCanCast(t, true) end) - local friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1200, true) - local friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) - local enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, castRange) - local friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) - local neutralCreeps = npcBot:GetNearbyNeutralCreeps(castRange) - local damage = ability:GetAbilityDamage() - local weakestEnemy, enemyHealth = utility.GetWeakestUnit(targettableEnemies) - local weakCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) return t:GetHealth() < t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) end) - local weakestCreep = utility.GetWeakestUnit(weakCreeps) - local tower = AbilityExtensions:GetLaningTower(npcBot) - local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) - return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) - end) - if #friendCreeps == 0 then - forbiddenCreeps = {} - end - - -end - AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, Abilities) - function AbilityUsageThink() - if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then - return - end - + if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then + return + end attackRange = npcBot:GetAttackRange() healthPercent = AbilityExtensions:GetHealthPercent(npcBot) mana = npcBot:GetMana() manaPercent = AbilityExtensions:GetManaPercent(npcBot) - cast=ability_item_usage_generic.ConsiderAbility(Abilities,Consider) - local abilityIndex, target, castType = ability_item_usage_generic.UseAbility(Abilities,cast) + enemies = AbilityExtensions:GetNearbyHeroes(npcBot, 1599) + realEnemies = AbilityExtensions:Filter(enemies, function(t) + return AbilityExtensions:MayNotBeIllusion(npcBot, t) + end) + friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1599, true) + friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) + enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, 1599) + friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange() + 150, false) + neutralCreeps = npcBot:GetNearbyNeutralCreeps(1599) + tower = AbilityExtensions:GetLaningTower(npcBot) + cast = ability_item_usage_generic.ConsiderAbility(Abilities, Consider) + local abilityIndex,target,castType = ability_item_usage_generic.UseAbility(Abilities, cast) if abilityIndex == 1 then illusoryOrbCastLocation = npcBot:GetLocation() illusoryOrbMaxTravelDistance = Abilities[1]:GetSpecialValueInt("max_distance") - 50 end end - -function CourierUsageThink() - ability_item_usage_generic.CourierUsageThink() -end \ No newline at end of file +function CourierUsageThink() + ability_item_usage_generic.CourierUsageThink() +end diff --git a/ability_item_usage_puck.mira b/ability_item_usage_puck.mira new file mode 100644 index 00000000..df01769d --- /dev/null +++ b/ability_item_usage_puck.mira @@ -0,0 +1,150 @@ +local utility = require(GetScriptDirectory().."/utility") +local ability_item_usage_generic = require(GetScriptDirectory() .. "/ability_item_usage_generic") +local AbilityExtensions = require(GetScriptDirectory().."/util/AbilityAbstraction") + +local npcBot = GetBot() +if npcBot:IsIllusion() then + return +end +local AbilityNames, Abilities, Talents = AbilityExtensions:InitAbility() + +local AbilityToLevelUp = { + AbilityNames[1], + AbilityNames[3], + AbilityNames[1], + AbilityNames[2], + AbilityNames[1], + AbilityNames[5], + AbilityNames[1], + AbilityNames[3], + AbilityNames[2], + "talent", + AbilityNames[2], + AbilityNames[5], + AbilityNames[2], + AbilityNames[3], + "talent", + AbilityNames[3], + "nil", + AbilityNames[5], + "nil", + "talent", + "nil", + "nil", + "nil", + "nil", + "talent", +} + +local TalentTree = { + function() return Talents[2] end, + function() return Talents[3] end, + function() return Talents[6] end, + function() return Talents[8] end, +} + +utility.CheckAbilityBuild(AbilityToLevelUp) + +function AbilityLevelUpThink() + ability_item_usage_generic.AbilityLevelUpThink2(AbilityToLevelUp, TalentTree) +end + +-------------------------------------- +-- Ability Usage Thinking +-------------------------------------- +local cast= {} cast.Desire= {} cast.Target= {} cast.Type= {} +local CanCast = {} +CanCast[1] = t -> AbilityExtensions:NormalCanCast(t, false) +CanCast[2] = CanCast[1] +CanCast[3] = t -> not AbilityExtensions:IsInvulnerable(t) or not AbilityExtensions:ShouldNotBeAttacked(t) +CanCast[4] = t -> true +CanCast[5] = CanCast[1] + +local attackRange +local healthPercent +local mana +local manaPercent +local enemies +local realEnemies +local friends +local friendCount +local enemyCreeps +local friendCreeps +local neutralCreeps +local tower + +local illusoryOrbCastLocation +local illusoryOrbMaxTravelDistance + +local Consider = {} + +local function GetIllusoryOrb() + return AbilityExtensions:First(GetLinearProjectiles(), t-> t.ability and t.ability:GetName() == "puck_illusory_orb" and t.caster == npcBot) +end +local illusoryOrb = GetIllusoryOrb() + +Consider[1] = function() + local ability = Abilities[1] + if not ability:IsFullyCastable() then + return 0 + end + local abilityLevel = ability:GetLevel() + local castRange = ability:GetCastRange() + local castPoint = ability:GetCastPoint() + local manaCost = ability:GetManaCost() + local duration = ability:GetDuration() + local damage = ability:GetDamage() + local forbiddenCreeps = AbilityExtensions:Filter(enemyCreeps, function(t) + return t:GetHealth() > t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) and (t:GetHealth() <= t:GetActualIncomingDamage(damage, DAMAGE_TYPE_MAGICAL) + AbilityExtensions:AttackOnceDamage(npcBot, t) * (0.9+#enemyCreeps*0.1) or GetUnitToUnitDistance(tower, t) <= 700) + end) + if #friendCreeps == 0 then + forbiddenCreeps = {} + end + + if AbilityExtensions:NotRetreating(npcBot) then + if local target = AbilityExtensions:GetTargetIfGood(npcBot) then + coroutine.yield(BOT_ACTION_DESIRE_HIGH, target) + end + end + if AbilityExtensions:IsFarmingOrPushing(npcBot) then + if #enemyCreeps > 3 and #forbiddenCreeps == 0 and manaPercent > 0.6 + manaCost then + coroutine.yield(BOT_ACTION_DESIRE_MODERATE, enemyCreeps[2]:GetLocation()) + end + elif AbilityExtensions:IsRetreating(npcBot) then + + end +end + +AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, Abilities) + +function AbilityUsageThink() + if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then + return + end + + attackRange = npcBot:GetAttackRange() + healthPercent = AbilityExtensions:GetHealthPercent(npcBot) + mana = npcBot:GetMana() + manaPercent = AbilityExtensions:GetManaPercent(npcBot) + + enemies = AbilityExtensions:GetNearbyHeroes(npcBot, 1599) + realEnemies = AbilityExtensions:Filter(enemies, function(t) return AbilityExtensions:MayNotBeIllusion(npcBot, t) end) + friends = AbilityExtensions:GetNearbyHeroes(npcBot, 1599, true) + friendCount = AbilityExtensions:GetEnemyHeroNumber(npcBot, friends) + enemyCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, 1599) + friendCreeps = AbilityExtensions:GetNearbyAttackableCreeps(npcBot, npcBot:GetAttackRange()+150, false) + neutralCreeps = npcBot:GetNearbyNeutralCreeps(1599) + tower = AbilityExtensions:GetLaningTower(npcBot) + + + cast=ability_item_usage_generic.ConsiderAbility(Abilities,Consider) + local abilityIndex, target, castType = ability_item_usage_generic.UseAbility(Abilities,cast) + if abilityIndex == 1 then + illusoryOrbCastLocation = npcBot:GetLocation() + illusoryOrbMaxTravelDistance = Abilities[1]:GetSpecialValueInt("max_distance") - 50 + end +end + +function CourierUsageThink() + ability_item_usage_generic.CourierUsageThink() +end diff --git a/ability_item_usage_pudge.lua b/ability_item_usage_pudge.lua index c5a68f88..5a1a2914 100644 --- a/ability_item_usage_pudge.lua +++ b/ability_item_usage_pudge.lua @@ -137,6 +137,11 @@ Consider[2] = function() do local target = npcBot:GetTarget() if target and GetUnitToUnitDistance(target, npcBot) <= radius and AbilityExtensions:NormalCanCast(target, false) then + if not AbilityExtensions:IsHero(target) or AbilityExtensions:MustBeIllusion(target) then + if npcBot:GetHealth() <= 400 or AbilityExtensions:GetHealthPercent(npcBot) <= 0.3 then + return false + end + end return true end end diff --git a/ability_item_usage_pugna.lua b/ability_item_usage_pugna.lua index 0e4d2d5d..660663b4 100644 --- a/ability_item_usage_pugna.lua +++ b/ability_item_usage_pugna.lua @@ -74,39 +74,19 @@ end local cast={} cast.Desire={} cast.Target={} cast.Type={} local Consider ={} -local function CanCast2(npcTarget) - if(utility.NCanCast(npcTarget)==false) - then - return false - end - - if(npcTarget:GetTeam()==npcBot:GetTeam()) - then - local AttackTarget=npcTarget:GetAttackTarget() - if(AttackTarget==nil) - then - return true - else - return false - end + +local function CanCast2(t) + if AbilityExtensions:IsOnSameTeam(npcBot, t) then + return AbilityExtensions:AllyCanCast(t) and not AbilityExtensions:DontInterruptAlly(t) and t:GetAttackTarget() == nil else - local allys = npcTarget:GetNearbyHeroes( 600, false, BOT_MODE_ATTACK ); - local IsAttacked=false - for i,ally in pairs(allys) - do - local AttackTarget=ally:GetAttackTarget() - if(AttackTarget~=nil and AttackTarget==npcTarget) - then - IsAttacked=true - end - end - return IsAttacked + return AbilityExtensions:NormalCanCast(t) and not AbilityExtensions:GetNearbyNonIllusionHeroes(600, false):Any(function(t) + local target = t:GetAttackTarget() + return target == t + end) end - - return true end -local CanCast={utility.NCanCast,CanCast2,utility.NCanCast,utility.UCanCast} +local CanCast={utility.NCanCast,CanCast2,utility.NCanCast,AbilityExtensions.NormalCanCastFunction} local enemyDisabled=utility.enemyDisabled function GetComboDamage() diff --git a/ability_item_usage_shadow_demon.lua b/ability_item_usage_shadow_demon.lua index c7a543a6..4bc6412c 100644 --- a/ability_item_usage_shadow_demon.lua +++ b/ability_item_usage_shadow_demon.lua @@ -87,14 +87,14 @@ function GetComboMana() end local CanCast = {} -CanCast[1] = function(t) +CanCast[1] = function(target) if target:HasModifier("modifier_shadow_demon_disruption") then return false end - if npcBot:GetTeam() == t:GetTeam() then - return AbilityExtensions:SpellCanCast(t, true, true, true) and not AbilityExtensions:DontInterruptAlly(t) and not t:IsMagicImmune() + if npcBot:GetTeam() == target:GetTeam() then + return AbilityExtensions:SpellCanCast(target, true, true, true) and not AbilityExtensions:DontInterruptAlly(target) and not target:IsMagicImmune() else - return AbilityExtensions:NormalCanCast(t, false, DAMAGE_TYPE_MAGICAL) and not target:HasModifier("modifier_antimage_counterspell") + return AbilityExtensions:NormalCanCast(target, false, DAMAGE_TYPE_MAGICAL) and not target:HasModifier("modifier_antimage_counterspell") end end CanCast[2] = function(target) @@ -276,7 +276,7 @@ Consider[2] = function() if AbilityExtensions:NotRetreating(npcBot) then local findPlace = npcBot:FindAoELocation(true, true, npcBot:GetLocation(), castRange+100, radius, 0, 0) if findPlace.count >= 3 then - if GetUnitToLocationDistance(npcBot, findPlace) <= castRange then + if GetUnitToLocationDistance(npcBot, findPlace.targetloc) <= castRange then return BOT_ACTION_DESIRE_VERYHIGH, findPlace.targetloc else return BOT_ACTION_DESIRE_MODERATE, findPlace.targetloc diff --git a/ability_item_usage_slardar.lua b/ability_item_usage_slardar.lua index 7d96f313..a152f015 100644 --- a/ability_item_usage_slardar.lua +++ b/ability_item_usage_slardar.lua @@ -375,11 +375,29 @@ Consider[4]=function() end AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal) + +local crushLosingTarget + function AbilityUsageThink() -- Check if we're already using an ability - if ( npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() ) - then + if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then + if npcBot:IsCastingAbility() then + if npcBot:GetCurrentActiveAbility() == AbilitiesReal[2] then + if not AbilityExtensions:IsFarmingOrPushing(npcBot) then + local nearbyEnemies = AbilityExtensions:GetNearbyEnemyUnits(npcBot, AbilitiesReal[2]:GetAOERadius() + 40) + if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) == 0 then + if crushLosingTarget == nil then + crushLosingTarget = DotaTime() + elseif DotaTime() - crushLosingTarget > 0.15 then + npcBot:Action_ClearActions(true) + end + return + end + end + end + end + crushLosingTarget = nil return end diff --git a/ability_item_usage_spectre.lua b/ability_item_usage_spectre.lua index 80739d15..14d6be6d 100644 --- a/ability_item_usage_spectre.lua +++ b/ability_item_usage_spectre.lua @@ -48,16 +48,16 @@ local AbilityToLevelUp= local TalentTree={ function() - return Talents[1] + return Talents[2] end, function() return Talents[4] end, function() - return Talents[6] + return Talents[5] end, function() - return Talents[8] + return Talents[7] end } @@ -286,7 +286,7 @@ Consider[6]=function() -- Mode based usage -------------------------------------- -- If we're seriously retreating, see if we can land a stun on someone who's damaged us recently - if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH ) + if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH ) and AbilityExtensions:GetHealthPercent(npcBot) >= 0.4 then for _,npcEnemy in pairs( enemys ) do @@ -324,7 +324,89 @@ Consider[6]=function() end end - return BOT_ACTION_DESIRE_NONE, 0; + return BOT_ACTION_DESIRE_NONE + +end + +Consider[5]=function() + local abilityNumber=5 + -------------------------------------- + -- Generic Variable Setting + -------------------------------------- + local ability=AbilitiesReal[abilityNumber]; + + if not ability:IsFullyCastable() or ability:IsHidden() then + return BOT_ACTION_DESIRE_NONE, 0; + end + + local CastRange = ability:GetCastRange(); + local Damage = ability:GetAbilityDamage(); + local CastPoint = ability:GetCastPoint(); + + local allys = npcBot:GetNearbyHeroes( 1200, false, BOT_MODE_NONE ); + local enemys = npcBot:GetNearbyHeroes(1600,true,BOT_MODE_NONE) + local WeakestEnemy,HeroHealth=utility.GetWeakestUnit(enemys) + -------------------------------------- + -- Global high-priorty usage + -------------------------------------- + --Try to kill enemy hero + if(npcBot:GetActiveMode() ~= BOT_MODE_RETREAT ) + then + if (WeakestEnemy~=nil) + then + if ( CanCast[abilityNumber]( WeakestEnemy ) ) + then + if(HeroHealth<=WeakestEnemy:GetActualIncomingDamage(Damage,DAMAGE_TYPE_MAGICAL) or (HeroHealth<=WeakestEnemy:GetActualIncomingDamage(GetComboDamage(),DAMAGE_TYPE_MAGICAL) and npcBot:GetMana()>ComboMana)) + then + return BOT_ACTION_DESIRE_HIGH, WeakestEnemy:GetLocation() + end + end + end + end + + -------------------------------------- + -- Mode based usage + -------------------------------------- + -- If we're seriously retreating, see if we can land a stun on someone who's damaged us recently + if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH ) and AbilityExtensions:GetHealthPercent(npcBot) >= 0.4 + then + for _,npcEnemy in pairs( enemys ) + do + if ( npcBot:WasRecentlyDamagedByHero( npcEnemy, 2.0 ) ) + then + if ( CanCast[abilityNumber]( npcEnemy )) + then + return BOT_ACTION_DESIRE_HIGH, npcEnemy:GetLocation() + end + end + end + end + + -- If we're in a teamfight, use it on the scariest enemy + local tableNearbyAttackingAlliedHeroes = npcBot:GetNearbyHeroes( 1000, false, BOT_MODE_ATTACK ); + if ( #tableNearbyAttackingAlliedHeroes+#enemys >= 5 and #enemys>=1) + then + return BOT_ACTION_DESIRE_MODERATE, enemys[1]:GetLocation() + end + + -- If we're going after someone + if ( npcBot:GetActiveMode() == BOT_MODE_ROAM or + npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or + npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or + npcBot:GetActiveMode() == BOT_MODE_ATTACK ) + then + local npcEnemy = npcBot:GetTarget(); + + if ( npcEnemy ~= nil ) + then + if ( CanCast[abilityNumber]( npcEnemy ) and GetUnitToUnitDistance(npcBot,npcEnemy)< CastRange + 75*#allys) + then + return BOT_ACTION_DESIRE_MODERATE, npcEnemy:GetLocation() + end + end + end + + return BOT_ACTION_DESIRE_NONE end @@ -373,7 +455,7 @@ Consider[4]=function() -- Mode based usage -------------------------------------- -- If we're seriously retreating, see if we can land a stun on someone who's damaged us recently - if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH ) + if ( npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH) then for _,npcEnemy in pairs( enemys ) do diff --git a/ability_item_usage_tidehunter.lua b/ability_item_usage_tidehunter.lua index 2e09df64..724f3aed 100644 --- a/ability_item_usage_tidehunter.lua +++ b/ability_item_usage_tidehunter.lua @@ -73,7 +73,7 @@ end -------------------------------------- local cast={} cast.Desire={} cast.Target={} cast.Type={} local Consider ={} -local CanCast={utility.NCanCast,utility.NCanCast,utility.NCanCast,utility.NCanCast} +local CanCast={AbilityExtensions.NormalCanCastFunction,utility.UCanCast,AbilityExtensions.NormalCanCastFunction,AbilityExtensions.NormalCanCastFunction} local enemyDisabled=utility.enemyDisabled function GetComboDamage() @@ -313,7 +313,7 @@ Consider[3]=function() local allys = npcBot:GetNearbyHeroes( 1200, false, BOT_MODE_NONE ); - local enemys = npcBot:GetNearbyHeroes(Radius,true,BOT_MODE_NONE) + local enemys = AbilityExtensions:GetNearbyNonIllusionHeroes(Radius, true):Filter(CanCast[abilityNumber]) local WeakestEnemy,HeroHealth=utility.GetWeakestUnit(enemys) local creeps = npcBot:GetNearbyCreeps(Radius,true) local WeakestCreep,CreepHealth=utility.GetWeakestUnit(creeps) diff --git a/ability_item_usage_tusk.lua b/ability_item_usage_tusk.lua index a1a7eba1..2f8bcec6 100644 --- a/ability_item_usage_tusk.lua +++ b/ability_item_usage_tusk.lua @@ -555,6 +555,9 @@ Consider[7] = function() if snowballTarget == nil then RefreshSnowballTarget() end + if snowballTarget == nil then + return 0 + end if AbilityExtensions:HasAbilityRetargetModifier(snowballTarget) and not snowballTarget:IsChanneling() then return 0 diff --git a/ability_item_usage_warlock.lua b/ability_item_usage_warlock.lua index 259f6012..18e25d8b 100644 --- a/ability_item_usage_warlock.lua +++ b/ability_item_usage_warlock.lua @@ -556,7 +556,7 @@ function AbilityUsageThink() end) if enemies == 0 then if DotaTime() > upheavelTimer + 1.5 then - npcBot:Action_ClearActions() + npcBot:Action_ClearActions(true) upheavelTimer = nil end else diff --git a/bot_keeper_of_the_light.lua b/bot_keeper_of_the_light.lua new file mode 100644 index 00000000..48e2ca5a --- /dev/null +++ b/bot_keeper_of_the_light.lua @@ -0,0 +1,13 @@ +local minionutils = dofile( GetScriptDirectory().."/util/NewMinionUtil" ) + +function MinionThink( hMinionUnit ) + if minionutils.IsValidUnit(hMinionUnit) then + if hMinionUnit:IsIllusion() then + minionutils.IllusionThink(hMinionUnit) + elseif minionutils.CantBeControlled(hMinionUnit:GetUnitName()) then + minionutils.CantBeControlledThink(hMinionUnit) + else + return + end + end +end diff --git a/bot_leshrac.lua b/bot_leshrac.lua new file mode 100644 index 00000000..9443cffe --- /dev/null +++ b/bot_leshrac.lua @@ -0,0 +1,13 @@ +local minionutils = dofile( GetScriptDirectory().."/util/NewMinionUtil" ) + +function MinionThink( hMinionUnit ) + if minionutils.IsValidUnit(hMinionUnit) then + if hMinionUnit:IsIllusion() then + minionutils.IllusionThink(hMinionUnit) + elseif minionutils.CantBeControlled(hMinionUnit:GetUnitName()) then + minionutils.CantBeControlledThink(hMinionUnit) + else + return + end + end +end \ No newline at end of file diff --git a/changelog/changelog_en.txt b/changelog/changelog_en.txt index c10bc446..e46ae076 100644 --- a/changelog/changelog_en.txt +++ b/changelog/changelog_en.txt @@ -358,16 +358,16 @@ v1.7.4 (2021.05.08) Updated by AaronSong321 1. Fix lots of bugs; 2. Add Ember Spirit. v1.7.5 (2021.05.17) -1. Fix warding system; -2. Temporarily removing armlet to fix game slowdown. +1. Fix the warding system; +2. Temporarily remove armlet to fix game slowdown. v1.7.6 (2021.08.18) Optimize Item Purchase for 7.29. 1. Add Wind Waker for heroes who plan to buy Cyclone. 2. Add Octarine Core for heroes who plan to buy Aether Lens. -3. Add Aghanim's Shard for heroes who's Shard is powerful and easy to use. +3. Add Aghanim's Shard for heroes whose Shard ability is powerful and easy to use. 4. Add Aghanim's Blessing for heroes who have Aghanim's Scepter in late game. -5. Add an extra item for heroes who's last item is Aghanim's Blessing. -6. Optimize item purchasing list of Dark Seeker and Orge Magi. +5. Add an extra item for heroes whose last item is Aghanim's Blessing. +6. Optimize the item purchasing lists of Dark Seeker and Orge Magi. 7. Fix typo in item_purchase_treant. 8. Remove Item Necronomicon. 9. Change item purchase order to relieve backpack congestion. diff --git a/item_purchase_alchemist.lua b/item_purchase_alchemist.lua index 9f2d650b..1d7b13c0 100644 --- a/item_purchase_alchemist.lua +++ b/item_purchase_alchemist.lua @@ -17,9 +17,10 @@ local ItemsToBuy = "item_black_king_bar", --BKB "item_basher", --晕锤7.14 "item_assault", --强袭 - "item_mjollnir", --大电锤 + -- "item_mjollnir", --大电锤 "item_abyssal_blade", --大晕 "item_travel_boots", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_ancient_apparition.lua b/item_purchase_ancient_apparition.lua index 9e728958..123a4322 100644 --- a/item_purchase_ancient_apparition.lua +++ b/item_purchase_ancient_apparition.lua @@ -14,7 +14,6 @@ local ItemsToBuy = "item_flask", "item_magic_wand", "item_null_talisman", - -- "item_ring_of_basilius", "item_energy_booster", "item_glimmer_cape", --微光 "item_ghost", diff --git a/item_purchase_antimage.lua b/item_purchase_antimage.lua index 1e9f59a5..89f68d38 100644 --- a/item_purchase_antimage.lua +++ b/item_purchase_antimage.lua @@ -14,6 +14,7 @@ local p = { "item_orb_of_corrosion", "item_bfury", "item_manta", + "item_basher", "item_abyssal_blade", "item_black_king_bar", "item_butterfly", diff --git a/item_purchase_axe.lua b/item_purchase_axe.lua index 6b0658d2..5783ea6d 100644 --- a/item_purchase_axe.lua +++ b/item_purchase_axe.lua @@ -20,6 +20,7 @@ local ItemsToBuy = "item_heart", --龙心7.20 "item_lotus_orb", --清莲宝珠, "item_assault", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_bane.lua b/item_purchase_bane.lua index e4385f45..1664881e 100644 --- a/item_purchase_bane.lua +++ b/item_purchase_bane.lua @@ -9,8 +9,7 @@ local ItemsToBuy = "item_tango", "item_tango", "item_clarity", - "item_boots", - -- "item_ring_of_basilius", --圣殿 + "item_boots", --圣殿 "item_magic_wand", --大魔棒7.14 "item_energy_booster", "item_null_talisman", diff --git a/item_purchase_batrider.lua b/item_purchase_batrider.lua index 7831ce67..5e73edb3 100644 --- a/item_purchase_batrider.lua +++ b/item_purchase_batrider.lua @@ -19,7 +19,8 @@ local ItemsToBuy = "item_cyclone", --风杖 "item_ultimate_scepter_2", "item_lotus_orb", - "item_wind_waker" + "item_arcane_blink", + "item_wind_waker", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_brewmaster.lua b/item_purchase_brewmaster.lua index a1600d2e..21f5a15b 100644 --- a/item_purchase_brewmaster.lua +++ b/item_purchase_brewmaster.lua @@ -17,8 +17,9 @@ local ItemsToBuy = "item_black_king_bar", --BKB "item_ultimate_scepter", --蓝杖 "item_lotus_orb", --清莲宝珠 + "item_overwhelming_blink", "item_ultimate_scepter_2", - "item_assault" --强袭 + "item_assault", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_centaur.lua b/item_purchase_centaur.lua index f6f6a1e0..ca1fd80d 100644 --- a/item_purchase_centaur.lua +++ b/item_purchase_centaur.lua @@ -21,6 +21,7 @@ local ItemsToBuy = "item_heart", --龙心7.20 "item_ultimate_scepter", --蓝杖 "item_ultimate_scepter_2", + "item_overwhelming_blink", "item_assault", } diff --git a/item_purchase_crystal_maiden.lua b/item_purchase_crystal_maiden.lua index 875d3349..c2f1e110 100644 --- a/item_purchase_crystal_maiden.lua +++ b/item_purchase_crystal_maiden.lua @@ -9,7 +9,6 @@ local ItemsToBuy = "item_tango", "item_flask", "item_clarity", - -- "item_ring_of_basilius", "item_tranquil_boots", "item_bracer", "item_glimmer_cape", --微光 diff --git a/item_purchase_dark_seer.lua b/item_purchase_dark_seer.lua index ec20d38c..dbe57c70 100644 --- a/item_purchase_dark_seer.lua +++ b/item_purchase_dark_seer.lua @@ -19,6 +19,7 @@ local ItemsToBuy = "item_blink", --跳刀 "item_lotus_orb", --清莲宝珠 "item_shivas_guard", --希瓦 + "item_arcane_blink", "item_ultimate_scepter_2", "item_heart" --龙心7.20 } diff --git a/item_purchase_dazzle.lua b/item_purchase_dazzle.lua index e2f67179..5af9f7fc 100644 --- a/item_purchase_dazzle.lua +++ b/item_purchase_dazzle.lua @@ -7,7 +7,6 @@ local ItemPurchaseSystem = dofile(GetScriptDirectory() .. "/util/ItemPurchaseSys local ItemsToBuy = { "item_tango", - -- "item_ring_of_basilius", "item_tango", "item_wind_lace", "item_boots", diff --git a/item_purchase_dragon_knight.lua b/item_purchase_dragon_knight.lua index 05f8064e..7880a601 100644 --- a/item_purchase_dragon_knight.lua +++ b/item_purchase_dragon_knight.lua @@ -17,7 +17,8 @@ local ItemsToBuy = "item_blink", "item_mjollnir", --大雷锤 "item_assault", --强袭 - "item_greater_crit" --大炮 + "item_greater_crit", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_earth_spirit.lua b/item_purchase_earth_spirit.lua index a192e93c..5df4b6bf 100644 --- a/item_purchase_earth_spirit.lua +++ b/item_purchase_earth_spirit.lua @@ -20,6 +20,7 @@ local ItemsToBuy = "item_wind_waker", "item_ultimate_scepter_2", "item_sheepstick", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_earthshaker.lua b/item_purchase_earthshaker.lua index 7bbaa179..0b138b53 100644 --- a/item_purchase_earthshaker.lua +++ b/item_purchase_earthshaker.lua @@ -15,8 +15,10 @@ local ItemsToBuy = "item_blink", --跳刀 "item_force_staff", --推推7.14 "item_ultimate_scepter", --蓝杖 + "item_aeon_disk", "item_ultimate_scepter_2", "item_black_king_bar", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_elder_titan.lua b/item_purchase_elder_titan.lua index 0e731280..03f09361 100644 --- a/item_purchase_elder_titan.lua +++ b/item_purchase_elder_titan.lua @@ -13,8 +13,7 @@ local ItemsToBuy = "item_tranquil_boots", "item_circlet", "item_ring_of_protection", - "item_recipe_urn_of_shadows", - "item_infused_raindrop", --骨灰盒7.06 + "item_urn_of_shadows", "item_pipe", --笛子 "item_rod_of_atos", --阿托斯7.20 "item_ultimate_scepter", --蓝杖 diff --git a/item_purchase_ember_spirit.lua b/item_purchase_ember_spirit.lua index 3e82d849..460a622a 100644 --- a/item_purchase_ember_spirit.lua +++ b/item_purchase_ember_spirit.lua @@ -8,11 +8,11 @@ local ItemsToBuy = { "item_quelling_blade", "item_circlet", - "item_bottle", + -- "item_bottle", "item_wraith_band", "item_phase_boots", "item_bfury", - "item_magic_wand", --大魔棒7.14 + "item_magic_wand", "item_lesser_crit", "item_black_king_bar", "item_greater_crit", diff --git a/item_purchase_enigma.lua b/item_purchase_enigma.lua index eb822c72..23383eb5 100644 --- a/item_purchase_enigma.lua +++ b/item_purchase_enigma.lua @@ -8,12 +8,11 @@ local ItemsToBuy = { "item_tango", "item_clarity", - -- "item_ring_of_basilius", "item_magic_wand", --大魔棒7.14 "item_arcane_boots", --秘法鞋 "item_blink", --跳刀 - "item_guardian_greaves", --卫士胫甲 "item_black_king_bar", --bkb + "item_guardian_greaves", --卫士胫甲 "item_aghanims_shard", "item_vitality_booster", "item_energy_booster", @@ -21,7 +20,8 @@ local ItemsToBuy = "item_ultimate_scepter", --蓝杖 "item_octarine_core", --玲珑心 "item_ultimate_scepter_2", - "item_refresher" --刷新球 + "item_refresher", + "item_arcane_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_huskar.lua b/item_purchase_huskar.lua index 8f508b71..d32a5b3a 100644 --- a/item_purchase_huskar.lua +++ b/item_purchase_huskar.lua @@ -18,8 +18,7 @@ local ItemsToBuy = "item_ultimate_scepter", --蓝杖 "item_satanic", --撒旦7.07 "item_aghanims_shard", - "item_ultimate_scepter_2", - "item_sange_and_yasha" + -- "item_ultimate_scepter_2", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_jakiro.lua b/item_purchase_jakiro.lua index 0fd5d07d..504a4ba9 100644 --- a/item_purchase_jakiro.lua +++ b/item_purchase_jakiro.lua @@ -7,7 +7,6 @@ local ItemPurchaseSystem = dofile(GetScriptDirectory() .. "/util/ItemPurchaseSys local ItemsToBuy = { "item_tango", - -- "item_ring_of_basilius", -- "item_tango", "item_clarity", "item_magic_stick", diff --git a/item_purchase_juggernaut.lua b/item_purchase_juggernaut.lua index b9b6587a..980e66a8 100644 --- a/item_purchase_juggernaut.lua +++ b/item_purchase_juggernaut.lua @@ -14,10 +14,10 @@ local ItemsToBuy = "item_wraith_band", "item_phase_boots", --相位7.21 "item_maelstrom", - "item_yasha", --夜叉 - "item_ultimate_orb", - "item_recipe_manta", --分身 + "item_yasha", + "item_manta", "item_aghanims_shard", + "item_basher", "item_abyssal_blade", --大晕锤 "item_butterfly", --蝴蝶 "item_ultimate_scepter", diff --git a/item_purchase_keeper_of_the_light.lua b/item_purchase_keeper_of_the_light.lua index d4b57841..8a11d5c4 100644 --- a/item_purchase_keeper_of_the_light.lua +++ b/item_purchase_keeper_of_the_light.lua @@ -7,11 +7,8 @@ local ItemPurchaseSystem = dofile(GetScriptDirectory() .. "/util/ItemPurchaseSys local ItemsToBuy = { "item_tango", - "item_null_talisman", "item_tango", "item_null_talisman", - "item_null_talisman", - "item_null_talisman", "item_wind_lace", "item_boots", "item_tranquil_boots", diff --git a/item_purchase_kunkka.lua b/item_purchase_kunkka.lua index f1e979da..610119ad 100644 --- a/item_purchase_kunkka.lua +++ b/item_purchase_kunkka.lua @@ -8,9 +8,9 @@ local ItemPurchaseSystem = dofile(GetScriptDirectory() .. "/util/ItemPurchaseSys local p = { "item_tango", "item_quelling_blade", --补刀斧 - "item_magic_wand", --大魔棒7.14 "item_bracer", "item_phase_boots", --相位7.21 + "item_magic_wand", --大魔棒7.14 "item_invis_sword", --隐刀 "item_black_king_bar", --bkb "item_greater_crit", --大炮 diff --git a/item_purchase_leshrac.lua b/item_purchase_leshrac.lua index 843eb20e..8ad06af7 100644 --- a/item_purchase_leshrac.lua +++ b/item_purchase_leshrac.lua @@ -16,9 +16,7 @@ local ItemsToBuy = "item_veil_of_discord", --纷争7.20 "item_cyclone", --风杖 "item_aghanims_shard", - "item_octarine_core", --玲珑心 - -- "item_mekansm", - -- "item_uardian_greaves", + "item_octarine_core", "item_ultimate_scepter", "item_ultimate_scepter_2", "item_shivas_guard", --希瓦 diff --git a/item_purchase_lich.lua b/item_purchase_lich.lua index f332f1d1..7132256e 100644 --- a/item_purchase_lich.lua +++ b/item_purchase_lich.lua @@ -13,7 +13,7 @@ local ItemsToBuy = "item_boots", "item_tranquil_boots", "item_urn_of_shadows", - "item_ghost", + -- "item_ghost", "item_glimmer_cape", --微光 "item_force_staff", "item_lotus_orb", --清莲宝珠 diff --git a/item_purchase_life_stealer.lua b/item_purchase_life_stealer.lua index 64e81231..7050816b 100644 --- a/item_purchase_life_stealer.lua +++ b/item_purchase_life_stealer.lua @@ -14,11 +14,10 @@ local ItemsToBuy = "item_phase_boots", --相位7.21 "item_orb_of_corrosion", "item_magic_wand", --大魔棒7.14 - --"item_armlet", --臂章 - --"item_scepter_shard", - "item_sange_and_yasha", + "item_basher", "item_desolator", "item_abyssal_blade", + "item_sange_and_yasha", "item_assault" } diff --git a/item_purchase_lion.lua b/item_purchase_lion.lua index 7774b62d..9f351982 100644 --- a/item_purchase_lion.lua +++ b/item_purchase_lion.lua @@ -21,6 +21,7 @@ local ItemsToBuy = "item_ultimate_scepter_2", "item_lotus_orb", --清莲宝珠 "item_octarine_core", + "item_arcane_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_lycan.lua b/item_purchase_lycan.lua index c18acf2d..b5547c88 100644 --- a/item_purchase_lycan.lua +++ b/item_purchase_lycan.lua @@ -10,6 +10,7 @@ local ItemsToBuy = "item_flask", "item_quelling_blade", --补刀斧 "item_power_treads", --假腿7.21 + "item_orb_of_corrosion", "item_helm_of_the_dominator", "item_vladmir", --祭品7.21 "item_helm_of_the_overlord", --level 2 helm dominator 7.30 diff --git a/item_purchase_magnataur.lua b/item_purchase_magnataur.lua index 1c8a9938..544324c7 100644 --- a/item_purchase_magnataur.lua +++ b/item_purchase_magnataur.lua @@ -17,7 +17,8 @@ local ItemsToBuy = "item_greater_crit", --大炮 "item_ultimate_scepter", --A "item_ultimate_scepter_2", - "item_assault" + "item_assault", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_night_stalker.lua b/item_purchase_night_stalker.lua index f917bedf..cc5ff4dd 100644 --- a/item_purchase_night_stalker.lua +++ b/item_purchase_night_stalker.lua @@ -17,7 +17,8 @@ local ItemsToBuy = "item_ultimate_scepter", "item_assault", --强袭 "item_ultimate_scepter_2", - "item_heart" + "item_heart", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_nyx_assassin.lua b/item_purchase_nyx_assassin.lua index 7c0daba3..43b593d9 100644 --- a/item_purchase_nyx_assassin.lua +++ b/item_purchase_nyx_assassin.lua @@ -21,10 +21,11 @@ local ItemsToBuy = "item_guardian_greaves", "item_recipe_dagon", "item_recipe_dagon", + "item_overwhelming_blink", "item_ultimate_scepter_2", "item_sheepstick", "item_recipe_dagon", - "item_recipe_dagon" --红杖 + "item_recipe_dagon", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_oracle.lua b/item_purchase_oracle.lua index 7676658d..7eb86446 100644 --- a/item_purchase_oracle.lua +++ b/item_purchase_oracle.lua @@ -9,7 +9,6 @@ local ItemsToBuy = "item_tango", "item_clarity", "item_flask", - -- "item_ring_of_basilius", "item_tranquil_boots", "item_urn_of_shadows", "item_ghost", diff --git a/item_purchase_phantom_assassin.lua b/item_purchase_phantom_assassin.lua index f9335925..23a02be7 100644 --- a/item_purchase_phantom_assassin.lua +++ b/item_purchase_phantom_assassin.lua @@ -15,6 +15,7 @@ local ItemsToBuy = "item_bfury", "item_desolator", "item_black_king_bar", --bkb + "item_basher", "item_abyssal_blade", --大晕锤 "item_satanic", --撒旦7.07 "item_assault" diff --git a/item_purchase_pudge.lua b/item_purchase_pudge.lua index 669d1d94..4a2716a7 100644 --- a/item_purchase_pudge.lua +++ b/item_purchase_pudge.lua @@ -20,7 +20,7 @@ local ItemsToBuy = "item_glimmer_cape", --微光 "item_force_staff", "item_ultimate_scepter_2", - "item_lotus_orb", --清莲宝珠 + "item_lotus_orb", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_riki.lua b/item_purchase_riki.lua index 3f20789a..ad9318a5 100644 --- a/item_purchase_riki.lua +++ b/item_purchase_riki.lua @@ -13,7 +13,7 @@ local ItemsToBuy = "item_power_treads", "item_diffusal_blade", --散失刀 "item_sange_and_yasha", --双刀 - --"item_mage_slayer", + "item_basher", "item_black_king_bar", --bkb "item_abyssal_blade" --大晕锤 } diff --git a/item_purchase_sand_king.lua b/item_purchase_sand_king.lua index 44beb7d2..e149de20 100644 --- a/item_purchase_sand_king.lua +++ b/item_purchase_sand_king.lua @@ -18,7 +18,8 @@ local ItemsToBuy = "item_black_king_bar", --bkb "item_aghanims_shard", "item_ultimate_scepter_2", - "item_lotus_orb" --清莲宝珠 + "item_lotus_orb", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_shadow_shaman.lua b/item_purchase_shadow_shaman.lua index 9d44eb90..10e049b6 100644 --- a/item_purchase_shadow_shaman.lua +++ b/item_purchase_shadow_shaman.lua @@ -21,6 +21,7 @@ local ItemsToBuy = "item_ultimate_scepter_2", "item_sheepstick", --羊刀 "item_octarine_core", + "item_arcane_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_skeleton_king.lua b/item_purchase_skeleton_king.lua index f33980f8..05c3bb0f 100644 --- a/item_purchase_skeleton_king.lua +++ b/item_purchase_skeleton_king.lua @@ -23,6 +23,7 @@ local ItemsToBuy = "item_blink", "item_assault", --强袭 "item_ultimate_scepter", --蓝杖 + "item_overwhelming_blink", "item_ultimate_scepter_2", "item_monkey_king_bar", } diff --git a/item_purchase_slardar.lua b/item_purchase_slardar.lua index 0f13dfba..7e2b9d9b 100644 --- a/item_purchase_slardar.lua +++ b/item_purchase_slardar.lua @@ -16,7 +16,8 @@ local ItemsToBuy = "item_echo_sabre", --连击刀 "item_black_king_bar", --bkb "item_assault", --强袭 - "item_heart" --龙心7.20 + "item_heart", + "item_overwhelming_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_slark.lua b/item_purchase_slark.lua index 9483aa81..1f27ce37 100644 --- a/item_purchase_slark.lua +++ b/item_purchase_slark.lua @@ -18,6 +18,7 @@ local ItemsToBuy = "item_basher", "item_black_king_bar", --bkb "item_diffusal_blade", --散失 + "item_basher", "item_abyssal_blade", --大晕锤 "item_butterfly", } diff --git a/item_purchase_sven.lua b/item_purchase_sven.lua index 87b7d4a7..30a080b0 100644 --- a/item_purchase_sven.lua +++ b/item_purchase_sven.lua @@ -15,9 +15,9 @@ local ItemsToBuy = "item_greater_crit", --大炮 "item_ultimate_scepter", "item_blink", - "item_sange_and_yasha", + "item_assault", + "item_overwhelming_blink", "item_ultimate_scepter_2", - "item_assault" --强袭 } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_templar_assassin.lua b/item_purchase_templar_assassin.lua index 5ddf32d5..d2f06e3b 100644 --- a/item_purchase_templar_assassin.lua +++ b/item_purchase_templar_assassin.lua @@ -17,6 +17,7 @@ local ItemsToBuy = "item_blink", "item_butterfly", --蝴蝶 "item_bloodthorn", + "item_swift_blink", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_tidehunter.lua b/item_purchase_tidehunter.lua index 395c8cd5..c8e63498 100644 --- a/item_purchase_tidehunter.lua +++ b/item_purchase_tidehunter.lua @@ -19,8 +19,9 @@ local ItemsToBuy = "item_medallion_of_courage", "item_solar_crest", "item_aghanims_shard", + "item_overwhelming_blink", "item_helm_of_the_overlord", --level 2 helm dominator 7.30 - "item_shivas_guard" --希瓦 + "item_shivas_guard", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/item_purchase_tiny.lua b/item_purchase_tiny.lua index ae51a3b2..d31405eb 100644 --- a/item_purchase_tiny.lua +++ b/item_purchase_tiny.lua @@ -17,6 +17,7 @@ local ItemsToBuy = "item_black_king_bar", --bkb "item_assault", --强袭 "item_ultimate_scepter", + "item_overwhelming_blink", "item_ultimate_scepter_2", "item_greater_crit", } diff --git a/item_purchase_tusk.lua b/item_purchase_tusk.lua index d9a99531..100ac72a 100644 --- a/item_purchase_tusk.lua +++ b/item_purchase_tusk.lua @@ -17,6 +17,7 @@ local ItemsToBuy = "item_aghanims_shard", "item_black_king_bar", --bkb "item_ultimate_scepter", --蓝杖 + "item_aghanims_shard", "item_silver_edge", --大隐刀 "item_ultimate_scepter_2", "item_sheepstick", diff --git a/item_purchase_ursa.lua b/item_purchase_ursa.lua index 1af4e25b..dbe5da6c 100644 --- a/item_purchase_ursa.lua +++ b/item_purchase_ursa.lua @@ -20,8 +20,10 @@ local ItemsToBuy = "item_basher", --晕锤7.14 "item_black_king_bar", --bkb "item_aghanims_shard", + "item_basher", "item_ultimate_scepter", - "item_abyssal_blade", --大晕锤 + "item_abyssal_blade", + "item_overwhelming_blink", "item_ultimate_scepter_2", "item_skadi", } diff --git a/item_purchase_vengefulspirit.lua b/item_purchase_vengefulspirit.lua index a8b4e5b8..9f2bcb76 100644 --- a/item_purchase_vengefulspirit.lua +++ b/item_purchase_vengefulspirit.lua @@ -8,7 +8,6 @@ local ItemsToBuy = { "item_tango", "item_clarity", - -- "item_ring_of_basilius", "item_magic_stick", "item_power_treads", --假腿7.21 "item_medallion_of_courage", --勋章 diff --git a/item_purchase_windrunner.lua b/item_purchase_windrunner.lua index 363eab58..fbb90df7 100644 --- a/item_purchase_windrunner.lua +++ b/item_purchase_windrunner.lua @@ -18,9 +18,10 @@ local ItemsToBuy = "item_black_king_bar", "item_monkey_king_bar", "item_ultimate_scepter", + "item_arcane_blink", "item_ultimate_scepter_2", "item_sphere", --林肯 - "item_mjollnir" --大雷锤 + "item_mjollnir", } ItemPurchaseSystem:CreateItemInformationTable(GetBot(), ItemsToBuy) diff --git a/util/AbilityAbstraction.lua b/util/AbilityAbstraction.lua index fe075c11..317087ca 100644 --- a/util/AbilityAbstraction.lua +++ b/util/AbilityAbstraction.lua @@ -1,9 +1,5 @@ local M = {} - local binlib = require(GetScriptDirectory().."/util/BinDecHex") - --- LINQ functions - local magicTable = {} local function NewTable() local a = {} @@ -11,18 +7,18 @@ local function NewTable() return a end magicTable.__index = magicTable - M.Range = function(self, min, max, step) - if step == nil then step = 1 end + if step == nil then + step = 1 + end local g = NewTable() for i = min, max, step do table.insert(g, i) end return g end - M.Contains = function(self, tb, value, equals) - equals = equals or function(a, b) return a == b end + equals = equals or function(__mira_olpar_1, __mira_olpar_2) return __mira_olpar_1 == __mira_olpar_2 end for _, v in ipairs(tb) do if equals(v, value) then return true @@ -30,9 +26,8 @@ M.Contains = function(self, tb, value, equals) end return false end - M.ContainsKey = function(self, tb, key, equals) - equals = equals or function(a, b) return a == b end + equals = equals or function(__mira_olpar_1, __mira_olpar_2) return __mira_olpar_1 == __mira_olpar_2 end for k, _ in pairs(tb) do if equals(key, k) then return true @@ -40,7 +35,6 @@ M.ContainsKey = function(self, tb, key, equals) end return false end - function M:Keys(tb) local g = NewTable() for k, _ in pairs(tb) do @@ -48,7 +42,6 @@ function M:Keys(tb) end return g end - M.Filter = function(self, tb, filter) local g = NewTable() for k, v in ipairs(tb) do @@ -67,7 +60,6 @@ M.FilterNot = function(self, tb, filter) end return g end - M.Count = function(self, tb, filter) local g = 0 for k, v in ipairs(tb) do @@ -77,13 +69,11 @@ M.Count = function(self, tb, filter) end return g end - function M:NonEmpty(self, tb) return self:Filter(tb, function(t) return t ~= nil and #t ~= 0 end) end - M.Map = function(self, tb, transform) local g = NewTable() for k, v in ipairs(tb) do @@ -91,7 +81,6 @@ M.Map = function(self, tb, transform) end return g end - function M:MapDic(tb, transform) local g = NewTable() for k, v in pairs(tb) do @@ -99,13 +88,11 @@ function M:MapDic(tb, transform) end return g end - M.ForEach = function(self, tb, action) for k, v in ipairs(tb) do action(v, k) end end - M.Any = function(self, tb, filter) for k, v in ipairs(tb) do if filter == nil or filter(v, k) then @@ -114,7 +101,6 @@ M.Any = function(self, tb, filter) end return false end - M.All = function(self, tb, filter) for k, v in ipairs(tb) do if not filter(v, k) then @@ -123,14 +109,12 @@ M.All = function(self, tb, filter) end return true end - M.Aggregate = function(self, seed, tb, aggregate) for k, v in ipairs(tb) do seed = aggregate(seed, v, k) end return seed end - M.ShallowCopy = function(self, tb) local g = NewTable() for k, v in pairs(tb) do @@ -138,7 +122,6 @@ M.ShallowCopy = function(self, tb) end return g end - M.First = function(self, tb, filter) for k, v in ipairs(tb) do if filter == nil or filter(v, k) then @@ -146,7 +129,6 @@ M.First = function(self, tb, filter) end end end - M.Skip = function(self, tb, number) local g = NewTable() local i = 0 @@ -158,7 +140,6 @@ M.Skip = function(self, tb, number) end return g end - M.Take = function(self, tb, number) local g = NewTable() local i = 0 @@ -168,11 +149,11 @@ M.Take = function(self, tb, number) table.insert(g, v) else break + end end return g end - local function deepCopy(self, tb) local copiedTables = NewTable() local g = NewTable() @@ -191,7 +172,6 @@ local function deepCopy(self, tb) return g end M.DeepCopy = deepCopy - M.Concat = function(self, a, ...) local g = NewTable() local rec @@ -207,10 +187,9 @@ M.Concat = function(self, a, ...) rec(a, ...) return g end - M.Remove = function(self, a, b) local g = self:ShallowCopy(a) - for k,v in pairs(a) do + for k, v in pairs(a) do if v == b then g[k] = nil end @@ -219,21 +198,23 @@ M.Remove = function(self, a, b) end M.RemoveAll = function(self, a, b) local g = NewTable() - for _,v in pairs(a) do + for _, v in pairs(a) do if not self:Contains(b, v) then table.insert(g, v) end end return g end - M.Prepend = function(self, a, b) return self:Concat(b, a) end - M.GroupBy = function(self, collection, keySelector, elementSelector, resultSelector, comparer) - comparer = comparer or function(a,b) return a==b end - resultSelector = resultSelector or function(key, value) return value end + comparer = comparer or function(a, b) + return a == b + end + resultSelector = resultSelector or function(key, value) + return value + end elementSelector = elementSelector or self.IdentityFunction local keys = NewTable() local values = NewTable() @@ -244,16 +225,16 @@ M.GroupBy = function(self, collection, keySelector, elementSelector, resultSelec keyFound = true table.insert(values[readKeyIndex], elementSelector(k)) break + end end if not keyFound then table.insert(keys, keySelector(k)) - table.insert(values, { elementSelector(k) }) + table.insert(values, { elementSelector(k) }) end end return self:Map2(keys, values, resultSelector) end - M.Partition = function(self, tb, filter) local a = NewTable() local b = NewTable() @@ -266,9 +247,8 @@ M.Partition = function(self, tb, filter) end return a, b end - M.Distinct = function(self, tb, equals) - equals = equals or function(a, b) return a == b end + equals = equals or function(__mira_olpar_1, __mira_olpar_2) return __mira_olpar_1 == __mira_olpar_2 end local g = NewTable() for _, v in pairs(tb) do if not self:Contains(g, v, equals) then @@ -277,28 +257,28 @@ M.Distinct = function(self, tb, equals) end return g end - -M.Reverse = function(self, tb) +M.Reverse = function(self, tb) local g = NewTable() for i = #tb, 1, -1 do table.insert(g, tb[i]) end return g end - M.Last = function(self, tb, filter) return self:First(self:Reverse(tb), filter) end - -function M:Identity(t) return t end -M.IdentityFunction = function(t) return t end - +function M:Identity(t) + return t +end +M.IdentityFunction = function(t) + return t +end function M:Max(tb, map) if #tb == 0 then return nil end map = map or self.IdentityFunction - local maxv, maxm = tb[1], map(tb[1]) + local maxv,maxm = tb[1], map(tb[1]) for i = 2, #tb do local m = map(tb[i]) if m > maxm then @@ -308,13 +288,12 @@ function M:Max(tb, map) end return maxv end - function M:Min(tb, map) if #tb == 0 then return nil end map = map or self.IdentityFunction - local maxv, maxm = tb[1], map(tb[1]) + local maxv,maxm = tb[1], map(tb[1]) for i = 2, #tb do local m = map(tb[i]) if m < maxm then @@ -324,15 +303,13 @@ function M:Min(tb, map) end return maxv end - M.Repeat = function(self, element, count) local g = NewTable() for i = 1, count do table.insert(g, element) end - return g + return g end - M.Select = M.Map M.SelectMany = function(self, tb, map, filter) local g = NewTable() @@ -346,13 +323,10 @@ M.SelectMany = function(self, tb, map, filter) end return g end - M.Where = M.Filter - M.SkipLast = function(self, tb, number) return self:Skip(self:Reverse(tb), number) end - M.Replace = function(self, tb, filter, map) local g = NewTable() for k, v in ipairs(tb) do @@ -364,7 +338,6 @@ M.Replace = function(self, tb, filter, map) end return g end - M.IndexOf = function(self, tb, filter) local g = NewTable() for k, v in ipairs(tb) do @@ -374,17 +347,19 @@ M.IndexOf = function(self, tb, filter) end elseif filter ~= nil then if v == filter then - return k + return k end end end return -1 end - -M.Zip2 = function(self, tb1, tb2, map) +M.Zip2 = function(self, tb1, tb2, map) if map == nil then map = function(a, b) - return {a, b} + return { + a, + b, + } end end local g = NewTable() @@ -393,24 +368,27 @@ M.Zip2 = function(self, tb1, tb2, map) end return g end - M.ForEach2 = function(self, tb1, tb2, func) for i = 1, #tb1 do func(tb1[i], tb2[i]) end end - M.Map2 = function(self, tb1, tb2, map) local g = NewTable() for i = 1, #tb1 do table.insert(g, map(tb1[i], tb2[i], i)) end - return g + return g end - M.Filter2 = function(self, tb1, tb2, filter, map) if map == nil then - map = function(a,b,c) return {a,b,c} end + map = function(a, b, c) + return { + a, + b, + c, + } + end end local g = NewTable() for i = 1, #tb1 do @@ -418,23 +396,22 @@ M.Filter2 = function(self, tb1, tb2, filter, map) table.insert(map(tb1[i], tb2[i], i)) end end - return g + return g end - M.SlowSort = function(self, tb, sort) local g = self:ShallowCopy(tb) local len = #g if sort ~= nil then - for i = 1, len-1 do - for j = i+1, len do + for i = 1, len - 1 do + for j = i + 1, len do if sort(g[i], g[j]) > 0 then g[i], g[j] = g[j], g[i] end end end else - for i = 1, len-1 do - for j = i+1, len do + for i = 1, len - 1 do + for j = i + 1, len do if g[i] > g[j] then g[i], g[j] = g[j], g[i] end @@ -443,10 +420,11 @@ M.SlowSort = function(self, tb, sort) end return g end - M.MergeSort = function(self, tb, sort) if sort == nil then - sort = function(a, b) return a-b end + sort = function(a, b) + return a - b + end end local function Merge(a, b) local g = NewTable() @@ -457,10 +435,10 @@ M.MergeSort = function(self, tb, sort) while i <= aLen and j <= bLen do if sort(a[i], b[j]) > 0 then table.insert(g, b[j]) - j = j+1 + j = j + 1 else table.insert(g, a[i]) - i = i+1 + i = i + 1 end end if i <= aLen then @@ -480,32 +458,36 @@ M.MergeSort = function(self, tb, sort) if tableLength == 1 then return tab end - local left = SortRec(self:Take(tab, tableLength/2)) - local right = SortRec(self:Skip(tab, tableLength/2)) + local left = SortRec(self:Take(tab, tableLength / 2)) + local right = SortRec(self:Skip(tab, tableLength / 2)) local merge = Merge(left, right) return merge end return SortRec(tb) end - M.Sort = M.SlowSort M.SortByMaxFirst = function(self, tb, map) - map = map or function(a, b) return b-a end + map = map or function(a, b) + return b - a + end return self:Sort(tb, function(a, b) return map(b) - map(a) end) end M.SortByMinFirst = function(self, tb, map) - map = map or function(a, b) return a-b end + map = map or function(a, b) + return a - b + end return self:Sort(tb, function(a, b) return map(a) - map(b) end) end - function M:Remove_Modify(tb, item) local filter = item if type(item) ~= "function" then - filter = function(t) return t == item end + filter = function(t) + return t == item + end end local i = 1 local d = #tb @@ -518,7 +500,6 @@ function M:Remove_Modify(tb, item) end end end - function M:InsertAfter_Modify(tb, item, after) if after == nil then table.insert(tb, item) @@ -532,7 +513,6 @@ function M:InsertAfter_Modify(tb, item, after) table.insert(tb, item) end end - function M:Unpack(tb) local index = #tb local function rec(...) @@ -545,7 +525,6 @@ function M:Unpack(tb) end return rec() end - function M:UnpackIfTable(p) if type(p) == "table" then return self:Unpack(p) @@ -553,38 +532,24 @@ function M:UnpackIfTable(p) return p end end - function M:Also(tb, block) block(tb) return tb end - function M:Let(tb, block) return block(tb) end - - --- function M:ClampGroup(tb, min, max) --- local vmin = math.min(tb) --- local vmax = math.max(tb) --- return self:Map(tb, function(t) return RemapVal(t, vmin, vmax, min, max) end) --- end - local function AddLinqFunctionsToMetatable(mt) for k, v in pairs(M) do mt[k] = function(...) - return v(M, ...) + return v(M, ...) end end for functionName, func in pairs(table) do mt[functionName] = func end end - AddLinqFunctionsToMetatable(magicTable) - --- bot mode behaviour - local Trim = function(v, left, right) if right >= left then if v > right then @@ -604,37 +569,30 @@ local Trim = function(v, left, right) end end end - M.TrimDesire = function(self, desire) return Trim(desire, 0, 1) end - M.GetAbilityImportance = function(self, cooldown) - return Trim(cooldown/120, 0, 1) + return Trim(cooldown / 120, 0, 1) end - M.IsFarmingOrPushing = function(self, npcBot) local mode = npcBot:GetActiveMode() - return mode==BOT_MODE_FARM or mode==BOT_MODE_PUSH_TOWER_BOT or mode==BOT_MODE_PUSH_TOWER_MID or mode==BOT_MODE_PUSH_TOWER_TOP or mode == BOT_MODE_DEFEND_TOWER_BOT or mode==BOT_MODE_DEFEND_TOWER_MID or mode==BOT_MODE_DEFEND_TOWER_TOP + return mode == BOT_MODE_FARM or mode == BOT_MODE_PUSH_TOWER_BOT or mode == BOT_MODE_PUSH_TOWER_MID or mode == BOT_MODE_PUSH_TOWER_TOP or mode == BOT_MODE_DEFEND_TOWER_BOT or mode == BOT_MODE_DEFEND_TOWER_MID or mode == BOT_MODE_DEFEND_TOWER_TOP end - M.IsLaning = function(self, npcBot) local mode = npcBot:GetActiveMode() return mode == BOT_MODE_LANING end - M.IsAttackingEnemies = function(self, npcBot) local mode = npcBot:GetActiveMode() - return mode == BOT_MODE_ROAM or mode == BOT_MODE_TEAM_ROAM or mode == BOT_MODE_ATTACK or mode == BOT_MODE_DEFEND_ALLY + return mode == BOT_MODE_ROAM or mode == BOT_MODE_TEAM_ROAM or mode == BOT_MODE_ATTACK or mode == BOT_MODE_DEFEND_ALLY end - M.IsRetreating = function(self, npcBot) return npcBot:GetActiveMode() == BOT_MODE_RETREAT end M.NotRetreating = function(self, npcBot) return npcBot:GetActiveMode() ~= BOT_MODE_RETREAT end - M.HasEnoughManaToUseAttackAttachedAbility = function(self, npcBot, ability) local percent = self:GetManaPercent(npcBot) if percent >= 0.8 and npcBot:GetMana() >= 650 then @@ -642,13 +600,9 @@ M.HasEnoughManaToUseAttackAttachedAbility = function(self, npcBot, ability) end return percent >= 0.4 and npcBot:GetMana() >= 300 and npcBot:GetManaRegen() >= npcBot:GetAttackSpeed() / 100 * ability:GetManaCost() * 0.75 end - --- function generator - --- turn a function that returns true, false, nil to a function that decides whether to toggle the ability or not M.ToggleFunctionToAction = function(self, npcBot, oldConsider, ability) return function() - local value, target, castType = oldConsider() + local value,target,castType = oldConsider() if type(value) == "number" then return value, target, castType end @@ -659,10 +613,9 @@ M.ToggleFunctionToAction = function(self, npcBot, oldConsider, ability) end end end - M.ToggleFunctionToAutoCast = function(self, npcBot, oldConsider, ability) return function() - local value, target, castType = oldConsider() + local value,target,castType = oldConsider() if type(value) == "number" then return value, target, castType end @@ -672,10 +625,9 @@ M.ToggleFunctionToAutoCast = function(self, npcBot, oldConsider, ability) return 0 end end - M.PreventAbilityAtIllusion = function(self, npcBot, oldConsiderFunction, ability) return function() - local desire, target, targetTypeString = oldConsiderFunction() + local desire,target,targetTypeString = oldConsiderFunction() if desire == 0 or target == nil or target == 0 or self:IsVector(target) or targetTypeString == "Location" then return desire, target, targetTypeString end @@ -685,23 +637,20 @@ M.PreventAbilityAtIllusion = function(self, npcBot, oldConsiderFunction, ability return desire, target, targetTypeString end end - M.PreventEnemyTargetAbilityUsageAtAbilityBlock = function(self, npcBot, oldConsiderFunction, ability) local newConsider = function() - -- TODO: do we consider the base cooldown or the modified cooldown (arcane rune, octarine orb)? Will you crack a sphere's spell block with an ultimate ability when you're on arcane rune? - local desire, target, targetTypeString = oldConsiderFunction() + local desire,target,targetTypeString = oldConsiderFunction() if desire == 0 or target == nil or target == 0 or self:IsVector(target) or targetTypeString == "Location" then return desire, target, targetTypeString end local oldDesire = desire - if npcBot:GetTeam() ~= target:GetTeam() then -- some ability can cast to both allies and enemies (abbadon_mist_coil, etc) + if npcBot:GetTeam() ~= target:GetTeam() then local cooldown = ability:GetCooldown() local abilityImportance = self:GetAbilityImportance(cooldown) - if target:HasModifier("modifier_antimage_counterspell") then return 0 end - if target:HasModifier "modifier_item_sphere" or target:HasModifier("modifier_roshan_spell_block") or target:HasModifier("modifier_special_bonus_spell_block") then -- qop lv 25 + if target:HasModifier "modifier_item_sphere" or target:HasModifier("modifier_roshan_spell_block") or target:HasModifier("modifier_special_bonus_spell_block") then if cooldown >= 30 then desire = desire - abilityImportance elseif cooldown <= 20 then @@ -711,7 +660,7 @@ M.PreventEnemyTargetAbilityUsageAtAbilityBlock = function(self, npcBot, oldConsi if target:HasModifier("modifier_item_sphere_target") then if cooldown >= 60 then desire = 0 - elseif cooldown >= 30 then + elseif cooldown >= 30 then desire = desire - abilityImportance + 0.1 elseif cooldown <= 20 then desire = desire + abilityImportance @@ -724,27 +673,24 @@ M.PreventEnemyTargetAbilityUsageAtAbilityBlock = function(self, npcBot, oldConsi if npcBot:GetActiveMode() == BOT_MODE_RETREAT then desire = 0 else - desire = desire - abilityImportance/2 + desire = desire - abilityImportance / 2 end end if target:HasModifier("modifier_mirror_shield_delay") then - desire = desire - abilityImportance*1.5 + desire = desire - abilityImportance * 1.5 end - desire = self:TrimDesire(desire) end return desire, target, targetTypeString end return newConsider end - M.GetUsedAbilityInfo = function(self, ability, abilityInfoTable, considerTarget) abilityInfoTable.lastUsedTime = DotaTime() abilityInfoTable.lastUsedCharge = ability:GetCurrentCharges() abilityInfoTable.lastUsedTarget = considerTarget abilityInfoTable.lastUsedRemainingCooldown = ability:GetCooldownTimeRemaining() end - M.AddCooldownToChargeAbility = function(self, oldConsider, ability, abilityInfoTable, additionalCooldown) return function() if abilityInfoTable.lastUsedTime == nil then @@ -759,7 +705,6 @@ M.AddCooldownToChargeAbility = function(self, oldConsider, ability, abilityInfoT return oldConsider() end end - M.AutoModifyConsiderFunction = function(self, npcBot, considers, abilitiesReal) for index, ability in pairs(abilitiesReal) do if not binlib.Test(ability:GetBehavior(), ABILITY_BEHAVIOR_PASSIVE) and considers[index] == nil then @@ -773,16 +718,15 @@ M.AutoModifyConsiderFunction = function(self, npcBot, considers, abilitiesReal) end npcBot.abilityRecords = {} end - function M:InitAbility(npcBot) local abilities = NewTable() local abilityNames = NewTable() local talents = NewTable() for i = 0, 23 do local ability = npcBot:GetAbilityInSlot(i) - if (ability ~= nil) then - if (ability:GetName() ~= "generic_hidden") then - if (ability:IsTalent() == true) then + if ability ~= nil then + if ability:GetName() ~= "generic_hidden" then + if ability:IsTalent() == true then table.insert(talents, ability:GetName()) else table.insert(abilityNames, ability:GetName()) @@ -794,11 +738,7 @@ function M:InitAbility(npcBot) npcBot.abilityInited = true return abilityNames, abilities, talents end - --- ability information - local keysBeforeAbilityInformation = M:Keys(M) - M.UndisjointableProjectiles = { "alchemist_berser_potion", "alchemist_unstable_concoction_throw", @@ -823,7 +763,6 @@ M.UndisjointableProjectiles = { "tusk_snowball", "witch_doctor_paralyzing_cask", } - M.targetTrackingStunAbilities = { "alchemist_berser_potion", "alchemist_unstable_concoction_throw", @@ -837,7 +776,6 @@ M.targetTrackingStunAbilities = { "vengefulspirit_magic_missile", "windrunner_shackleshot", } - M.targetNonTrackingStunAbilities = { "bane_fiends_grip", "beastmaster_primal_roar", @@ -855,9 +793,7 @@ M.targetNonTrackingStunAbilities = { "shadow_shaman_shackles", "storm_spirit_electric_vortex", } - M.targetStunAbilities = M:Concat(M.targetTrackingStunAbilities, M.targetNonTrackingStunAbilities) - M.locationStunAbilities = { "axe_berserkers_call", "centaur_hoof_stomp", @@ -886,14 +822,12 @@ M.locationStunAbilities = { "slardar_slithereen_crush", "tidehunter_ravage", } - M.targetTrackingDisableAbilities = { "gleipnir_eternal_chains", "naga_siren_ensnare", "riki_sleeping_dart", "viper_viper_strike", } - M.targetNonTrackingDisableAbilities = { "bloodseeker_rupture", "doom_bringer_doom", @@ -905,9 +839,7 @@ M.targetNonTrackingDisableAbilities = { "shadow_demon_purge", "shadow_shaman_voodoo", } - M.targetDisableAbilities = M:Concat(M.targetNonTrackingStunAbilities, M.targetTrackingDisableAbilities) - M.locationDisableAbilities = { "dark_willow_bramble_maze", "death_prophet_silence", @@ -918,7 +850,6 @@ M.locationDisableAbilities = { "invoker_deafening_wave", "treant_overgrowth", } - M.targetTrackingHeavyDamageAbilities = { "item_ethereal_blade", "lich_chain_frost", @@ -926,7 +857,6 @@ M.targetTrackingHeavyDamageAbilities = { "morphling_adaptive_strike_agi", "sniper_assassinate", } - M.targetNonTrackingHeavyDamageAbilities = { "antimage_mana_void", "item_dagon", @@ -935,9 +865,7 @@ M.targetNonTrackingHeavyDamageAbilities = { "tinker_laser", "zuus_lightning_bolt", } - M.targetHeavyDamageAbilities = M:Concat(M.targetTrackingHeavyDamageAbilities, M.targetNonTrackingHeavyDamageAbilities) - M.locationHeavyDamageAbilities = { "ancient_apparition_ice_blast", "antimage_mana_void", @@ -956,11 +884,8 @@ M.locationHeavyDamageAbilities = { "skywrath_mage_mystic_flare", "venomancer_poison_nova", } - M.heavyDamageAbilities = M:Concat(M.targetTrackingHeavyDamageAbilities, M.locationHeavyDamageAbilities) - M.dodgeWorthAbilities = M:Concat(M.targetStunAbilities, M.locationStunAbilities, M.heavyDamageAbilities) - M.invisibleModifiers = { "modifier_bounty_hunter_wind_walk", "modifier_clinkz_wind_walk", @@ -979,7 +904,6 @@ M.invisibleModifiers = { "modifier_visage_silent_as_the_grave", "modifier_weaver_shukuchi", } - M.phaseModifiers = { "modifier_bounty_hunter_wind_walk", "modifier_clinkz_wind_walk", @@ -998,7 +922,6 @@ M.phaseModifiers = { "modifier_templar_assassin_meld", "modifier_weaver_shukuchi", } - M.phaseUnits = { "npc_dota_brewmaster_fire_1", "npc_dota_brewmaster_fire_2", @@ -1012,7 +935,6 @@ M.phaseUnits = { "npc_dota_techies_remote_mine", "npc_dota_weaver_swarm", } - M.unobstructedMovementModifiers = { "modifier_batrider_firefly", "modifier_broodmother_spin_web", @@ -1026,13 +948,11 @@ M.unobstructedMovementModifiers = { "modifier_item_spider_legs_active", "modifier_visage_silent_as_the_grave", } - M.flyingModifiers = { "modifier_rattletrap_jetpack", "modifier_night_stalker_darkness", "modifier_winter_wyvern_arctic_burn_flight", } - M.flyingUnits = { "npc_dota_visage_familiar1", "npc_dota_visage_familiar2", @@ -1040,7 +960,6 @@ M.flyingUnits = { "npc_dota_flying_courier", "npc_dota_beastmaster_hawk", } - M.positiveForceMovementModifiers = { "modifier_faceless_void_time_walk", "modifier_huskar_life_break_charge", @@ -1058,15 +977,12 @@ M.positiveForceMovementModifiers = { "modifier_sand_king_burrowstrike", "modifier_techies_suicide_leap", } - M.timeSensitivePositiveModifiers = { "modifier_item_black_king_bar", "modifier_faceless_void_chronosphere_selfbuff", "modifier_medusa_stone_gaze", "modifier_monkey_king_fur_army_soldier_in_position", } --- sorted by importance, used by dispell abilities - M.basicDispellablePositiveModifiers = { "modifier_omniknight_guardian_angle", "modifier_ember_spirit_flame_guard", @@ -1089,7 +1005,6 @@ M.basicDispellablePositiveModifiers = { "modifier_item_spider_legs_active", "modifier_item_bullwhip_buff", } - M.basicDispellWorthPositiveModifiers = { "modifier_omniknight_guardian_angle", "modifier_ember_spirit_flame_guard", @@ -1106,17 +1021,11 @@ M.basicDispellWorthPositiveModifiers = { "modifier_item_ethereal_blade_ethereal", "modifier_ghost_state", } - -M.basicDispellWorthNegativeModifiers = { - "modifier_abaddon_frostmourne_debuff_bonus", -} - +M.basicDispellWorthNegativeModifiers = { "modifier_abaddon_frostmourne_debuff_bonus" } M.basicDispellableNegativeModifiers = { "modifier_abaddon_frostmourne_debuff", "modifier_abaddon_frostmourne_debuff_bonus", - } - M.unbreakableChannelAbilities = { "puck_phase_shift", "pangolier_gyroshell", @@ -1124,45 +1033,38 @@ M.unbreakableChannelAbilities = { "phoenix_supernova", "lycan_shapeshift", } - M.nonIllusionModifiers = {} - M.valubleNeutrals = { "npc_dota_neutral_alpha_wolf", - "npc_dota_neutral_centaur_khan", - "npc_dota_neutral_polar_furbolg_ursa_warrior", - "npc_dota_neutral_dark_troll_warlord", - "npc_dota_neutral_mud_golem", - "npc_dota_neutral_satyr_hellcaller", + "npc_dota_neutral_centaur_khan", + "npc_dota_neutral_polar_furbolg_ursa_warrior", + "npc_dota_neutral_dark_troll_warlord", + "npc_dota_neutral_mud_golem", + "npc_dota_neutral_satyr_hellcaller", } - M.valubleAncientNeutrals = { "npc_dota_neutral_black_dragon", "npc_dota_neutral_rock_golem", "npc_dota_neutral_big_thunder_lizard", } - M.hypnosisModifiers = { "modifier_lich_sinister_gaze", "modifier_void_spirit_aether_remnant_pull", "modifier_keeper_of_the_light_will_o_wisp", } - M.fearModifiers = { "modifier_dark_willow_debuff_fear", "modifier_lone_druid_savage_roar", "modifier_shadow_fiend_requiem_fear", "modifier_terrorblade_fear", } - M.hexModifiers = { "modifier_lion_voodoo", "modifier_shadow_shaman_voodoo", "modifier_sheepstick_debuff", "modifier_item_princes_knife_hex", - "modifier_hexxed", -- dazzle_poison_touch lv 25 + "modifier_hexxed", } - M.silenceModifiers = { "modifier_abaddon_frostmourne_debuff_bonus", "modifier_silence", @@ -1182,7 +1084,6 @@ M.silenceModifiers = { "modifier_silencer_last_word", "modifier_skywrath_mage_ancient_seal", } - M.timedSilenceModifiers = { "modifier_abaddon_frostmourne_debuff_bonus", "modifier_silence", @@ -1198,7 +1099,6 @@ M.timedSilenceModifiers = { "modifier_silencer_last_word", "modifier_skywrath_mage_ancient_seal", } - M.magicImmuneModifiers = { "modifier_item_black_king_bar", "modifier_life_stealer_rage", @@ -1209,36 +1109,27 @@ M.magicImmuneModifiers = { "modifier_legion_commander_press_the_attack_immunity", "modifier_lion_mana_drain_immunity", } - M.muteModifiers = { "modifier_tusk_snowball", "modifier_doom_bringer_doom", "modifier_disruptor_static_storm_mute", } - M.breakModifiers = { - -- "modifier_doom_bringer_doom",--only breaks with scepter "modifier_hoodwink_sharpshooter", "modifier_phantom_assassin_fan_of_knives", - -- "modifier_shadow_demon_purge_slow",--only break with scepter "modifier_silver_edge_debuff", - -- "modifier_spirit_breaker_greaterbash_break",--only break with shard "modifier_viper_nethertoxin", } --- TODO: how to record he caster of these abilities - M.noTrueSightRootAbilityAssociation = { dark_willow_branble_maze = "modifier_dark_willow_bramble_maze", - item_diffusal_blade = "modifier_rooted", -- most people don't know diffusal blade apply root on non-hero units + item_diffusal_blade = "modifier_rooted", } - M.conditionalTrueSightRootAbilityAssociation = { dark_troll_warlord_ensnare = "modifier_dark_troll_warlord_ensnare", ember_spirit_searing_chains = "modifier_ember_spirit_searing_chains", oracle_fortunes_end = "modifier_oracle_fortunes_end_purge", item_rod_of_atos = "modifier_rod_of_atos_debuff", } - M.permanentTrueSightRootAbilityAssociation = { broodmother_silken_bola = "modifier_broodmother_silken_bola", crystal_maiden_frostbite = "modfifier_crystal_maiden_frostbite", @@ -1250,52 +1141,47 @@ M.permanentTrueSightRootAbilityAssociation = { troll_warlord_berserkers_rage = "modifier_troll_warlord_berserkers_rage_ensnare", abyssal_underlord_pit_of_malice = "modifier_abyssal_underlord_pit_of_malice_ensare", } - M.rootAbilityAssociation = M:Concat(M.noTrueSightRootAbilityAssociation, M.conditionalTrueSightRootAbilityAssociation, M.permanentTrueSightRootAbilityAssociation) - local keysAfterAbilityInformation = M:Keys(M) local abilityInformationKeys = keysAfterAbilityInformation:RemoveAll(keysBeforeAbilityInformation) -abilityInformationKeys:ForEach(function(t) setmetatable(M[t], magicTable) end) -abilityInformationKeys = abilityInformationKeys:Filter(function(t) return t:match("AbilityAssociation") end) - +abilityInformationKeys:ForEach(function(t) + setmetatable(M[t], magicTable) +end) +abilityInformationKeys = abilityInformationKeys:Filter(function(t) + return t:match("AbilityAssociation") +end) local function ExtendAssociation(association) - return association:MapDic(function(key, value) return key end), association:Map(function(key, value) return value end):Distinct() + return association:MapDic(function(key, value) + return key + end), association:Map(function(key, value) + return value + end):Distinct() end abilityInformationKeys:ForEach(function(t) - local a, b = ExtendAssociation(M[t]) - local k = t:sub(1, #t-#"AbilityAssociation") + local a,b = ExtendAssociation(M[t]) + local k = t:sub(1, #t - #"AbilityAssociation") M[k.."Abilities"] = a M[k.."Modifiers"] = b end) - - --- unit function - function M:IsRoshan(npcTarget) return npcTarget ~= nil and npcTarget:IsAlive() and string.find(npcTarget:GetUnitName(), "roshan") end - function M:IsHero(t) return t:IsHero() end - function M:IsTempestDouble(npc) return npc:HasModifier("modifier_arc_warden_tempest_double") end - function M:IsLoneDruidBear(npc) return string.match(npc:GetUnitName(), "npc_dota_lone_druid_bear") end - function M:IsVisageFamiliar(npc) return string.match(npc:GetUnitName(), "npc_dota_visage_familiar") end - function M:IsBrewmasterPrimalSplit(npc) local unitName = npc:GetUnitName() return string.match(unitName, "npc_dota_brewmaster_") end - M.GetIncomingDodgeWorthProjectiles = function(self, npc) local health = npc:GetHealth() local projectiles = npc:GetIncomingTrackingProjectiles() @@ -1318,7 +1204,6 @@ M.GetIncomingDodgeWorthProjectiles = function(self, npc) end) return projectiles end - M.GetTargetHealAmplifyPercent = function(self, npc) local modifiers = npc:FindAllModifiers() local amplify = 1 @@ -1333,11 +1218,11 @@ M.GetTargetHealAmplifyPercent = function(self, npc) if modifierName == "modifier_holy_blessing" then amplify = amplify + 0.3 end - if modifierName == "modifier_necrolyte_sadist_active" then -- ghost shroud + if modifierName == "modifier_necrolyte_sadist_active" then amplify = amplify + 0.75 end if modifierName == "modifier_wisp_tether_haste" then - amplify = amplify + 0.6 -- 0.8/1/1.2 + amplify = amplify + 0.6 end if modifierName == "modifier_oracle_false_promise" then amplify = amplify + 1 @@ -1345,17 +1230,14 @@ M.GetTargetHealAmplifyPercent = function(self, npc) end return amplify end - M.IsChannelingItem = function(self, npc) return npc:HasModifier("modifier_item_meteor_hammer") or npc:HasModifier("modifier_teleporting") or npc:HasModifier("modifier_boots_of_travel_incoming") end - M.IsChannelingAbility = function(self, npc) return npc:IsChanneling() and not self:IsChannelingItem(npc) end - function M:IsChannelingBreakWorthAbility(npc) - if not npc:IsChanneling() then + if not npc:IsChanneling() then return false end local ability = npc:GetCurrentActiveAbility() @@ -1372,10 +1254,8 @@ function M:IsChannelingBreakWorthAbility(npc) end return true end - M.RadiantPlayerId = GetTeamPlayers(TEAM_RADIANT) M.DirePlayerId = GetTeamPlayers(TEAM_DIRE) - M.GetTeamPlayers = function(self, team) if team == TEAM_RADIANT then return self.RadiantPlayerId @@ -1383,23 +1263,20 @@ M.GetTeamPlayers = function(self, team) return self.DirePlayerId end end - M.GetEnemyTeamMemberNames = function(self, npcBot) local enemies = self:GetEnemyHeroUnique(npcBot, GetUnitList(UNIT_LIST_ENEMY_HEROES)) - return self:Map(enemies, function(t) return t:GetUnitName() end) + return self:Map(enemies, function(t) + return t:GetUnitName() + end) end - M.enemyVisibleIllusionModifiers = { "modifier_illusion", - -- "modifier_phantom_lancer_doppelwalk_illusion", - -- "modifier_phantom_lancer_juxtapose_illusion", "modifier_terrorblade_conjureimage", "modifier_grimstroke_scepter_buff", "modifier_arc_warden_tempest_double", "modifier_skeleton_king_reincarnation_active", "modifier_vengefulspirit_hybrid_special", } - M.MustBeIllusion = function(self, npcBot, target) if npcBot:GetTeam() == target:GetTeam() then return target:IsIllusion() or self:HasAnyModifier(target, self.enemyVisibleIllusionModifiers) @@ -1415,20 +1292,18 @@ M.MustBeIllusion = function(self, npcBot, target) end return false end -M.MayNotBeIllusion = function(self, npcBot, target) return not self:MustBeIllusion(npcBot, target) end - +M.MayNotBeIllusion = function(self, npcBot, target) + return not self:MustBeIllusion(npcBot, target) +end function M:IsOnSameTeam(a, b) return a:GetTeam() == b:GetTeam() end - function M:IsNonIllusionHero(npcBot, target) return self:MayNotBeIllusion(npcBot, target) and self:IsHero(target) end - function M:HasNonIllusionModifier(npc) return self:HasAnyModifier(npc, self.nonIllusionModifiers) end - function M:CanIllusionUseAbility(npc) local name = npc:GetUnitName() local ability = npc:GetCurrentActiveAbility() @@ -1439,15 +1314,20 @@ function M:CanIllusionUseAbility(npc) return true end end - M.DetectIllusion = function(self, npcBot) local nearbyEnemies = self:GetNearbyNonIllusionHeroes(npcBot, 1599) - nearbyEnemies = self:Filter(nearbyEnemies, function(t) return string.match(t:GetUnitName(), "npc_dota_hero") end) - local nearbyEnemyGroups = self:GroupBy(nearbyEnemies, function(t) return t:GetUnitName() end) - nearbyEnemyGroups = self:Filter(nearbyEnemyGroups, function(t) return #t > 1 end) + nearbyEnemies = self:Filter(nearbyEnemies, function(t) + return string.match(t:GetUnitName(), "npc_dota_hero") + end) + local nearbyEnemyGroups = self:GroupBy(nearbyEnemies, function(t) + return t:GetUnitName() + end) + nearbyEnemyGroups = self:Filter(nearbyEnemyGroups, function(t) + return #t > 1 + end) self:ForEach(nearbyEnemyGroups, function(nearbyEnemyGroup) local castingEnemies = self:Filter(nearbyEnemyGroup, function(t) - return (t:IsUsingAbility() or t:IsChanneling() or self:HasNonIllusionModifier(t) or t.markedAsRealHero) and not t.markedAsIllusion + return (t:IsUsingAbility() or t:IsChanneling() or self:HasNonIllusionModifier(t) or t.markedAsRealHero) and not t.markedAsIllusion end) local castingEnemy = castingEnemies[1] if castingEnemy and not self:CanIllusionUseAbility(castingEnemy) then @@ -1459,7 +1339,6 @@ M.DetectIllusion = function(self, npcBot) end end) end - M.GetNearbyHeroes = function(self, npcBot, range, getEnemy, botModeMask) range = range or 1200 if getEnemy == nil then @@ -1469,7 +1348,6 @@ M.GetNearbyHeroes = function(self, npcBot, range, getEnemy, botModeMask) local heroes = npcBot:GetNearbyHeroes(range, getEnemy, botModeMask) return heroes end - M.GetNearbyNonIllusionHeroes = function(self, npcBot, range, getEnemy, botModeMask) range = range or 1200 if getEnemy == nil then @@ -1477,24 +1355,25 @@ M.GetNearbyNonIllusionHeroes = function(self, npcBot, range, getEnemy, botModeMa end botModeMask = botModeMask or BOT_MODE_NONE local heroes = npcBot:GetNearbyHeroes(range, getEnemy, botModeMask) - return self:Filter(heroes, function(t) return self:MayNotBeIllusion(npcBot, t) end) + return self:Filter(heroes, function(t) + return self:MayNotBeIllusion(npcBot, t) + end) end - function M:AttackOnceDamage(npcBot, target) - return target:GetActualIncomingDamage(npcBot:GetAttackDamage() - npcBot:GetBaseDamageVariance()/2, DAMAGE_TYPE_PHYSICAL) + return target:GetActualIncomingDamage(npcBot:GetAttackDamage() - npcBot:GetBaseDamageVariance() / 2, DAMAGE_TYPE_PHYSICAL) end - function M:GetNearbyAttackableCreeps(npcBot, range, getEnemy) if getEnemy == nil then getEnemy = true end local creeps = npcBot:GetNearbyCreeps(range, getEnemy) if getEnemy then - creeps = self:Filter(creeps, function(t) return t:HasModifier("modifier_fountain_glyph") end) + creeps = self:Filter(creeps, function(t) + return t:HasModifier("modifier_fountain_glyph") + end) end return creeps end - M.GetNearbyAllUnits = function(self, npcBot, range) local h1 = npcBot:GetNearbyHeroes(range, true, BOT_MODE_NONE) local h2 = self:Remove(npcBot:GetNearbyHeroes(range, false, BOT_MODE_NONE), npcBot) @@ -1502,16 +1381,15 @@ M.GetNearbyAllUnits = function(self, npcBot, range) local h4 = npcBot:GetNearbyCreeps(range, false) return self:Concat(h1, h2, h3, h4) end - function M:GetNearbyEnemyUnits(npc, range) local h1 = npc:GetNearbyHeroes(range, true, BOT_MODE_NONE) local h3 = npc:GetNearbyCreeps(range, true) return self:Concat(h1, h3) end - - M.GetEnemyHeroUnique = function(self, npcBot, enemies) - local p = self:Filter(enemies, function(t) self:MayNotBeIllusion(npcBot, t) end) + local p = self:Filter(enemies, function(t) + self:MayNotBeIllusion(npcBot, t) + end) local g = NewTable() local readNames = NewTable() for _, enemy in pairs(p) do @@ -1523,14 +1401,12 @@ M.GetEnemyHeroUnique = function(self, npcBot, enemies) end return g end - M.GetMovementSpeedPercent = function(self, npc) return npc:GetCurrentMovementSpeed() / npc:GetBaseMovementSpeed() end M.CanHardlyMove = function(self, npc) return npc:IsStunned() or npc:IsRooted() or npc:GetCurrentMovementSpeed() <= 150 end - M.GetModifierRemainingDuration = function(self, npc, modifierName) local mod = npc:GetModifierByName(modifierName) if mod ~= -1 then @@ -1538,7 +1414,6 @@ M.GetModifierRemainingDuration = function(self, npc, modifierName) end return 0 end - M.imprisonmentModifier = { "modifier_item_cyclone", "modifier_item_wind_waker", @@ -1546,19 +1421,27 @@ M.imprisonmentModifier = { "modifier_obsidian_destroyer_astral_imprisonment_prison", "modifier_brewmaster_storm_cyclone", "modifier_invoker_tornado", - --"modifier_x_marks_the_target", } M.GetImprisonmentRemainingDuration = function(self, npc) - return self:First(self:Map(self.imprisonmentModifier, function(t) return self:GetModifierRemainingDuration(npc, t) end), function(t) return t ~= 0 end) or 0 + return self:First(self:Map(self.imprisonmentModifier, function(t) + return self:GetModifierRemainingDuration(npc, t) + end), function(t) + return t ~= 0 + end) or 0 end - function M:GetMagicImmuneRemainingDuration(npc) - local remainingTime = self:Map(self.magicImmuneModifiers, function(t) return { t, self:GetModifierRemainingDuration(npc, t)} end) - remainingTime = self:SortByMaxFirst(remainingTime, function(t) return t[2] end) + local remainingTime = self:Map(self.magicImmuneModifiers, function(t) + return { + t, + self:GetModifierRemainingDuration(npc, t), + } + end) + remainingTime = self:SortByMaxFirst(remainingTime, function(t) + return t[2] + end) remainingTime = remainingTime[1] return remainingTime and remainingTime[2] or 0 end - function M:GetSilenceRemainingDuration(npc) local silenceModifierRemainings = self:Map(self.timedSilenceModifiers, function(t) return self:GetModifierRemainingDuration(npc, t) @@ -1572,19 +1455,15 @@ function M:GetSilenceRemainingDuration(npc) silenceModifierRemainings = #silenceModifierRemainings ~= 0 and math.max(self:Unpack(silenceModifierRemainings)) or 0 return silenceModifierRemainings end - function M:GetStunRemainingDuration(npc) return self:IsStunned(npc) and 1 or 0 end - M.GetEnemyHeroNumber = function(self, npcBot, enemies) return #self:GetEnemyHeroUnique(npcBot, enemies) end - function M:HasPhasedMovement(npc) return self:HasAnyModifier(npc, self.phaseModifiers) or self:Contains(self.phaseUnits, npc:GetUnitName()) end - function M:HasUnobstructedMovement(npc) if self:HasAnyModifier(npc, self.flyingModifiers) or self:Contains(self.flyingUnits, npc:GetUnitName()) then if string.match(npc:GetUnitName(), "npc_dota_visage_familiar") then @@ -1592,7 +1471,9 @@ function M:HasUnobstructedMovement(npc) end return true end - local activeFlyingModifiers = self:Filter(self.unobstructedMovementModifiers, function(t) return npc:HasModifier(t) end) + local activeFlyingModifiers = self:Filter(self.unobstructedMovementModifiers, function(t) + return npc:HasModifier(t) + end) if #activeFlyingModifiers ~= 0 then local dragonKnightDragonForm = self:IndexOf(activeFlyingModifiers, "modifier_dragon_knight_dragon_form") if dragonKnightDragonForm ~= -1 then @@ -1611,9 +1492,6 @@ function M:HasUnobstructedMovement(npc) end return #activeFlyingModifiers ~= 0 end - --- item function - M.GetAvailableItem = function(self, npc, itemName) for i = 0, 5 do local item = npc:GetItemInSlot(i) @@ -1622,10 +1500,8 @@ M.GetAvailableItem = function(self, npc, itemName) end end end - -local radianceAncientLocation = Vector(-7200,-6666) -local direAncientLocation = Vector(7137,6548) - +local radianceAncientLocation = Vector(-7200, -6666) +local direAncientLocation = Vector(7137, 6548) M.GetAncientLocation = function(self, npc) if npc:GetTeam() == TEAM_RADIANT then return radianceAncientLocation @@ -1633,12 +1509,10 @@ M.GetAncientLocation = function(self, npc) return direAncientLocation end end - M.GetDistanceFromAncient = function(self, npc) local fountain = self:GetAncientLocation(npc) return GetUnitToLocationDistance(npc, fountain) end - M.TryUseTp = function(self, npc) local item = npc:GetItemInSlot(15) if item ~= nil and item:IsFullyCastable() and self:CanMove(npc) then @@ -1652,7 +1526,6 @@ M.TryUseTp = function(self, npc) return true end end - M.GetAvailableBlink = function(self, npc) local blinks = { "item_blink", @@ -1664,7 +1537,6 @@ M.GetAvailableBlink = function(self, npc) return a or self:GetAvailableItem(npc, blinkName) end) end - function M:GetAvailableTravelBoots(npc) local travelBoots = { "item_travel_boots", @@ -1674,39 +1546,35 @@ function M:GetAvailableTravelBoots(npc) return seed or self:GetAvailableItem(npc, t) end) end - M.GetEmptyInventorySlots = function(self, npc) local g = 0 for i = 0, 5 do if npc:GetItemInSlot(i) == nil then - g = g+1 + g = g + 1 end end return g end - M.GetEmptyItemSlots = function(self, npc) local g = 0 for i = 0, 8 do if npc:GetItemInSlot(i) == nil then - g = g+1 + g = g + 1 end end return g end - M.GetEmptyBackpackSlots = function(self, npc) local g = 0 - for i = 7, 9 do + for i = 6, 8 do if npc:GetItemInSlot(i) == nil then - g = g+1 + g = g + 1 end end return g end - M.SwapItemToBackpack = function(self, npc, itemIndex) - for i = 6,8 do + for i = 6, 8 do if npc:GetItemInSlot(i) == nil then npc:ActionImmediate_SwapItems(itemIndex, i) return true @@ -1714,7 +1582,6 @@ M.SwapItemToBackpack = function(self, npc, itemIndex) end return false end - M.GetCarriedItems = function(self, npc) local g = NewTable() for i = 0, 8 do @@ -1726,7 +1593,6 @@ M.GetCarriedItems = function(self, npc) end return g end - M.GetInventoryItems = function(self, npc) local g = NewTable() for i = 0, 5 do @@ -1738,7 +1604,6 @@ M.GetInventoryItems = function(self, npc) end return g end - M.GetInventoryItemNames = function(self, npc) local g = NewTable() for i = 0, 5 do @@ -1750,7 +1615,6 @@ M.GetInventoryItemNames = function(self, npc) end return g end - M.GetStashItems = function(self, npc) local g = NewTable() for i = 9, 14 do @@ -1762,7 +1626,6 @@ M.GetStashItems = function(self, npc) end return g end - function M:GetCourierItems(courier) local g = NewTable() for i = 0, 8 do @@ -1773,16 +1636,14 @@ function M:GetCourierItems(courier) end return g end - function M:GetMyCourier(npcBot) if npcBot.courierIDNew == nil then self:FindCourier(npcBot) end return GetCourier(npcBot.courierIDNew) end - function M:FindCourier(npcBot) - for i = 0,4 do + for i = 0, 4 do local courier = GetCourier(i) if courier ~= nil then if courier:GetPlayerID() == npcBot:GetPlayerID() then @@ -1791,7 +1652,6 @@ function M:FindCourier(npcBot) end end end - M.GetAllBoughtItems = function(self, npcBot) local g = NewTable() for i = 0, 15 do @@ -1805,45 +1665,42 @@ M.GetAllBoughtItems = function(self, npcBot) end return g end - M.IsBoots = function(self, item) if type(item) ~= "string" then item = item:GetName() end return string.match(item, "boots") or item == "item_guardian_greaves" or #item >= 17 and string.sub(item, 17) == "item_power_treads" end - M.SwapCheapestItemToBackpack = function(self, npc) - local cheapestItem = self:First(self:Sort(self:Filter(self:GetInventoryItems(npc), function(t) - return not self:IsBoots(t) and not string.match(t:GetName(), "item_ward") - end), function(a, b) return GetItemCost(a:GetName()) - GetItemCost(b:GetName()) end)) + local cheapestItem = self:First(self:Sort(self:Filter(self:GetInventoryItems(npc), function(t) + return not self:IsBoots(t) and not string.match(t:GetName(), "ward") + end), function(a, b) + return GetItemCost(a:GetName()) - GetItemCost(b:GetName()) + end)) if cheapestItem == nil then return false end return self:SwapItemToBackpack(npc, cheapestItem.slotIndex) end - M.SuitableForSilence = function(self, npc, target) return self:MayNotBeIllusion(npc, target) and not target:IsMagicImmune() and not self:IsInvulnerable(target) end - M.GetHeroFullName = function(self, s) return "npc_dota_hero_"..s end M.GetHeroShortName = function(self, s) return string.sub(s, 12) end - M.IsMeleeHero = function(self, npc) local range = npc:GetAttackRange() local name = npc:GetUnitName() return range <= 210 or name == self:GetHeroFullName("tiny") or name == self:GetHeroFullName("doom_bringer") or name == self:GetHeroFullName("pudge") end - function M:HasAnyModifier(npc, modifierGroup) - return self:First(modifierGroup, function(t) return npc:HasModifier(t) end) + return self:First(modifierGroup, function(t) + return npc:HasModifier(t) + end) end - M.AttackPassiveAbilities = { "doom_bringer_infernal_blade", "drow_ranger_frost_arrows", @@ -1867,43 +1724,33 @@ M.IgnoreAbilityBlockAbilities = { "medallion_of_courage_valor", "solar_crest_armor_shine", } - M.IgnoreAbilityBlock = function(self, ability) local abilityName = ability:GetName() return self:Contains(self.AttackPassiveAbilities, abilityName) or self:Contains(self.IgnoreAbilityBlockAbilities, abilityName) or self:Contains(self.OtherIgnoreAbilityBlockAbilities, abilityName) end - M.AbilityRetargetModifiers = { "modifier_antimage_counterspell", "modifier_item_lotus_orb_active", "modifier_nyx_assassin_spiked_carapace", - -- "modifier_item_blade_mail", } M.HasAbilityRetargetModifier = function(self, npc) return self:HasAnyModifier(npc, self.AbilityRetargetModifiers) end - M.CanMove = function(self, npc) return not npc:IsStunned() and not npc:IsRooted() and not self:IsNightmared(npc) and not self:IsTaunted(npc) end - function M:CannotMove(npc) - return -- npc:IsStunned() or self:IsNightmared(npc) or --actually still able to cast abilities or move while stunned or nightmared, but provides no dfference - npc:IsRooted() or self:IsTaunted(npc)or self:IsHypnosed(npc) or self:IsFeared(npc) + return npc:IsRooted() or self:IsTaunted(npc) or self:IsHypnosed(npc) or self:IsFeared(npc) end - function M:CannotTeleport(npc) - return npc:IsRooted() or self:IsTaunted(npc)or self:IsHypnosed(npc) or self:IsFeared(npc) + return npc:IsRooted() or self:IsTaunted(npc) or self:IsHypnosed(npc) or self:IsFeared(npc) end - function M:IsNightmared(npc) return npc:HasModifier("modifier_bane_nightmare") or npc:HasModifier("modifier_riki_poison_dart_debuff") end - function M:IsTaunted(npc) return npc:HasModifier("modifier_axe_berserkers_call") or npc:HasModifier("modifier_legion_commander_duel") end - function M:IsDuelCaster(npc) local function IsTaunting(_npc) local ability = _npc:GetAbilityByName("legion_commander_duel") @@ -1920,42 +1767,30 @@ function M:IsDuelCaster(npc) return not IsTaunting(tauntingPlayer) end end - function M:IsMuted(npc) return npc:IsHexed() or self:HasAnyModifier(npc, self.muteModifiers) end - function M:IsHypnosed(npc) return self:HasAnyModifier(npc, self.hypnosisModifiers) end - function M:IsFeared(npc) return self:HasAnyModifier(npc, self.fearModifiers) end - M.IsSeverelyDisabled = function(self, npc) - return npc:IsStunned() or npc:IsHexed() or npc:IsRooted() or self:IsFeared(npc) or self:IsHypnosed(npc) - or self:IsNightmared(npc) - or npc:HasModifier("modifier_legion_commander_duel") and not self:IsDuelCaster(npc) or npc:HasModifier("modifier_axe_berserkers_call") - or npc:HasModifier("modifier_shadow_demon_purge_slow") - or npc:HasModifier("modifier_doom_bringer_doom") + return npc:IsStunned() or npc:IsHexed() or npc:IsRooted() or self:IsFeared(npc) or self:IsHypnosed(npc) or self:IsNightmared(npc) or npc:HasModifier("modifier_legion_commander_duel") and not self:IsDuelCaster(npc) or npc:HasModifier("modifier_axe_berserkers_call") or npc:HasModifier("modifier_shadow_demon_purge_slow") or npc:HasModifier("modifier_doom_bringer_doom") end - M.IsSeverelyDisabledOrSlowed = function(self, npc) return self:IsSeverelyDisabled(npc) or self:GetMovementSpeedPercent(npc) <= 0.35 end - M.HasSeverelyDisableProjectiles = function(self, npc) local projectiles = self:GetIncomingDodgeWorthProjectiles(npc) return self:Any(projectiles, function(t) return self:Contains(self.targetTrackingStunAbilities, t.ability:GetName()) end) end - M.IsOrGoingToBeSeverelyDisabled = function(self, npc) return self:IsSeverelyDisabled(npc) or self:HasSeverelyDisableProjectiles(npc) end - M.EtherealModifiers = { "modifier_ghost_state", "modifier_item_ethereal_blade_ethereal", @@ -1966,27 +1801,26 @@ M.EtherealModifiers = { M.IsEthereal = function(self, npc) return self:HasAnyModifier(npc, self.EtherealModifiers) end - function M:NotBlasted(self, npc) return not npc:HasModifier("modifier_ice_blast") end - M.CannotBeTargetted = function(self, npc) return self:HasAnyModifier(npc, self.CannotBeTargettedModifiers) end - -M.CanBeTargettedFunction = function(npc) return not M:CanBeTargetted(npc) end - +M.CanBeTargettedFunction = function(npc) + return not M:CanBeTargetted(npc) +end M.CannotBeAttacked = function(self, npc) return self:IsEthereal(npc) or self:IsInvulnerable(npc) or self:CannotBeTargetted(npc) end - -M.CanBeAttackedFunction = function(npc) return not M:CanBeAttacked(npc) end - +M.CanBeAttackedFunction = function(npc) + return not M:CanBeAttacked(npc) +end M.IsInvulnerable = function(self, npc) - return npc:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) return npc:HasModifier(t) end) + return npc:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) + return npc:HasModifier(t) + end) end - M.MayNotBeSeen = function(self, npc) if not npc:IsInvisible() or npc:HasModifier("modifier_item_dust") or npc:HasModifier("modifier_bounty_hunter_track") or npc:HasModifier("modifier_slardar_amplify_damage") or npc:HasModifier("modifier_truesight") then return false @@ -2013,27 +1847,25 @@ M.MayNotBeSeen = function(self, npc) return t:GetUnitName() == "npc_dota_necronomicon_warrior_3" end) end - M.ShouldNotBeAttacked = function(self, npc) - return self:CannotBeAttacked(npc) or self:Any(self.IgnoreDamageModifiers, function(t) return npc:HasModifier(t) end) or self:Any(self.IgnorePhysicalDamageModifiers, function(t) return npc:HasModifier(t) end) + return self:CannotBeAttacked(npc) or self:Any(self.IgnoreDamageModifiers, function(t) + return npc:HasModifier(t) + end) or self:Any(self.IgnorePhysicalDamageModifiers, function(t) + return npc:HasModifier(t) + end) end - M.PhysicalCanCastFunction = function(npc) return not M:IsInvulnerable(npc) and not M:ShouldNotBeAttacked(npc) and not npc:IsMagicImmune() end - M.IsPhysicalOutputDisabled = function(self, npc) return npc:IsDisarmed() or npc:IsBlind() and not npc:GetAvailableItem("item_monkey_king_bar") or self:IsEthereal(npc) end - M.GetHealthPercent = function(self, npc) return npc:GetHealth() / npc:GetMaxHealth() end - function M:GetPhysicalHealth(t) - return t:GetHealth() * (1+0.06*t:GetArmor()) / (1 - t:GetEvasion()) + return t:GetHealth() * (1 + 0.06 * t:GetArmor()) / (1 - t:GetEvasion()) end - function M:GetBuildingPhysicalHealth(t) local h = self:GetPhysicalHealth(t) if t:HasModifier("modifier_fountain_glyph") then @@ -2041,35 +1873,21 @@ function M:GetBuildingPhysicalHealth(t) end return h end - M.GetManaPercent = function(self, npc) return npc:GetMana() / npc:GetMaxMana() end - M.GetHealthDeficit = function(self, npc) return npc:GetMaxHealth() - npc:GetHealth() end - function M:GetManaDeficit(npc) return npc:GetMaxMana() - npc:GetMana() end - function M:GetTargetIfGood(npc) local target = npc:GetTarget() - if target~=nil and target:IsHero() and self:MayNotBeIllusion(npc, target) then + if target ~= nil and target:IsHero() and self:MayNotBeIllusion(npc, target) then return target end end - --- function M:GetAcrossAbilityStatusPerFrame(npc) --- local g = {} --- g.health = npc:GetHealth() --- g.mana = npc:GetMana() --- g.healthPercent = self:GetHealthPercent(npc) --- g.manaPercent = self:GetManaPercent(npc) --- g.attackRange = npc:GetAttackRange() --- end - function M:IndexOfBasicDispellablePositiveModifier(npc) return self:Aggregate(nil, self.basicDispellWorthPositiveModifiers, function(seed, modifier, index) if seed then @@ -2083,24 +1901,38 @@ function M:IndexOfBasicDispellablePositiveModifier(npc) end end) or -1 end - function M:HasBasicDispellablePositiveModifier(npc) - return self:Any(self.basicDispellWorthPositiveModifiers, function(t) return t:HasModifier(t) end) + return self:Any(self.basicDispellWorthPositiveModifiers, function(t) + return t:HasModifier(t) + end) end - function M:DontInterruptAlly(npc) return self:HasAnyModifier(npc, self.positiveForceMovementModifiers) or self:HasAnyModifier(npc, self.timeSensitivePositiveModifiers) or self:IsDuelCaster(npc) end - -M.MidLaneTowers = { TOWER_MID_1, TOWER_MID_2, TOWER_MID_3 } -M.BotLaneTowers = { TOWER_BOT_1, TOWER_BOT_2, TOWER_BOT_3 } -M.TopLaneTowers = { TOWER_TOP_1, TOWER_TOP_2, TOWER_TOP_3 } - +M.MidLaneTowers = { + TOWER_MID_1, + TOWER_MID_2, + TOWER_MID_3, +} +M.BotLaneTowers = { + TOWER_BOT_1, + TOWER_BOT_2, + TOWER_BOT_3, +} +M.TopLaneTowers = { + TOWER_TOP_1, + TOWER_TOP_2, + TOWER_TOP_3, +} function M:GetLaningTower(npc) local team = npc:GetTeam() local lane = npc:GetAssignedLane() - local function ToTower(t) return GetTower(team, t) end - local function TowerExists(t) return t:GetHealth() > 0 end + local function ToTower(t) + return GetTower(team, t) + end + local function TowerExists(t) + return t:GetHealth() > 0 + end if lane == LANE_BOT then local a = self:Map(self.BotLaneTowers, ToTower) return self:First(a, TowerExists) @@ -2110,14 +1942,11 @@ function M:GetLaningTower(npc) return self:First(self:Map(self.TopLaneTowers, ToTower), TowerExists) end end - --- debug functions - M.DebugTable = function(self, tb) local msg = "{ " local DebugRec DebugRec = function(tc) - for k,v in pairs(tc) do + for k, v in pairs(tc) do if type(v) == "number" or type(v) == "string" then msg = msg..k.." = "..v..", " elseif type(v) == "boolean" then @@ -2133,9 +1962,8 @@ M.DebugTable = function(self, tb) msg = msg.." }" print(msg) end - M.DebugLongTable = function(self, tb) - for k,v in pairs(tb) do + for k, v in pairs(tb) do if type(v) == "table" then print(tostring(k).." = ") self:DebugTable(v) @@ -2144,9 +1972,8 @@ M.DebugLongTable = function(self, tb) end end end - M.DebugArray = function(self, tb) - for k,v in ipairs(tb) do + for k, v in ipairs(tb) do if type(v) == "table" then self:DebugTable(v) else @@ -2154,10 +1981,9 @@ M.DebugArray = function(self, tb) end end end - M.PrintAbilities = function(self, npcBot) local abilityNames = "{\n" - for i = 0,23 do + for i = 0, 23 do local ability = npcBot:GetAbilityInSlot(i) if ability ~= nil and ability:GetName() ~= "generic_hidden" then abilityNames = abilityNames.."\t\""..ability:GetName().."\",\n" @@ -2167,9 +1993,6 @@ M.PrintAbilities = function(self, npcBot) print(npcBot:GetUnitName()) print(abilityNames) end - --- ability function - function M:NormalCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen, mustBeTargettable) damageType = damageType or DAMAGE_TYPE_MAGICAL if pierceMagicImmune == nil then @@ -2197,14 +2020,16 @@ function M:NormalCanCast(target, isPureDamageWithoutDisable, damageType, pierceM if targetMustBeSeen and not target:CanBeSeen() then return false end - if isPureDamageWithoutDisable and (damageType == DAMAGE_TYPE_PHYSICAL and self:ShouldNotBeAttacked(target) or damageType == DAMAGE_TYPE_MAGICAL and (target:IsMagicImmune() or self:Contains(self.IgnoreMagicalDamageModifiers, function(t) target:HasModifier(t) end))) then + if isPureDamageWithoutDisable and (damageType == DAMAGE_TYPE_PHYSICAL and self:ShouldNotBeAttacked(target) or damageType == DAMAGE_TYPE_MAGICAL and (target:IsMagicImmune() or self:Contains(self.IgnoreMagicalDamageModifiers, function(t) + target:HasModifier(t) + end))) then return false end return true end - -M.NormalCanCastFunction = function(target) return M:NormalCanCast(target) end - +M.NormalCanCastFunction = function(target) + return M:NormalCanCast(target) +end function M:SpellCanCast(target, pierceMagicImmune, targetMustBeSeen, mustBeTargettable) if targetMustBeSeen == nil then targetMustBeSeen = true @@ -2223,22 +2048,20 @@ function M:SpellCanCast(target, pierceMagicImmune, targetMustBeSeen, mustBeTarge end return true end - -M.SpellCanCastFunction = function(target) return M:SpellCanCast(target) end - +M.SpellCanCastFunction = function(target) + return M:SpellCanCast(target) +end function M:AllyCanCast(target, pierceMagicImmune) if pierceMagicImmune == nil then pierceMagicImmune = true end return not target:IsInvulnerable() and not self:CannotBeTargetted(target) end - -M.AllyCanCastFunction = function(target) return M:AllyCanCast(target) end - +M.AllyCanCastFunction = function(target) + return M:AllyCanCast(target) +end function M:NeutralCanCast(target) - end - function M:EnemyAllyCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen) if self:IsOnSameTeam(target, GetBot()) then return self:NormalCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen) @@ -2246,11 +2069,9 @@ function M:EnemyAllyCanCast(target, isPureDamageWithoutDisable, damageType, pier return self:AllyCanCast(target, pierceMagicImmune, targetMustBeSeen) end end - M.SpecialBonusAttributes = "special_bonus_attributes" M.TalentNamePrefix = "special_bonus_" M.IncorrectAbilityName = "incorrect_name" - M.IsTalent = function(self, ability) if ability == nil then return false @@ -2260,10 +2081,9 @@ M.IsTalent = function(self, ability) end return ability ~= "special_bonus_attributes" and #ability >= #self.TalentNamePrefix and string.sub(ability, 1, #self.TalentNamePrefix) == self.TalentNamePrefix end - M.GetAbilities = function(self, npcBot) local g = NewTable() - for i = 0,25 do + for i = 0, 25 do local ability = npcBot:GetAbilityInSlot(i) if ability ~= nil and ability:GetName() ~= "generic_hidden" then table.insert(g, ability) @@ -2271,19 +2091,19 @@ M.GetAbilities = function(self, npcBot) end return g end - M.GetAbilityNames = function(self, npcBot) - return self:Map(self:GetAbilities(npcBot), function(t) return t:GetName() end) + return self:Map(self:GetAbilities(npcBot), function(t) + return t:GetName() + end) end - M.GetTalents = function(self, npcBot) - return self:Filter(self:GetAbilities(npcBot), function(t) return self:IsTalent(t) end) + return self:Filter(self:GetAbilities(npcBot), function(t) + return self:IsTalent(t) + end) end - M.GetAbilityLevelUpIndex = function(self, npcBot) return npcBot:GetLevel() - npcBot:GetAbilityPoints() + 1 + npcBot.abilityTable.incorrectAbilityLevelUpNumber end - M.FillInAbilities = function(self, npcBot, abilityTable) local abilities = self:GetAbilityNames(npcBot) if #abilityTable == 19 then @@ -2313,16 +2133,18 @@ M.FillInAbilities = function(self, npcBot, abilityTable) end) return end - - local talents = self:Map(self:GetTalents(npcBot), function(t) return t:GetName() end) - local levelUpTalents = self:Filter(abilityTable, function(t) return self:IsTalent(t) end) + local talents = self:Map(self:GetTalents(npcBot), function(t) + return t:GetName() + end) + local levelUpTalents = self:Filter(abilityTable, function(t) + return self:IsTalent(t) + end) local g = self:Concat(abilityTable, self:RemoveAll(talents, levelUpTalents)) g.incorrectAbilityLevelUpNumber = self:Count(g, function(ability, index) return index < npcBot:GetLevel() - npcBot:GetAbilityPoints() + 1 and (ability == nil or not ability:CanAbilityBeUpgraded() or ability:GetName() == self.IncorrectAbilityName) end) npcBot.abilityTable = g end - M.ExecuteAbilityLevelUp = function(self, npcBot) local abilityTable = npcBot.abilityTable if abilityTable.justLevelUpAbility then @@ -2342,92 +2164,52 @@ M.ExecuteAbilityLevelUp = function(self, npcBot) npcBot:ActionImmediate_LevelAbility(abilityName) abilityTable.justLevelUpAbility = true end - --- geometry - M.IsVector = function(self, object) - return type(object)=="userdata" and type(object.x)=="number" and type(object.y)=="number" and type(object.z)=="number" + return type(object) == "userdata" and type(object.x) == "number" and type(object.y) == "number" and type(object.z) == "number" end M.ToStringVector = function(self, object) - return string.format("(%d,%d,%d)",object.x,object.y,object.z) + return string.format("(%d,%d,%d)", object.x, object.y, object.z) end - M.GetLine = function(self, a, b) if a.x == b.x then - return { a = 1, b = 0, c = -a.x } - end - local k = (a.y-b.y)/(a.x-b.x) - local bb = a.y-k*a.x - return { a = k, b = -1, c = bb } + return { + a = 1, + b = 0, + c = -a.x, + } + end + local k = (a.y - b.y) / (a.x - b.x) + local bb = a.y - k * a.x + return { + a = k, + b = -1, + c = bb, + } end - M.GetPointToLineDistance = function(self, point, line) - local up = math.abs(line.a*point.x+line.b*point.y+line.c) - local down = math.sqrt(line.a*line.a+line.b*line.b) - return up/down + local up = math.abs(line.a * point.x + line.b * point.y + line.c) + local down = math.sqrt(line.a * line.a + line.b * line.b) + return up / down end - M.GetPointToPointDistance = function(self, a, b) - return ((a.x-b.x)^2+(a.y-b.y)^2)^0.5 + return ((a.x - b.x) ^ 2 + (a.y - b.y) ^ 2) ^ 0.5 end - --- Get the location on the line determined by startPoint and endPoint, with distance from startPoint to the target location M.GetPointFromLineByDistance = function(self, startPoint, endPoint, distance) local distanceTo = self:GetPointToPointDistance(startPoint, endPoint) local divide = (endPoint - startPoint) / distanceTo * distance return startPoint + divide end - M.GetCos = function(self, b, c, a) - return (b^2+c*2-a*2)/2/b/c + return (b ^ 2 + c * 2 - a * 2) / 2 / b / c end M.GetLocationToLocationDistance = function(self, a, b) - return ((a.x-b.x)^2+(a.y-b.y)^2)^0.5 + return ((a.x - b.x) ^ 2 + (a.y - b.y) ^ 2) ^ 0.5 end - function M:GetDegree(loc1, loc2) - local y = loc2.y-loc1.y - local x = loc2.x-loc1.x + local y = loc2.y - loc1.y + local x = loc2.x - loc1.x return math.atan2(y, x) * 180 / math.pi end --- Find the location to use a aoe at a single target with the least distance the hero needs to walk before casting ---M.FindAOELocationAtSingleTarget = function(self, npcBot, target, radius, castRange, castPoint) --- if self:CanMove(target) then --- radius = radius * 0.8 --- end --- local g --- GeneratePath(npcBot:GetLocation(), target:GetLocation(), {}, function(distance, waypoints) --- if waypoints == 0 then --- waypoints = { npcBot:GetLocation(), target:GetLocation() } --- end --- for i = 1, #waypoints-1 do --- local waypoint1 = waypoints[i] --- local waypoint2 = waypoints[i+1] --- local dis1 = GetUnitToLocationDistance(target, waypoint1) --- local dis2 = GetUnitToLocationDistance(target, waypoint2) --- if dis1 > dis2 then --- if radius >= dis1 then --- g = waypoint1 --- return --- elseif radius >= dis2 then --- local waypointDis = self:GetLocationToLocationDistance(waypoint1, waypoint2) --- local cosine = self:GetCos(dis2, waypointDis, dis1) --- local walkDis = dis1*cosine - (dis1^2-radius*2)^0.5 --- local targetLocation = self:GetPointFromLineByDistance(waypoint1, waypoint2, walkDis) --- g = targetLocation --- return --- end --- else --- if radius >= dis1 then --- g = waypoint1 --- return --- end --- end --- end --- g = waypoints[#waypoints] --- end) --- return g ---end M.FindAOELocationAtSingleTarget = function(self, npcBot, target, radius, castRange, castPoint) radius = radius - 80 local distance = GetUnitToUnitDistance(npcBot, target) @@ -2437,29 +2219,27 @@ M.FindAOELocationAtSingleTarget = function(self, npcBot, target, radius, castRan return self:GetPointFromLineByDistance(target:GetLocation(), npcBot:GetLocation(), radius) end end - M.MinValue = function(self, coefficients, min, max) max = max or 10000 min = min or -10000 local function Differential(coefficients) local g = {} for index, coefficient in pairs(coefficients) do - g[index-1] = coefficient*index + g[index - 1] = coefficient * index end - return g + return g end local function Y(coefficients, x) local g = 0 for index, coefficient in pairs(coefficients) do g = g + coefficient * x ^ index end - return g + return g end - local differential = Differential(coefficients) if differential[1] ~= nil and differential[1] ~= 0 then - local zeroPoint = -differential[0]/differential[1] - if Y(coefficients, zeroPoint+0.1)>0 then + local zeroPoint = -differential[0] / differential[1] + if Y(coefficients, zeroPoint + 0.1) > 0 then if zeroPoint > max then return Y(coefficients, max) elseif zeroPoint < min then @@ -2478,11 +2258,10 @@ M.MinValue = function(self, coefficients, min, max) return math.min(val1, val2) end end - else + else return Y(coefficients, min) end end - M.MaxValue = function(self, coefficients, min, max) local g = {} for index, coefficient in coefficients do @@ -2490,21 +2269,19 @@ M.MaxValue = function(self, coefficients, min, max) end return self:MinValue(g, -max, -min) end - M.GetCollapseInfo = function(self, obj1, obj2, timeLimit) - local x1 = obj1.location.x-obj2.location.x - local x2 = obj1.velocity.x-obj2.velocity.x - local coefficient0 = x1^2 - local coefficient1 = 2*x1*x2 - local coefficient3 = x2^2 - x1 = obj1.location.y-obj2.location.y - x2 = obj2.velocity.y-obj2.velocity.y + local x1 = obj1.location.x - obj2.location.x + local x2 = obj1.velocity.x - obj2.velocity.x + local coefficient0 = x1 ^ 2 + local coefficient1 = 2 * x1 * x2 + local coefficient3 = x2 ^ 2 + x1 = obj1.location.y - obj2.location.y + x2 = obj2.velocity.y - obj2.velocity.y coefficient0 = coefficient0 end - M.PreventHealAtHealSuppressTarget = function(self, npcBot, oldConsiderFunction, ability) return function() - local desire, target, targetTypeString = oldConsiderFunction() + local desire,target,targetTypeString = oldConsiderFunction() if desire == 0 or target == nil or target == 0 or targetTypeString == "Location" then return desire, target, targetTypeString end @@ -2515,38 +2292,13 @@ M.PreventHealAtHealSuppressTarget = function(self, npcBot, oldConsiderFunction, return desire, target, targetTypeString end end - -M.PURCHASE_ITEM_OUT_OF_STOCK=82 -M.PURCHASE_ITEM_INVALID_ITEM_NAME=33 -M.PURCHASE_ITEM_DISALLOWED_ITEM=78 -M.PURCHASE_ITEM_INSUFFICIENT_GOLD=63 -M.PURCHASE_ITEM_NOT_AT_SECRET_SHOP=62 -M.PURCHASE_ITEM_NOT_AT_HOME_SHOP=67 -M.PURCHASE_ITEM_SUCCESS=-1 - --- specified ability is not actually an ability (2) --- invalid order(3) unrecognised order name --- invalid order(40) order not allowed for illusions --- unit is dead (20) --- target tree is not active (43) --- ability is still in cooldown (15) --- ability is hidden --- cannot cast ability on tree --- cannot cast ability on target --- target is unselectable --- order requires a physical item target, but specified target is not a physical item (9) --- item cannot be used from stash (37) --- does not have enough mana to cast ability (14) --- item is still in cooldown --- can't cast attack ability on target, target is attack immune (32) --- order invalid for units with attack ability DOTA_UNIT_CAP_NO_ATTACK (41) --- can't cast on target, ability cannot target enemies (30) --- can't cast on target, ability cannot target creeps (56) --- unit can't perform command, unit has commands restricted (74) --- hero does not have enough ability points to upgrade ability (13) --- ability is hidden (60) - - +M.PURCHASE_ITEM_OUT_OF_STOCK = 82 +M.PURCHASE_ITEM_INVALID_ITEM_NAME = 33 +M.PURCHASE_ITEM_DISALLOWED_ITEM = 78 +M.PURCHASE_ITEM_INSUFFICIENT_GOLD = 63 +M.PURCHASE_ITEM_NOT_AT_SECRET_SHOP = 62 +M.PURCHASE_ITEM_NOT_AT_HOME_SHOP = 67 +M.PURCHASE_ITEM_SUCCESS = -1 M.IgnoreDamageModifiers = { "modifier_abaddon_borrowed_time", "modifier_item_aeon_disk_buff", @@ -2554,25 +2306,17 @@ M.IgnoreDamageModifiers = { "modifier_winter_wyvern_winters_curse_aura", "modifier_skeleton_king_reincarnation_scepter_active", } - M.CannotKillModifiers = { "modifier_dazzle_shadow_grave", "modifier_troll_warlord_battle_trance", } - M.CannotBeTargettedModifiers = { "modifier_slark_shadow_dance", "modifier_item_book_of_shadows", "modifier_dark_willow_shadow_realm_buff", } - -M.IgnorePhysicalDamageModifiers = { - "modifier_winter_wyvern_cold_embrace", -} -M.IgnoreMagicalDamageModifiers = { - "modifier_oracle_fates_edict", -} - +M.IgnorePhysicalDamageModifiers = { "modifier_winter_wyvern_cold_embrace" } +M.IgnoreMagicalDamageModifiers = { "modifier_oracle_fates_edict" } M.LastForAtLeastSeconds = function(self, predicate, time, infoTable) if infoTable.lastTrueTime == nil then infoTable.lastTrueTime = DotaTime() @@ -2588,12 +2332,25 @@ M.LastForAtLeastSeconds = function(self, predicate, time, infoTable) return false end end - M.GoodIllusionHero = { - "antimage","spectre","terrorblade","naga_siren", + "antimage", + "spectre", + "terrorblade", + "naga_siren", } M.ModerateIllusionHero = { - "abaddon","axe","chaos_knight","arc_warden","juggernaut","luna","medusa","morphling","phantom_lancer","sniper","wraith_king","phantom_assassin", + "abaddon", + "axe", + "chaos_knight", + "arc_warden", + "juggernaut", + "luna", + "medusa", + "morphling", + "phantom_lancer", + "sniper", + "wraith_king", + "phantom_assassin", } M.GetIllusionBattlePower = function(self, npc) local name = self:GetHeroShortName(npc:GetUnitName()) @@ -2611,34 +2368,38 @@ M.GetIllusionBattlePower = function(self, npc) elseif t:IsRanged() then t = t + t:GetAttackRange() / 600 end - local inventory = self:Map(self:GetInventoryItems(npc), function(t) return t:GetName() end) + local inventory = self:Map(self:GetInventoryItems(npc), function(t) + return t:GetName() + end) if self:Contains(inventory, "item_radiance") then - t = t+0.07 + t = t + 0.07 end if self:Contains(inventory, "item_diffusal_blade") then - t = t+0.05 + t = t + 0.05 end if self:Contains(inventory, "item_lesser_crit") then - t = t+0.04 + t = t + 0.04 end if self:Contains(inventory, "item_greater_crit") then - t = t+0.08 + t = t + 0.08 end - if npc:HasModifier("modifier_special_bonus_mana_break") then-- mirana talent[5] - t = t+0.04 + if npc:HasModifier("modifier_special_bonus_mana_break") then + t = t + 0.04 end - return t + return t end - M.GetNetWorth = function(self, npc, isEnemy) if isEnemy then - local itemCost = self:Map(self:GetInventoryItems(npc), function(t) return GetItemCost(t:GetName()) end) - return self:Aggregate(0, itemCost, function(a, b) return a+b end) + local itemCost = self:Map(self:GetInventoryItems(npc), function(t) + return GetItemCost(t:GetName()) + end) + return self:Aggregate(0, itemCost, function(a, b) + return a + b + end) else return npc:GetNetWorth() end end - function M:GetBattlePower(npc) local power = 0 local name = npc:GetUnitName() @@ -2652,7 +2413,7 @@ function M:GetBattlePower(npc) end elseif string.match(name, "npc_dota_lone_druid_bear") then local heroLevel = GetHeroLevel(npc:GetPlayerID()) - power = name[#"npc_dota_lone_druid_bear"+1]*2000-1000 + power = name[#"npc_dota_lone_druid_bear" + 1] * 2000 - 1000 power = power + heroLevel * 250 power = power + npc:GetNetWorth() end @@ -2673,12 +2434,20 @@ function M:GetBattlePower(npc) end return power end - M.GetHeroGroupBattlePower = function(self, npcBot, heroes, isEnemy) local function A(tb) - local battlePowerMap = self:Map(tb, function(t) return { t:GetUnitName(), self:GetBattlePower(t) } end) - battlePowerMap = self:SortByMaxFirst(battlePowerMap, function(t) return t[2] end) - battlePowerMap = self:Map(battlePowerMap, function(t, index) return t[2] * (1.15-0.15*index) end) + local battlePowerMap = self:Map(tb, function(t) + return { + t:GetUnitName(), + self:GetBattlePower(t), + } + end) + battlePowerMap = self:SortByMaxFirst(battlePowerMap, function(t) + return t[2] + end) + battlePowerMap = self:Map(battlePowerMap, function(t, index) + return t[2] * (1.15 - 0.15 * index) + end) local g = NewTable() for _, v in ipairs(battlePowerMap) do g[v[1]] = v[2] @@ -2692,7 +2461,6 @@ M.GetHeroGroupBattlePower = function(self, npcBot, heroes, isEnemy) local name = enemy:GetUnitName() if not self:Contains(readNames, name) then table.insert(readNames, name) - -- TODO: enemyNetWorthMap[name] should not be null if enemyNetWorthMap[name] then netWorth = netWorth + enemyNetWorthMap[name] end @@ -2704,34 +2472,22 @@ M.GetHeroGroupBattlePower = function(self, npcBot, heroes, isEnemy) end return netWorth end - M.Outnumber = function(self, npcBot, friends, enemies) return self:GetHeroGroupBattlePower(npcBot, friends, false) >= self:GetHeroGroupBattlePower(npcBot, enemies, true) * 1.8 end - - M.CannotBeKilledNormally = function(self, target) - return target:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) target:HasModifier(t) end) or target:HasModifier("modifier_dazzle_shallow_grave") + return target:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) + target:HasModifier(t) + end) or target:HasModifier("modifier_dazzle_shallow_grave") end - M.HasScepter = function(self, npc) return npc:HasScepter() or npc:HasModifier("modifier_wisp_tether_scepter") or npc:HasModifier("modifier_item_ultimate_scepter") or npc:HasModifier("modifier_item_ultimate_scepter_consumed_alchemist") end - --- ability record - local locationAOEAbilities = { - cone = { - "lina_dragon_slave", - }, - circle = { - "lina_light_strike_array", - }, - isoscelesTrapezoid = { - "kunkka_tidebringer", - }, + cone = { "lina_dragon_slave" }, + circle = { "lina_light_strike_array" }, + isoscelesTrapezoid = { "kunkka_tidebringer" }, } - function M:RecordAbility(npc, index, target, castType, abilities) local abilityRecords = npc.abilityRecords if index ~= nil then @@ -2759,27 +2515,20 @@ function M:RecordAbility(npc, index, target, castType, abilities) end end end - local frameNumber = 0 local dotaTimer local function FloatEqual(a, b) - return math.abs(a-b)<0.000001 + return math.abs(a - b) < 0.000001 end - --- tick - function M:GetFrameNumber() return frameNumber end - function M:EveryManyFrames(count, times) times = times or 1 return frameNumber % count < times end - local defaultReturn = NewTable() local everySecondsCallRegistry = NewTable() - function M:EveryManySeconds(second, oldFunction) local functionName = tostring(oldFunction) everySecondsCallRegistry[functionName.."lastCallTime"] = RandomFloat(0, second) @@ -2792,9 +2541,7 @@ function M:EveryManySeconds(second, oldFunction) end end end - local singleForTeamRegistry = NewTable() - function M:SingleForTeam(oldFunction) local functionName = tostring(oldFunction)..GetTeam() return function(...) @@ -2806,9 +2553,7 @@ function M:SingleForTeam(oldFunction) end end end - local singleForAllBots = NewTable() - function M:SingleForAllBots(oldFunction) local functionName = tostring(oldFunction) return function(...) @@ -2820,7 +2565,6 @@ function M:SingleForAllBots(oldFunction) end end end - local groupAnnounceTimes1 = 0 function M:AnnounceGroups1(npcBot) if groupAnnounceTimes1 == 0 then @@ -2835,19 +2579,16 @@ function M:AnnounceGroups2(npcBot) groupAnnounceTimes2 = 1 end end - function M:CalledOnThisFrame(functionInvocationResult) return functionInvocationResult ~= defaultReturn end - local slowFunctionRegistries = NewTable() local coroutineRegistry = NewTable() local coroutineExempt = NewTable() - function M:TickFromDota() local time = DotaTime() local function ResumeCoroutine(thread) - local coroutineResult = { coroutine.resume(thread[1], time - dotaTimer) } + local coroutineResult = { coroutine.resume(thread[1], time - dotaTimer) } if not coroutineResult[1] then error(coroutineResult[2]) end @@ -2858,7 +2599,9 @@ function M:TickFromDota() end if not FloatEqual(time, dotaTimer) then frameNumber = frameNumber + 1 - self:ForEach(slowFunctionRegistries, function(t) t(time - dotaTimer) end) + self:ForEach(slowFunctionRegistries, function(t) + t(time - dotaTimer) + end) local threadIndex = 1 while threadIndex <= #coroutineRegistry do local t = coroutineRegistry[threadIndex] @@ -2889,7 +2632,6 @@ function M:TickFromDota() dotaTimer = time end end - function M:RegisterSlowFunction(oldFunction, calledWhenHowManyFrames, frameOffset, defaultReturn) return function(...) if frameNumber % calledWhenHowManyFrames == frameOffset then @@ -2899,32 +2641,31 @@ function M:RegisterSlowFunction(oldFunction, calledWhenHowManyFrames, frameOffse end end end - --- coroutine - function M:ResumeUntilReturn(func) local g = NewTable() local thread = coroutine.create(func) while true do - local values = { coroutine.resume(thread) } + local values = { coroutine.resume(thread) } if values[1] then table.remove(values, 1) table.insert(g, values) else error(values[2]) break + end end return g end - function M:StartCoroutine(func) local newCoroutine = coroutine.create(func) table.insert(coroutineRegistry, newCoroutine) - table.insert(coroutineExempt, {newCoroutine, frameNumber}) + table.insert(coroutineExempt, { + newCoroutine, + frameNumber, + }) return newCoroutine end - function M:WaitForSeconds(seconds) local function WaitFor(firstFrameTime) local t = seconds - firstFrameTime @@ -2934,20 +2675,16 @@ function M:WaitForSeconds(seconds) end return self:StartCoroutine(WaitFor) end - function M:StopCoroutine(thread) - self:Remove_Modify(coroutineExempt, function(t) return t[1] == thread end) + self:Remove_Modify(coroutineExempt, function(t) + return t[1] == thread + end) self:Remove_Modify(coroutineRegistry, thread) end - --- get data from ability --- for example, to get value aoe_radius, use ability.aoe_radius rather than ability:GetSpecialValueInt("aoe_radius") - local function GetDataFromAbility(ability, valueName) local a = ability:GetSpecialValueInt(valueName) - return a==0 and ability:GetSpecialValueFloat(valueName) or a + return a == 0 and ability:GetSpecialValueFloat(valueName) or a end - local function Append__Index(tb, __index) local m = getmetatable(tb) if m == nil then @@ -2959,7 +2696,7 @@ local function Append__Index(tb, __index) m.__index = __index elseif type(oldIndex) == "function" then m.__index = function(ability, i) - local oldResult = { oldIndex(ability, i) } + local oldResult = { oldIndex(ability, i) } if oldResult[1] == nil then return __index(ability, i) else @@ -2969,7 +2706,7 @@ local function Append__Index(tb, __index) elseif type(oldIndex) == "table" then if oldIndex == m then m.__index = function(g, h) - local newResult = { __index(g, h) } + local newResult = { __index(g, h) } if newResult[1] == nil then return oldIndex[h] else @@ -2982,9 +2719,8 @@ local function Append__Index(tb, __index) end end Append__Index(CDOTABaseAbility_BotScript, GetDataFromAbility) - function M:pcall(func, ...) - local result = { func(...) } + local result = { func(...) } if result[1] then table.remove(result, 1) return self:Unpack(result) @@ -2993,12 +2729,9 @@ function M:pcall(func, ...) DebugPause() end end - --- M.debug = true function M:DebugPause() if self.debug then DebugPause() end end - return M diff --git a/util/AbilityAbstraction.mira b/util/AbilityAbstraction.mira new file mode 100644 index 00000000..6800af64 --- /dev/null +++ b/util/AbilityAbstraction.mira @@ -0,0 +1,3013 @@ +local M = {} + +local binlib = require(GetScriptDirectory().."/util/BinDecHex") + +-- LINQ functions + +local magicTable = {} +local function NewTable() + local a = {} + setmetatable(a, magicTable) + return a +end +magicTable.__index = magicTable + +M.Range = function(self, min, max, step) + if step == nil then step = 1 end + local g = NewTable() + for i = min, max, step do + table.insert(g, i) + end + return g +end + +M.Contains = function(self, tb, value, equals) + equals = equals or {==} + for _, v in ipairs(tb) do + if equals(v, value) then + return true + end + end + return false +end + +M.ContainsKey = function(self, tb, key, equals) + equals = equals or {==} + for k, _ in pairs(tb) do + if equals(key, k) then + return true + end + end + return false +end + +function M:Keys(tb) + local g = NewTable() + for k, _ in pairs(tb) do + table.insert(g, k) + end + return g +end + +M.Filter = function(self, tb, filter) + local g = NewTable() + for k, v in ipairs(tb) do + if filter(v, k) then + table.insert(g, v) + end + end + return g +end +M.FilterNot = function(self, tb, filter) + local g = NewTable() + for k, v in ipairs(tb) do + if not filter(v, k) then + table.insert(g, v) + end + end + return g +end + +M.Count = function(self, tb, filter) + local g = 0 + for k, v in ipairs(tb) do + if filter == nil or filter(v, k) then + g = g + 1 + end + end + return g +end + +function M:NonEmpty(self, tb) + return self:Filter(tb, function(t) + return t ~= nil and #t ~= 0 + end) +end + +M.Map = function(self, tb, transform) + local g = NewTable() + for k, v in ipairs(tb) do + g[k] = transform(v) + end + return g +end + +function M:MapDic(tb, transform) + local g = NewTable() + for k, v in pairs(tb) do + g[k] = transform(k, v) + end + return g +end + +M.ForEach = function(self, tb, action) + for k, v in ipairs(tb) do + action(v, k) + end +end + +M.Any = function(self, tb, filter) + for k, v in ipairs(tb) do + if filter == nil or filter(v, k) then + return true + end + end + return false +end + +M.All = function(self, tb, filter) + for k, v in ipairs(tb) do + if not filter(v, k) then + return false + end + end + return true +end + +M.Aggregate = function(self, seed, tb, aggregate) + for k, v in ipairs(tb) do + seed = aggregate(seed, v, k) + end + return seed +end + +M.ShallowCopy = function(self, tb) + local g = NewTable() + for k, v in pairs(tb) do + g[k] = v + end + return g +end + +M.First = function(self, tb, filter) + for k, v in ipairs(tb) do + if filter == nil or filter(v, k) then + return v + end + end +end + +M.Skip = function(self, tb, number) + local g = NewTable() + local i = 0 + for _, v in ipairs(tb) do + i = i + 1 + if i > number then + table.insert(g, v) + end + end + return g +end + +M.Take = function(self, tb, number) + local g = NewTable() + local i = 0 + for _, v in ipairs(tb) do + i = i + 1 + if i <= number then + table.insert(g, v) + else + break + end + end + return g +end + +local function deepCopy(self, tb) + local copiedTables = NewTable() + local g = NewTable() + table.insert(copiedTables, tb) + for k, v in pairs(tb) do + if type(v) ~= "table" then + g[k] = v + else + if self:Contains(copiedTables, v) then + print("Copy loop!") + return {} + end + g[k] = deepCopy(self, v) + end + end + return g +end +M.DeepCopy = deepCopy + +M.Concat = function(self, a, ...) + local g = NewTable() + local rec + rec = function(b, ...) + if b == nil then + return + end + for _, v in ipairs(b) do + table.insert(g, v) + end + rec(...) + end + rec(a, ...) + return g +end + +M.Remove = function(self, a, b) + local g = self:ShallowCopy(a) + for k,v in pairs(a) do + if v == b then + g[k] = nil + end + end + return g +end +M.RemoveAll = function(self, a, b) + local g = NewTable() + for _,v in pairs(a) do + if not self:Contains(b, v) then + table.insert(g, v) + end + end + return g +end + +M.Prepend = function(self, a, b) + return self:Concat(b, a) +end + +M.GroupBy = function(self, collection, keySelector, elementSelector, resultSelector, comparer) + comparer = comparer or function(a,b) return a==b end + resultSelector = resultSelector or function(key, value) return value end + elementSelector = elementSelector or self.IdentityFunction + local keys = NewTable() + local values = NewTable() + for _, k in ipairs(collection) do + local keyFound = false + for readKeyIndex, readKey in ipairs(keys) do + if comparer(readKey, keySelector(k)) then + keyFound = true + table.insert(values[readKeyIndex], elementSelector(k)) + break + end + end + if not keyFound then + table.insert(keys, keySelector(k)) + table.insert(values, { elementSelector(k) }) + end + end + return self:Map2(keys, values, resultSelector) +end + +M.Partition = function(self, tb, filter) + local a = NewTable() + local b = NewTable() + for k, v in pairs(tb) do + if filter(v, k) then + table.insert(a, v) + else + table.insert(b, v) + end + end + return a, b +end + +M.Distinct = function(self, tb, equals) + equals = equals or {==} + local g = NewTable() + for _, v in pairs(tb) do + if not self:Contains(g, v, equals) then + table.insert(g, v) + end + end + return g +end + +M.Reverse = function(self, tb) + local g = NewTable() + for i = #tb, 1, -1 do + table.insert(g, tb[i]) + end + return g +end + +M.Last = function(self, tb, filter) + return self:First(self:Reverse(tb), filter) +end + +function M:Identity(t) return t end +M.IdentityFunction = function(t) return t end + +function M:Max(tb, map) + if #tb == 0 then + return nil + end + map = map or self.IdentityFunction + local maxv, maxm = tb[1], map(tb[1]) + for i = 2, #tb do + local m = map(tb[i]) + if m > maxm then + maxm = m + maxv = tb[i] + end + end + return maxv +end + +function M:Min(tb, map) + if #tb == 0 then + return nil + end + map = map or self.IdentityFunction + local maxv, maxm = tb[1], map(tb[1]) + for i = 2, #tb do + local m = map(tb[i]) + if m < maxm then + maxm = m + maxv = tb[i] + end + end + return maxv +end + +M.Repeat = function(self, element, count) + local g = NewTable() + for i = 1, count do + table.insert(g, element) + end + return g +end + +M.Select = M.Map +M.SelectMany = function(self, tb, map, filter) + local g = NewTable() + for _, source in ipairs(tb) do + local collection = map(source) + for index, value in ipairs(collection) do + if filter == nil or filter(value, index) then + table.insert(g, value) + end + end + end + return g +end + +M.Where = M.Filter + +M.SkipLast = function(self, tb, number) + return self:Skip(self:Reverse(tb), number) +end + +M.Replace = function(self, tb, filter, map) + local g = NewTable() + for k, v in ipairs(tb) do + if filter(v, k) then + table.insert(g, map(v, k)) + else + table.insert(g, v) + end + end + return g +end + +M.IndexOf = function(self, tb, filter) + local g = NewTable() + for k, v in ipairs(tb) do + if type(filter) == "function" then + if filter(v, k) then + return k + end + elseif filter ~= nil then + if v == filter then + return k + end + end + end + return -1 +end + +M.Zip2 = function(self, tb1, tb2, map) + if map == nil then + map = function(a, b) + return {a, b} + end + end + local g = NewTable() + for i = 1, #tb1 do + table.insert(g, map(tb1[i], tb2[i])) + end + return g +end + +M.ForEach2 = function(self, tb1, tb2, func) + for i = 1, #tb1 do + func(tb1[i], tb2[i]) + end +end + +M.Map2 = function(self, tb1, tb2, map) + local g = NewTable() + for i = 1, #tb1 do + table.insert(g, map(tb1[i], tb2[i], i)) + end + return g +end + +M.Filter2 = function(self, tb1, tb2, filter, map) + if map == nil then + map = function(a,b,c) return {a,b,c} end + end + local g = NewTable() + for i = 1, #tb1 do + if filter(tb1[i], tb2[i], i) then + table.insert(map(tb1[i], tb2[i], i)) + end + end + return g +end + +M.SlowSort = function(self, tb, sort) + local g = self:ShallowCopy(tb) + local len = #g + if sort ~= nil then + for i = 1, len-1 do + for j = i+1, len do + if sort(g[i], g[j]) > 0 then + g[i], g[j] = g[j], g[i] + end + end + end + else + for i = 1, len-1 do + for j = i+1, len do + if g[i] > g[j] then + g[i], g[j] = g[j], g[i] + end + end + end + end + return g +end + +M.MergeSort = function(self, tb, sort) + if sort == nil then + sort = function(a, b) return a-b end + end + local function Merge(a, b) + local g = NewTable() + local aLen = #a + local bLen = #b + local i = 1 + local j = 1 + while i <= aLen and j <= bLen do + if sort(a[i], b[j]) > 0 then + table.insert(g, b[j]) + j = j+1 + else + table.insert(g, a[i]) + i = i+1 + end + end + if i <= aLen then + for _ = i, aLen do + table.insert(g, a[i]) + end + end + if j <= bLen then + for _ = j, bLen do + table.insert(g, b[j]) + end + end + return g + end + local function SortRec(tab) + local tableLength = #tab + if tableLength == 1 then + return tab + end + local left = SortRec(self:Take(tab, tableLength/2)) + local right = SortRec(self:Skip(tab, tableLength/2)) + local merge = Merge(left, right) + return merge + end + return SortRec(tb) +end + +M.Sort = M.SlowSort +M.SortByMaxFirst = function(self, tb, map) + map = map or function(a, b) return b-a end + return self:Sort(tb, function(a, b) + return map(b) - map(a) + end) +end +M.SortByMinFirst = function(self, tb, map) + map = map or function(a, b) return a-b end + return self:Sort(tb, function(a, b) + return map(a) - map(b) + end) +end + +function M:Remove_Modify(tb, item) + local filter = item + if type(item) ~= "function" then + filter = function(t) return t == item end + end + local i = 1 + local d = #tb + while i <= d do + if filter(tb[i]) then + table.remove(tb, i) + d = d - 1 + else + i = i + 1 + end + end +end + +function M:InsertAfter_Modify(tb, item, after) + if after == nil then + table.insert(tb, item) + else + for index, value in ipairs(tb) do + if item == value then + table.insert(tb, index) + return + end + end + table.insert(tb, item) + end +end + +function M:Unpack(tb) + local index = #tb + local function rec(...) + if index >= 1 then + index = index - 1 + return rec(tb[index + 1], ...) + else + return ... + end + end + return rec() +end + +function M:UnpackIfTable(p) + if type(p) == "table" then + return self:Unpack(p) + else + return p + end +end + +function M:Also(tb, block) + block(tb) + return tb +end + +function M:Let(tb, block) + return block(tb) +end + + +-- function M:ClampGroup(tb, min, max) +-- local vmin = math.min(tb) +-- local vmax = math.max(tb) +-- return self:Map(tb, function(t) return RemapVal(t, vmin, vmax, min, max) end) +-- end + +local function AddLinqFunctionsToMetatable(mt) + for k, v in pairs(M) do + mt[k] = function(...) + return v(M, ...) + end + end + for functionName, func in pairs(table) do + mt[functionName] = func + end +end + +AddLinqFunctionsToMetatable(magicTable) + +-- bot mode behaviour + +local Trim = function(v, left, right) + if right >= left then + if v > right then + return right + elseif v < left then + return left + else + return v + end + else + if v > left then + return left + elseif v < right then + return right + else + return v + end + end +end + +M.TrimDesire = function(self, desire) + return Trim(desire, 0, 1) +end + +M.GetAbilityImportance = function(self, cooldown) + return Trim(cooldown/120, 0, 1) +end + +M.IsFarmingOrPushing = function(self, npcBot) + local mode = npcBot:GetActiveMode() + return mode==BOT_MODE_FARM or mode==BOT_MODE_PUSH_TOWER_BOT or mode==BOT_MODE_PUSH_TOWER_MID or mode==BOT_MODE_PUSH_TOWER_TOP or mode == BOT_MODE_DEFEND_TOWER_BOT or mode==BOT_MODE_DEFEND_TOWER_MID or mode==BOT_MODE_DEFEND_TOWER_TOP +end + +M.IsLaning = function(self, npcBot) + local mode = npcBot:GetActiveMode() + return mode == BOT_MODE_LANING +end + +M.IsAttackingEnemies = function(self, npcBot) + local mode = npcBot:GetActiveMode() + return mode == BOT_MODE_ROAM or mode == BOT_MODE_TEAM_ROAM or mode == BOT_MODE_ATTACK or mode == BOT_MODE_DEFEND_ALLY +end + +M.IsRetreating = function(self, npcBot) + return npcBot:GetActiveMode() == BOT_MODE_RETREAT +end +M.NotRetreating = function(self, npcBot) + return npcBot:GetActiveMode() ~= BOT_MODE_RETREAT +end + +M.HasEnoughManaToUseAttackAttachedAbility = function(self, npcBot, ability) + local percent = self:GetManaPercent(npcBot) + if percent >= 0.8 and npcBot:GetMana() >= 650 then + return true + end + return percent >= 0.4 and npcBot:GetMana() >= 300 and npcBot:GetManaRegen() >= npcBot:GetAttackSpeed() / 100 * ability:GetManaCost() * 0.75 +end + +-- function generator + +-- turn a function that returns true, false, nil to a function that decides whether to toggle the ability or not +M.ToggleFunctionToAction = function(self, npcBot, oldConsider, ability) + return function() + local value, target, castType = oldConsider() + if type(value) == "number" then + return value, target, castType + end + if value ~= ability:GetToggleState() and ability:IsFullyCastable() then + return BOT_ACTION_DESIRE_HIGH + else + return 0 + end + end +end + +M.ToggleFunctionToAutoCast = function(self, npcBot, oldConsider, ability) + return function() + local value, target, castType = oldConsider() + if type(value) == "number" then + return value, target, castType + end + if ability:IsFullyCastable() and value ~= ability:GetAutoCastState() then + ability:ToggleAutoCast() + end + return 0 + end +end + +M.PreventAbilityAtIllusion = function(self, npcBot, oldConsiderFunction, ability) + return function() + local desire, target, targetTypeString = oldConsiderFunction() + if desire == 0 or target == nil or target == 0 or self:IsVector(target) or targetTypeString == "Location" then + return desire, target, targetTypeString + end + if self:MustBeIllusion(npcBot, target) then + return 0 + end + return desire, target, targetTypeString + end +end + +M.PreventEnemyTargetAbilityUsageAtAbilityBlock = function(self, npcBot, oldConsiderFunction, ability) + local newConsider = function() + -- TODO: do we consider the base cooldown or the modified cooldown (arcane rune, octarine orb)? Will you crack a sphere's spell block with an ultimate ability when you're on arcane rune? + local desire, target, targetTypeString = oldConsiderFunction() + if desire == 0 or target == nil or target == 0 or self:IsVector(target) or targetTypeString == "Location" then + return desire, target, targetTypeString + end + local oldDesire = desire + if npcBot:GetTeam() ~= target:GetTeam() then -- some ability can cast to both allies and enemies (abbadon_mist_coil, etc) + local cooldown = ability:GetCooldown() + local abilityImportance = self:GetAbilityImportance(cooldown) + + if target:HasModifier("modifier_antimage_counterspell") then + return 0 + end + if target:HasModifier "modifier_item_sphere" or target:HasModifier("modifier_roshan_spell_block") or target:HasModifier("modifier_special_bonus_spell_block") then -- qop lv 25 + if cooldown >= 30 then + desire = desire - abilityImportance + elseif cooldown <= 20 then + desire = desire + abilityImportance + end + end + if target:HasModifier("modifier_item_sphere_target") then + if cooldown >= 60 then + desire = 0 + elseif cooldown >= 30 then + desire = desire - abilityImportance + 0.1 + elseif cooldown <= 20 then + desire = desire + abilityImportance + if abilityImportance > 0.1 then + desire = desire - 0.1 + end + end + end + if target:HasModifier("modifier_item_lotus_orb_active") then + if npcBot:GetActiveMode() == BOT_MODE_RETREAT then + desire = 0 + else + desire = desire - abilityImportance/2 + end + end + if target:HasModifier("modifier_mirror_shield_delay") then + desire = desire - abilityImportance*1.5 + end + + desire = self:TrimDesire(desire) + end + return desire, target, targetTypeString + end + return newConsider +end + +M.GetUsedAbilityInfo = function(self, ability, abilityInfoTable, considerTarget) + abilityInfoTable.lastUsedTime = DotaTime() + abilityInfoTable.lastUsedCharge = ability:GetCurrentCharges() + abilityInfoTable.lastUsedTarget = considerTarget + abilityInfoTable.lastUsedRemainingCooldown = ability:GetCooldownTimeRemaining() +end + +M.AddCooldownToChargeAbility = function(self, oldConsider, ability, abilityInfoTable, additionalCooldown) + return function() + if abilityInfoTable.lastUsedTime == nil then + abilityInfoTable.lastUsedTime = DotaTime() + end + if not (ability:GetCurrentCharges() > 0 and ability:IsFullyCastable()) then + return 0 + end + if DotaTime() <= abilityInfoTable.lastUsedTime + additionalCooldown and abilityInfoTable.lastUsedCharge >= ability:GetCurrentCharges() and abilityInfoTable.lastUsedRemainingCooldown <= ability:GetCooldownTimeRemaining() then + return 0 + end + return oldConsider() + end +end + +M.AutoModifyConsiderFunction = function(self, npcBot, considers, abilitiesReal) + for index, ability in pairs(abilitiesReal) do + if not binlib.Test(ability:GetBehavior(), ABILITY_BEHAVIOR_PASSIVE) and considers[index] == nil then + print("Missing consider function "..ability:GetName()) + elseif binlib.Test(ability:GetTargetTeam(), ABILITY_TARGET_TEAM_ENEMY) and binlib.Test(ability:GetTargetType(), binlib.Or(ABILITY_TARGET_TYPE_HERO, ABILITY_TARGET_TYPE_CREEP, ABILITY_TARGET_TYPE_BUILDING)) and binlib.Test(ability:GetBehavior(), ABILITY_BEHAVIOR_UNIT_TARGET) then + considers[index] = self.PreventAbilityAtIllusion(self, npcBot, considers[index], ability) + if not self:IgnoreAbilityBlock(ability) then + considers[index] = self.PreventEnemyTargetAbilityUsageAtAbilityBlock(self, npcBot, considers[index], ability) + end + end + end + npcBot.abilityRecords = {} +end + +function M:InitAbility(npcBot) + local abilities = NewTable() + local abilityNames = NewTable() + local talents = NewTable() + for i = 0, 23 do + local ability = npcBot:GetAbilityInSlot(i) + if (ability ~= nil) then + if (ability:GetName() ~= "generic_hidden") then + if (ability:IsTalent() == true) then + table.insert(talents, ability:GetName()) + else + table.insert(abilityNames, ability:GetName()) + table.insert(abilities, ability) + end + end + end + end + npcBot.abilityInited = true + return abilityNames, abilities, talents +end + +-- ability information + +local keysBeforeAbilityInformation = M:Keys(M) + +M.UndisjointableProjectiles = { + "alchemist_berser_potion", + "alchemist_unstable_concoction_throw", + "arc_warden_spark_wraith", + "grimstroke_phantoms_embrace", + "earthshaker_echoslam", + "gyrocopter_homing_missile", + "beastmaster_hawk_dive", + "huskar_life_break", + "lich_chain_frost", + "medusa_cold_blooded", + "medusa_mystic_snake", + "mirana_starstorm", + "necrolyte_death_pulse", + "necrolyte_death_seeker", + "oracle_fortunes_end", + "queenofpain_scream_of_pain", + "skywrath_mage_arcane_bolt", + "snapfire_firesnap_cookie", + "spectre_spectral_dagger", + "tiny_toss", + "tusk_snowball", + "witch_doctor_paralyzing_cask", +} + +M.targetTrackingStunAbilities = { + "alchemist_berser_potion", + "alchemist_unstable_concoction_throw", + "chaos_knight_chaos_bolt", + "dragon_knight_dragon_tail", + "gyrocopter_homing_missile", + "morphling_adaptive_strike_str", + "mud_golem_hurl_boulder", + "skeleton_king_hellfire_blast", + "sven_storm_hammer", + "vengefulspirit_magic_missile", + "windrunner_shackleshot", +} + +M.targetNonTrackingStunAbilities = { + "bane_fiends_grip", + "beastmaster_primal_roar", + "dark_willow_cursed_crown", + "enigma_malefice", + "invoker_cold_snap", + "item_abyssal_blade", + "lich_sinister_gaze", + "luna_lucent_beam", + "necrolyte_reapers_scythe", + "ogre_magi_fireblast", + "ogre_magi_unrefined_fireblast", + "pudge_dismember", + "rubick_telekinesis", + "shadow_shaman_shackles", + "storm_spirit_electric_vortex", +} + +M.targetStunAbilities = M:Concat(M.targetTrackingStunAbilities, M.targetNonTrackingStunAbilities) + +M.locationStunAbilities = { + "axe_berserkers_call", + "centaur_hoof_stomp", + "dark_seer_vacuum", + "dark_willow_cursed_crown", + "dawnbreaker_fire_wreath", + "dawnbreaker_solar_guardian", + "earthshaker_fissure", + "earthshaker_enchant_totem", + "earthshaker_echoslam", + "enigma_black_hole", + "faceless_void_chronosphere", + "jakiro_ice_path", + "keeper_of_the_light_will_o_wisp", + "kunkka_ghostship", + "kunkka_torrent", + "kunkka_torrent_storm", + "lina_light_strike_array", + "magnataur_skewer", + "magnataur_horn_toss", + "magnataur_reverse_polarity", + "monkey_king_boundless_strike", + "phoenix_supernova", + "puck_dream_coil", + "sand_king_burrowstrike", + "slardar_slithereen_crush", + "tidehunter_ravage", +} + +M.targetTrackingDisableAbilities = { + "gleipnir_eternal_chains", + "naga_siren_ensnare", + "riki_sleeping_dart", + "viper_viper_strike", +} + +M.targetNonTrackingDisableAbilities = { + "bloodseeker_rupture", + "doom_bringer_doom", + "ember_spirit_searing_chains", + "grimstroke_ink_creature", + "grimstroke_soul_chain", + "item_sheepstick", + "lion_vex", + "shadow_demon_purge", + "shadow_shaman_voodoo", +} + +M.targetDisableAbilities = M:Concat(M.targetNonTrackingStunAbilities, M.targetTrackingDisableAbilities) + +M.locationDisableAbilities = { + "dark_willow_bramble_maze", + "death_prophet_silence", + "disruptor_kinetic_field", + "disruptor_static_storm", + "drow_ranger_wave_of_silence", + "elder_titan_echo_stomp", + "invoker_deafening_wave", + "treant_overgrowth", +} + +M.targetTrackingHeavyDamageAbilities = { + "item_ethereal_blade", + "lich_chain_frost", + "lion_finger_of_death", + "morphling_adaptive_strike_agi", + "sniper_assassinate", +} + +M.targetNonTrackingHeavyDamageAbilities = { + "antimage_mana_void", + "item_dagon", + "lina_laguna_blade", + "pugna_life_drain", + "tinker_laser", + "zuus_lightning_bolt", +} + +M.targetHeavyDamageAbilities = M:Concat(M.targetTrackingHeavyDamageAbilities, M.targetNonTrackingHeavyDamageAbilities) + +M.locationHeavyDamageAbilities = { + "ancient_apparition_ice_blast", + "antimage_mana_void", + "disruptor_static_storm", + "invoker_chaos_meteor", + "invoker_sun_strike", + "jakiro_macropyre", + "kunkka_ghostship", + "nevermore_requiem_of_souls", + "obsidian_destroyer_sanitys_eclipse", + "phoenix_sun_ray", + "puck_dream_coil", + "pugna_nether_blast", + "queenofpain_sonic_wave", + "sand_king_epicenter", + "skywrath_mage_mystic_flare", + "venomancer_poison_nova", +} + +M.heavyDamageAbilities = M:Concat(M.targetTrackingHeavyDamageAbilities, M.locationHeavyDamageAbilities) + +M.dodgeWorthAbilities = M:Concat(M.targetStunAbilities, M.locationStunAbilities, M.heavyDamageAbilities) + +M.invisibleModifiers = { + "modifier_bounty_hunter_wind_walk", + "modifier_clinkz_wind_walk", + "modifier_dark_willow_shadow_realm_buff", + "modifier_item_glimmer_cape_glimmer", + "modifier_invoker_ghost_walk_self", + "modifier_nyx_assassin_vendetta", + "modifier_item_phase_boots_active", + "modifier_item_shadow_amulet_fade", + "modifier_item_invisibility_edge_windwalk", + "modifier_shadow_fiend_requiem_thinker", + "modifier_item_silver_edge_windwalk", + "modifier_windrunner_wind_walk", + "modifier_storm_wind_walk", + "modifier_templar_assassin_meld", + "modifier_visage_silent_as_the_grave", + "modifier_weaver_shukuchi", +} + +M.phaseModifiers = { + "modifier_bounty_hunter_wind_walk", + "modifier_clinkz_wind_walk", + "modifier_dark_willow_shadow_realm_buff", + "modifier_faceless_void_chronosphere_selfbuff", + "modifier_item_glimmer_cape_glimmer", + "modifier_invoker_ghost_walk_self", + "modifier_nyx_assassin_vendetta", + "modifier_item_phase_boots_active", + "modifier_item_shadow_amulet_fade", + "modifier_item_invisibility_edge_windwalk", + "modifier_shadow_fiend_requiem_thinker", + "modifier_item_silver_edge_windwalk", + "modifier_slardar_sprint", + "modifier_storm_wind_walk", + "modifier_templar_assassin_meld", + "modifier_weaver_shukuchi", +} + +M.phaseUnits = { + "npc_dota_brewmaster_fire_1", + "npc_dota_brewmaster_fire_2", + "npc_dota_brewmaster_fire_3", + "npc_dota_broodmother_web", + "npc_dota_courier", + "npc_dota_phoenix_sun", + "npc_dota_juggernaut_healing_ward", + "npc_dota_techies_land_mine", + "npc_dota_techies_stasis_trap", + "npc_dota_techies_remote_mine", + "npc_dota_weaver_swarm", +} + +M.unobstructedMovementModifiers = { + "modifier_batrider_firefly", + "modifier_broodmother_spin_web", + "modifier_centaur_stampede", + "modifier_dragon_knight_dragon_form", + "modifier_item_giants_ring_giants_foot", + "modifier_lich_sinister_gaze", + "modifier_legion_commander_duel", + "modifier_nyx_assassin_vendetta", + "modifier_spectre_spectral_dagger_path_phased", + "modifier_item_spider_legs_active", + "modifier_visage_silent_as_the_grave", +} + +M.flyingModifiers = { + "modifier_rattletrap_jetpack", + "modifier_night_stalker_darkness", + "modifier_winter_wyvern_arctic_burn_flight", +} + +M.flyingUnits = { + "npc_dota_visage_familiar1", + "npc_dota_visage_familiar2", + "npc_dota_visage_familiar3", + "npc_dota_flying_courier", + "npc_dota_beastmaster_hawk", +} + +M.positiveForceMovementModifiers = { + "modifier_faceless_void_time_walk", + "modifier_huskar_life_break_charge", + "modifier_magnataur_skewer_movement", + "modifier_monkey_king_bounce", + "modifier_monkey_king_bounce_leap", + "modifier_monkey_king_tree_dance_activity", + "modifier_monkey_king_bounce_perch", + "modifier_monkey_king_right_click_jump_activity", + "modifier_pangolier_swashbuckle", + "modiifer_pangolier_shield_crash_jump", + "modifier_pangolier_rollup", + "modifier_snapfire_firesnap_cookie", + "modifier_snapfire_gobble_up", + "modifier_sand_king_burrowstrike", + "modifier_techies_suicide_leap", +} + +M.timeSensitivePositiveModifiers = { + "modifier_item_black_king_bar", + "modifier_faceless_void_chronosphere_selfbuff", + "modifier_medusa_stone_gaze", + "modifier_monkey_king_fur_army_soldier_in_position", +} +-- sorted by importance, used by dispell abilities + +M.basicDispellablePositiveModifiers = { + "modifier_omniknight_guardian_angle", + "modifier_ember_spirit_flame_guard", + "modifier_legion_commander_press_the_attack", + "modifier_windrunner_windrun", + "modifier_lich_frost_shield", + "modifier_oracle_purifying_flames", + "modifier_ogre_magi_bloodlust", + "modifier_treant_living_armor", + "modifier_mirana_leap_buff", + "modifier_necrolyte_death_seeker", + "modifier_necrolyte_sadist_active", + "modifier_pugna_decrepify", + "modifier_item_ethereal_blade_ethereal", + "modifier_ghost_state", + "modifier_abaddon_frostmourne_buff", + "modifier_item_mjollnir_static", + "modifier_visage_silent_as_the_grave", + "modifier_spirit_breaker_bulldoze", + "modifier_item_spider_legs_active", + "modifier_item_bullwhip_buff", +} + +M.basicDispellWorthPositiveModifiers = { + "modifier_omniknight_guardian_angle", + "modifier_ember_spirit_flame_guard", + "modifier_legion_commander_press_the_attack", + "modifier_windrunner_windrun", + "modifier_lich_frost_shield", + "modifier_oracle_purifying_flames", + "modifier_ogre_magi_bloodlust", + "modifier_treant_living_armor", + "modifier_mirana_leap_buff", + "modifier_necrolyte_death_seeker", + "modifier_necrolyte_sadist_active", + "modifier_pugna_decrepify", + "modifier_item_ethereal_blade_ethereal", + "modifier_ghost_state", +} + +M.basicDispellWorthNegativeModifiers = { + "modifier_abaddon_frostmourne_debuff_bonus", +} + +M.basicDispellableNegativeModifiers = { + "modifier_abaddon_frostmourne_debuff", + "modifier_abaddon_frostmourne_debuff_bonus", + +} + +M.unbreakableChannelAbilities = { + "puck_phase_shift", + "pangolier_gyroshell", + "lone_druid_true_form", + "phoenix_supernova", + "lycan_shapeshift", +} + +M.nonIllusionModifiers = {} + +M.valubleNeutrals = { + "npc_dota_neutral_alpha_wolf", + "npc_dota_neutral_centaur_khan", + "npc_dota_neutral_polar_furbolg_ursa_warrior", + "npc_dota_neutral_dark_troll_warlord", + "npc_dota_neutral_mud_golem", + "npc_dota_neutral_satyr_hellcaller", +} + +M.valubleAncientNeutrals = { + "npc_dota_neutral_black_dragon", + "npc_dota_neutral_rock_golem", + "npc_dota_neutral_big_thunder_lizard", +} + +M.hypnosisModifiers = { + "modifier_lich_sinister_gaze", + "modifier_void_spirit_aether_remnant_pull", + "modifier_keeper_of_the_light_will_o_wisp", +} + +M.fearModifiers = { + "modifier_dark_willow_debuff_fear", + "modifier_lone_druid_savage_roar", + "modifier_shadow_fiend_requiem_fear", + "modifier_terrorblade_fear", +} + +M.hexModifiers = { + "modifier_lion_voodoo", + "modifier_shadow_shaman_voodoo", + "modifier_sheepstick_debuff", + "modifier_item_princes_knife_hex", + "modifier_hexxed", -- dazzle_poison_touch lv 25 +} + +M.silenceModifiers = { + "modifier_abaddon_frostmourne_debuff_bonus", + "modifier_silence", + "modifier_bloodthorn_debuff", + "modifier_disruptor_static_storm", + "modifier_doom_bringer_doom", + "modifier_drow_ranger_wave_of_silence", + "modifier_earth_spirit_geomagnetic_grip_debuff", + "modifier_enigma_black_hole_pull", + "modifier_grimstroke_ink_creature_debuff", + "modifier_legion_commander_duel", + "modifier_item_mask_of_madness_berserk", + "modifier_night_stalker_crippling_fear", + "modifier_orchid_malevolence_debuff", + "modifier_riki_smoke_screen", + "modifier_silencer_global_silence", + "modifier_silencer_last_word", + "modifier_skywrath_mage_ancient_seal", +} + +M.timedSilenceModifiers = { + "modifier_abaddon_frostmourne_debuff_bonus", + "modifier_silence", + "modifier_bloodthorn_debuff", + "modifier_doom_bringer_doom", + "modifier_drow_ranger_wave_of_silence", + "modifier_earth_spirit_geomagnetic_grip_debuff", + "modifier_grimstroke_ink_creature_debuff", + "modifier_legion_commander_duel", + "modifier_item_mask_of_madness_berserk", + "modifier_orchid_malevolence_debuff", + "modifier_silencer_global_silence", + "modifier_silencer_last_word", + "modifier_skywrath_mage_ancient_seal", +} + +M.magicImmuneModifiers = { + "modifier_item_black_king_bar", + "modifier_life_stealer_rage", + "modifier_juggernaut_blade_fury", + "modifier_minotaur_horn_immune", + "modifier_elder_titan_echo_stomp_magic_immune", + "modifier_huskar_life_break_charge", + "modifier_legion_commander_press_the_attack_immunity", + "modifier_lion_mana_drain_immunity", +} + +M.muteModifiers = { + "modifier_tusk_snowball", + "modifier_doom_bringer_doom", + "modifier_disruptor_static_storm_mute", +} + +M.breakModifiers = { + -- "modifier_doom_bringer_doom",--only breaks with scepter + "modifier_hoodwink_sharpshooter", + "modifier_phantom_assassin_fan_of_knives", + -- "modifier_shadow_demon_purge_slow",--only break with scepter + "modifier_silver_edge_debuff", + -- "modifier_spirit_breaker_greaterbash_break",--only break with shard + "modifier_viper_nethertoxin", +} +-- TODO: how to record the caster of these abilities + +M.noTrueSightRootAbilityAssociation = { + dark_willow_branble_maze = "modifier_dark_willow_bramble_maze", + item_diffusal_blade = "modifier_rooted", -- most people don't know diffusal blade apply root on non-hero units +} + +M.conditionalTrueSightRootAbilityAssociation = { + dark_troll_warlord_ensnare = "modifier_dark_troll_warlord_ensnare", + ember_spirit_searing_chains = "modifier_ember_spirit_searing_chains", + oracle_fortunes_end = "modifier_oracle_fortunes_end_purge", + item_rod_of_atos = "modifier_rod_of_atos_debuff", +} + +M.permanentTrueSightRootAbilityAssociation = { + broodmother_silken_bola = "modifier_broodmother_silken_bola", + crystal_maiden_frostbite = "modfifier_crystal_maiden_frostbite", + meepo_earthbind = "modifier_meepo_earthbind", + naga_siren_ensnare = "modifier_naga_siren_ensnare", + spirit_bear_entangling_claws = "modifier_lone_druid_spirit_bear_entangle_effect", + techies_stasis_trap = "modifier_techies_stasis_trap_stunned", + treant_overgrowth = "modifier_treant_overgrowth", + troll_warlord_berserkers_rage = "modifier_troll_warlord_berserkers_rage_ensnare", + abyssal_underlord_pit_of_malice = "modifier_abyssal_underlord_pit_of_malice_ensare", +} + +M.rootAbilityAssociation = M:Concat(M.noTrueSightRootAbilityAssociation, M.conditionalTrueSightRootAbilityAssociation, M.permanentTrueSightRootAbilityAssociation) + +local keysAfterAbilityInformation = M:Keys(M) +local abilityInformationKeys = keysAfterAbilityInformation:RemoveAll(keysBeforeAbilityInformation) +abilityInformationKeys:ForEach(function(t) setmetatable(M[t], magicTable) end) +abilityInformationKeys = abilityInformationKeys:Filter(function(t) return t:match("AbilityAssociation") end) + +local function ExtendAssociation(association) + return association:MapDic(function(key, value) return key end), association:Map(function(key, value) return value end):Distinct() +end +abilityInformationKeys:ForEach(function(t) + local a, b = ExtendAssociation(M[t]) + local k = t:sub(1, #t-#"AbilityAssociation") + M[k.."Abilities"] = a + M[k.."Modifiers"] = b +end) + + +-- unit function + +function M:IsRoshan(npcTarget) + return npcTarget ~= nil and npcTarget:IsAlive() and string.find(npcTarget:GetUnitName(), "roshan") +end + +function M:IsHero(t) + return t:IsHero() +end + +function M:IsTempestDouble(npc) + return npc:HasModifier("modifier_arc_warden_tempest_double") +end + +function M:IsLoneDruidBear(npc) + return string.match(npc:GetUnitName(), "npc_dota_lone_druid_bear") +end + +function M:IsVisageFamiliar(npc) + return string.match(npc:GetUnitName(), "npc_dota_visage_familiar") +end + +function M:IsBrewmasterPrimalSplit(npc) + local unitName = npc:GetUnitName() + return string.match(unitName, "npc_dota_brewmaster_") +end + +M.GetIncomingDodgeWorthProjectiles = function(self, npc) + local health = npc:GetHealth() + local projectiles = npc:GetIncomingTrackingProjectiles() + projectiles = self:Filter(projectiles, function(t) + if t.is_attack or npc:GetTeam() == t.caster:GetTeam() then + return false + end + local ability = t.ability + if ability then + local abilityName = ability:GetName() + if self:Contains(self.UndisjointableProjectiles, abilityName) then + return false + end + if self:Contains(self.targetTrackingStunAbilities, abilityName) or self:Contains(self.targetTrackingDisableAbilities, abilityName) or self:Contains(self.targetTrackingHeavyDamageAbilities, abilityName) or npc:GetHealth() <= npc:GetActualIncomingDamage(ability:GetAbilityDamage(), ability:GetDamageType()) then + return true + end + return false + end + return true + end) + return projectiles +end + +M.GetTargetHealAmplifyPercent = function(self, npc) + local modifiers = npc:FindAllModifiers() + local amplify = 1 + for i = 1, npc:NumModifiers() do + local modifierName = npc:GetModifierName(i) + if modifierName == "modifier_ice_blast" then + return 0 + end + if modifierName == "modifier_item_spirit_vessel_damage" then + amplify = amplify - 0.45 + end + if modifierName == "modifier_holy_blessing" then + amplify = amplify + 0.3 + end + if modifierName == "modifier_necrolyte_sadist_active" then -- ghost shroud + amplify = amplify + 0.75 + end + if modifierName == "modifier_wisp_tether_haste" then + amplify = amplify + 0.6 -- 0.8/1/1.2 + end + if modifierName == "modifier_oracle_false_promise" then + amplify = amplify + 1 + end + end + return amplify +end + +M.IsChannelingItem = function(self, npc) + return npc:HasModifier("modifier_item_meteor_hammer") or npc:HasModifier("modifier_teleporting") or npc:HasModifier("modifier_boots_of_travel_incoming") +end + +M.IsChannelingAbility = function(self, npc) + return npc:IsChanneling() and not self:IsChannelingItem(npc) +end + +function M:IsChannelingBreakWorthAbility(npc) + if not npc:IsChanneling() then + return false + end + local ability = npc:GetCurrentActiveAbility() + if ability == nil then + if npc:HasModifier("modifier_teleporting") then + return true + end + local item = self:GetAvailableItem(npc, "item_fallen_sky") + return item ~= nil + end + local name = ability:GetName() + if self:Contains(self.unbreakableChannelAbilities, name) then + return false + end + return true +end + +M.RadiantPlayerId = GetTeamPlayers(TEAM_RADIANT) +M.DirePlayerId = GetTeamPlayers(TEAM_DIRE) + +M.GetTeamPlayers = function(self, team) + if team == TEAM_RADIANT then + return self.RadiantPlayerId + else + return self.DirePlayerId + end +end + +M.GetEnemyTeamMemberNames = function(self, npcBot) + local enemies = self:GetEnemyHeroUnique(npcBot, GetUnitList(UNIT_LIST_ENEMY_HEROES)) + return self:Map(enemies, function(t) return t:GetUnitName() end) +end + +M.enemyVisibleIllusionModifiers = { + "modifier_illusion", + -- "modifier_phantom_lancer_doppelwalk_illusion", + -- "modifier_phantom_lancer_juxtapose_illusion", + "modifier_terrorblade_conjureimage", + "modifier_grimstroke_scepter_buff", + "modifier_arc_warden_tempest_double", + "modifier_skeleton_king_reincarnation_active", + "modifier_vengefulspirit_hybrid_special", +} + +M.MustBeIllusion = function(self, npcBot, target) + if npcBot:GetTeam() == target:GetTeam() then + return target:IsIllusion() or self:HasAnyModifier(target, self.enemyVisibleIllusionModifiers) + end + if self:Contains(self:GetTeamPlayers(npcBot:GetTeam()), target:GetPlayerID()) or target.markedAsIllusion then + return true + end + if target.markedAsRealHero then + return false + end + if not IsHeroAlive(target:GetPlayerID()) then + return true + end + return false +end +M.MayNotBeIllusion = function(self, npcBot, target) return not self:MustBeIllusion(npcBot, target) end + +function M:IsOnSameTeam(a, b) + return a:GetTeam() == b:GetTeam() +end + +function M:IsNonIllusionHero(npcBot, target) + return self:MayNotBeIllusion(npcBot, target) and self:IsHero(target) +end + +function M:HasNonIllusionModifier(npc) + return self:HasAnyModifier(npc, self.nonIllusionModifiers) +end + +function M:CanIllusionUseAbility(npc) + local name = npc:GetUnitName() + local ability = npc:GetCurrentActiveAbility() + if ability == nil then + return false + end + if name == "npc_dota_hero_bane" and self:HasScepter(npc) and ability:GetName() == "bane_fiends_grip" then + return true + end +end + +M.DetectIllusion = function(self, npcBot) + local nearbyEnemies = self:GetNearbyNonIllusionHeroes(npcBot, 1599) + nearbyEnemies = self:Filter(nearbyEnemies, function(t) return string.match(t:GetUnitName(), "npc_dota_hero") end) + local nearbyEnemyGroups = self:GroupBy(nearbyEnemies, function(t) return t:GetUnitName() end) + nearbyEnemyGroups = self:Filter(nearbyEnemyGroups, function(t) return #t > 1 end) + self:ForEach(nearbyEnemyGroups, function(nearbyEnemyGroup) + local castingEnemies = self:Filter(nearbyEnemyGroup, function(t) + return (t:IsUsingAbility() or t:IsChanneling() or self:HasNonIllusionModifier(t) or t.markedAsRealHero) and not t.markedAsIllusion + end) + local castingEnemy = castingEnemies[1] + if castingEnemy and not self:CanIllusionUseAbility(castingEnemy) then + castingEnemy.markedAsRealHero = true + castingEnemies = self:Remove(nearbyEnemyGroup, castingEnemy) + self:ForEach(castingEnemies, function(t) + t.markedAsIllusion = true + end) + end + end) +end + +M.GetNearbyHeroes = function(self, npcBot, range, getEnemy, botModeMask) + range = range or 1200 + if getEnemy == nil then + getEnemy = true + end + botModeMask = botModeMask or BOT_MODE_NONE + local heroes = npcBot:GetNearbyHeroes(range, getEnemy, botModeMask) + return heroes +end + +M.GetNearbyNonIllusionHeroes = function(self, npcBot, range, getEnemy, botModeMask) + range = range or 1200 + if getEnemy == nil then + getEnemy = true + end + botModeMask = botModeMask or BOT_MODE_NONE + local heroes = npcBot:GetNearbyHeroes(range, getEnemy, botModeMask) + return self:Filter(heroes, function(t) return self:MayNotBeIllusion(npcBot, t) end) +end + +function M:AttackOnceDamage(npcBot, target) + return target:GetActualIncomingDamage(npcBot:GetAttackDamage() - npcBot:GetBaseDamageVariance()/2, DAMAGE_TYPE_PHYSICAL) +end + +function M:GetNearbyAttackableCreeps(npcBot, range, getEnemy) + if getEnemy == nil then + getEnemy = true + end + local creeps = npcBot:GetNearbyCreeps(range, getEnemy) + if getEnemy then + creeps = self:Filter(creeps, function(t) return t:HasModifier("modifier_fountain_glyph") end) + end + return creeps +end + +M.GetNearbyAllUnits = function(self, npcBot, range) + local h1 = npcBot:GetNearbyHeroes(range, true, BOT_MODE_NONE) + local h2 = self:Remove(npcBot:GetNearbyHeroes(range, false, BOT_MODE_NONE), npcBot) + local h3 = npcBot:GetNearbyCreeps(range, true) + local h4 = npcBot:GetNearbyCreeps(range, false) + return self:Concat(h1, h2, h3, h4) +end + +function M:GetNearbyEnemyUnits(npc, range) + local h1 = npc:GetNearbyHeroes(range, true, BOT_MODE_NONE) + local h3 = npc:GetNearbyCreeps(range, true) + return self:Concat(h1, h3) +end + + +M.GetEnemyHeroUnique = function(self, npcBot, enemies) + local p = self:Filter(enemies, function(t) self:MayNotBeIllusion(npcBot, t) end) + local g = NewTable() + local readNames = NewTable() + for _, enemy in pairs(p) do + local name = enemy:GetUnitName() + if not self:Contains(readNames, name) then + table.insert(readNames, name) + table.insert(g, enemy) + end + end + return g +end + +M.GetMovementSpeedPercent = function(self, npc) + return npc:GetCurrentMovementSpeed() / npc:GetBaseMovementSpeed() +end +M.CanHardlyMove = function(self, npc) + return npc:IsStunned() or npc:IsRooted() or npc:GetCurrentMovementSpeed() <= 150 +end + +M.GetModifierRemainingDuration = function(self, npc, modifierName) + local mod = npc:GetModifierByName(modifierName) + if mod ~= -1 then + return npc:GetModifierRemainingDuration(mod) + end + return 0 +end + +M.imprisonmentModifier = { + "modifier_item_cyclone", + "modifier_item_wind_waker", + "modifier_shadow_demon_disruption", + "modifier_obsidian_destroyer_astral_imprisonment_prison", + "modifier_brewmaster_storm_cyclone", + "modifier_invoker_tornado", + --"modifier_x_marks_the_target", +} +M.GetImprisonmentRemainingDuration = function(self, npc) + return self:First(self:Map(self.imprisonmentModifier, function(t) return self:GetModifierRemainingDuration(npc, t) end), function(t) return t ~= 0 end) or 0 +end + +function M:GetMagicImmuneRemainingDuration(npc) + local remainingTime = self:Map(self.magicImmuneModifiers, function(t) return { t, self:GetModifierRemainingDuration(npc, t)} end) + remainingTime = self:SortByMaxFirst(remainingTime, function(t) return t[2] end) + remainingTime = remainingTime[1] + return remainingTime and remainingTime[2] or 0 +end + +function M:GetSilenceRemainingDuration(npc) + local silenceModifierRemainings = self:Map(self.timedSilenceModifiers, function(t) + return self:GetModifierRemainingDuration(npc, t) + end) + if npc:HasModifier("modifier_disruptor_static_storm") then + table.insert(silenceModifierRemainings, 1, 6) + end + if npc:HasModifier("modifier_enigma_black_hole_pull") or npc:HasModifier("modifier_riki_smoke_screen") then + table.insert(silenceModifierRemainings, 1, 4) + end + silenceModifierRemainings = #silenceModifierRemainings ~= 0 and math.max(self:Unpack(silenceModifierRemainings)) or 0 + return silenceModifierRemainings +end + +function M:GetStunRemainingDuration(npc) + return self:IsStunned(npc) and 1 or 0 +end + +M.GetEnemyHeroNumber = function(self, npcBot, enemies) + return #self:GetEnemyHeroUnique(npcBot, enemies) +end + +function M:HasPhasedMovement(npc) + return self:HasAnyModifier(npc, self.phaseModifiers) or self:Contains(self.phaseUnits, npc:GetUnitName()) +end + +function M:HasUnobstructedMovement(npc) + if self:HasAnyModifier(npc, self.flyingModifiers) or self:Contains(self.flyingUnits, npc:GetUnitName()) then + if string.match(npc:GetUnitName(), "npc_dota_visage_familiar") then + return npc:HasModifier("modifier_rooted") + end + return true + end + local activeFlyingModifiers = self:Filter(self.unobstructedMovementModifiers, function(t) return npc:HasModifier(t) end) + if #activeFlyingModifiers ~= 0 then + local dragonKnightDragonForm = self:IndexOf(activeFlyingModifiers, "modifier_dragon_knight_dragon_form") + if dragonKnightDragonForm ~= -1 then + local ability = npc:GetAbilityByName("dragon_knight_elder_dragon_form") + if ability == nil or not (ability:GetLevel() == 4) then + table.remove(activeFlyingModifiers, dragonKnightDragonForm) + end + end + local stampede = self:IndexOf(activeFlyingModifiers, "modifier_centaur_stampede") + if stampede ~= -1 then + local ability = npc:GetAbilityByName("centaur_stampede") + if ability == nil or not self:hasScepter(npc) then + table.remove(activeFlyingModifiers, stampede) + end + end + end + return #activeFlyingModifiers ~= 0 +end + +-- item function + +M.GetAvailableItem = function(self, npc, itemName) + for i = 0, 5 do + local item = npc:GetItemInSlot(i) + if item ~= nil and item:GetName() == itemName then + return item + end + end +end + +local radianceAncientLocation = Vector(-7200,-6666) +local direAncientLocation = Vector(7137,6548) + +M.GetAncientLocation = function(self, npc) + if npc:GetTeam() == TEAM_RADIANT then + return radianceAncientLocation + else + return direAncientLocation + end +end + +M.GetDistanceFromAncient = function(self, npc) + local fountain = self:GetAncientLocation(npc) + return GetUnitToLocationDistance(npc, fountain) +end + +M.TryUseTp = function(self, npc) + local item = npc:GetItemInSlot(15) + if item ~= nil and item:IsFullyCastable() and self:CanMove(npc) then + local distanceFromFountain + if npc:GetTeam() == TEAM_RADIANT then + distanceFromFountain = radianceAncientLocation + Vector(400, 400) + else + distanceFromFountain = direAncientLocation + Vector(-400, -400) + end + npc:ActionImmediate_UseAbilityOnLocation(item, distanceFromFountain) + return true + end +end + +M.GetAvailableBlink = function(self, npc) + local blinks = { + "item_blink", + "item_overwhelming_blink", + "item_swift_blink", + "item_arcane_blink", + } + return self:Aggregate(nil, blinks, function(a, blinkName) + return a or self:GetAvailableItem(npc, blinkName) + end) +end + +function M:GetAvailableTravelBoots(npc) + local travelBoots = { + "item_travel_boots", + "item_travel_boots_2", + } + return self:Aggregate(nil, travelBoots, function(seed, t) + return seed or self:GetAvailableItem(npc, t) + end) +end + +M.GetEmptyInventorySlots = function(self, npc) + local g = 0 + for i = 0, 5 do + if npc:GetItemInSlot(i) == nil then + g = g+1 + end + end + return g +end + +M.GetEmptyItemSlots = function(self, npc) + local g = 0 + for i = 0, 8 do + if npc:GetItemInSlot(i) == nil then + g = g+1 + end + end + return g +end + +M.GetEmptyBackpackSlots = function(self, npc) + local g = 0 + for i = 6, 8do + if npc:GetItemInSlot(i) == nil then + g = g+1 + end + end + return g +end + +M.SwapItemToBackpack = function(self, npc, itemIndex) + for i = 6, 8 do + if npc:GetItemInSlot(i) == nil then + npc:ActionImmediate_SwapItems(itemIndex, i) + return true + end + end + return false +end + +M.GetCarriedItems = function(self, npc) + local g = NewTable() + for i = 0, 8 do + local item = npc:GetItemInSlot(i) + if item ~= nil then + item.slotIndex = i + table.insert(g, item) + end + end + return g +end + +M.GetInventoryItems = function(self, npc) + local g = NewTable() + for i = 0, 5 do + local item = npc:GetItemInSlot(i) + if item ~= nil then + item.slotIndex = i + table.insert(g, item) + end + end + return g +end + +M.GetInventoryItemNames = function(self, npc) + local g = NewTable() + for i = 0, 5 do + local item = npc:GetItemInSlot(i) + if item ~= nil then + item.slotIndex = i + table.insert(g, item:GetName()) + end + end + return g +end + +M.GetStashItems = function(self, npc) + local g = NewTable() + for i = 9, 14 do + local item = npc:GetItemInSlot(i) + if item ~= nil then + item.slotIndex = i + table.insert(g, item) + end + end + return g +end + +function M:GetCourierItems(courier) + local g = NewTable() + for i = 0, 8 do + local item = courier:GetItemInSlot(i) + if item then + table.insert(g, item) + end + end + return g +end + +function M:GetMyCourier(npcBot) + if npcBot.courierIDNew == nil then + self:FindCourier(npcBot) + end + return GetCourier(npcBot.courierIDNew) +end + +function M:FindCourier(npcBot) + for i = 0,4 do + local courier = GetCourier(i) + if courier ~= nil then + if courier:GetPlayerID() == npcBot:GetPlayerID() then + npcBot.courierIDNew = i + end + end + end +end + +M.GetAllBoughtItems = function(self, npcBot) + local g = NewTable() + for i = 0, 15 do + local item = npcBot:GetItemInSlot(i) + if item then + table.insert(g, item) + end + end + if DotaTime() >= -70 then + g = self:Concat(g, self:GetCourierItems(self:GetMyCourier(npcBot))) + end + return g +end + +M.IsBoots = function(self, item) + if type(item) ~= "string" then + item = item:GetName() + end + return string.match(item, "boots") or item == "item_guardian_greaves" or #item >= 17 and string.sub(item, 17) == "item_power_treads" +end + +M.SwapCheapestItemToBackpack = function(self, npc) + local cheapestItem = self:First(self:Sort(self:Filter(self:GetInventoryItems(npc), function(t) + return not self:IsBoots(t) and not string.match(t:GetName(), "ward") + end), function(a, b) return GetItemCost(a:GetName()) - GetItemCost(b:GetName()) end)) + if cheapestItem == nil then + return false + end + return self:SwapItemToBackpack(npc, cheapestItem.slotIndex) +end + +M.SuitableForSilence = function(self, npc, target) + return self:MayNotBeIllusion(npc, target) and not target:IsMagicImmune() and not self:IsInvulnerable(target) +end + +M.GetHeroFullName = function(self, s) + return "npc_dota_hero_"..s +end +M.GetHeroShortName = function(self, s) + return string.sub(s, 12) +end + +M.IsMeleeHero = function(self, npc) + local range = npc:GetAttackRange() + local name = npc:GetUnitName() + return range <= 210 or name == self:GetHeroFullName("tiny") or name == self:GetHeroFullName("doom_bringer") or name == self:GetHeroFullName("pudge") +end + +function M:HasAnyModifier(npc, modifierGroup) + return self:First(modifierGroup, function(t) return npc:HasModifier(t) end) +end + +M.AttackPassiveAbilities = { + "doom_bringer_infernal_blade", + "drow_ranger_frost_arrows", + "clinkz_fire_arrows", + "viper_poison_attack", + "obsidian_destroyer_arcane_orb", +} +M.OtherIgnoreAbilityBlockAbilities = { + "batrider_flaming_lasso", + "gyrocopter_homing_missile", + "axe_culling_blade", +} +M.IgnoreAbilityBlockAbilities = { + "dark_seer_ion_shell", + "grimstroke_soulbind", + "rubick_spell_steal", + "spectre_spectral_dagger", + "morphling_morph", + "urn_of_shadows_soul_release", + "spirit_vessel_soul_release", + "medallion_of_courage_valor", + "solar_crest_armor_shine", +} + +M.IgnoreAbilityBlock = function(self, ability) + local abilityName = ability:GetName() + return self:Contains(self.AttackPassiveAbilities, abilityName) or self:Contains(self.IgnoreAbilityBlockAbilities, abilityName) or self:Contains(self.OtherIgnoreAbilityBlockAbilities, abilityName) +end + +M.AbilityRetargetModifiers = { + "modifier_antimage_counterspell", + "modifier_item_lotus_orb_active", + "modifier_nyx_assassin_spiked_carapace", + -- "modifier_item_blade_mail", +} +M.HasAbilityRetargetModifier = function(self, npc) + return self:HasAnyModifier(npc, self.AbilityRetargetModifiers) +end + +M.CanMove = function(self, npc) + return not npc:IsStunned() and not npc:IsRooted() and not self:IsNightmared(npc) and not self:IsTaunted(npc) +end + +function M:CannotMove(npc) + return -- npc:IsStunned() or self:IsNightmared(npc) or --actually still able to cast abilities or move while stunned or nightmared, but provides no dfference + npc:IsRooted() or self:IsTaunted(npc)or self:IsHypnosed(npc) or self:IsFeared(npc) +end + +function M:CannotTeleport(npc) + return npc:IsRooted() or self:IsTaunted(npc)or self:IsHypnosed(npc) or self:IsFeared(npc) +end + +function M:IsNightmared(npc) + return npc:HasModifier("modifier_bane_nightmare") or npc:HasModifier("modifier_riki_poison_dart_debuff") +end + +function M:IsTaunted(npc) + return npc:HasModifier("modifier_axe_berserkers_call") or npc:HasModifier("modifier_legion_commander_duel") +end + +function M:IsDuelCaster(npc) + local function IsTaunting(_npc) + local ability = _npc:GetAbilityByName("legion_commander_duel") + return ability and ability:GetCooldownTimeRemaining() + self:GetModifierRemainingDuration(_npc, "modifier_legion_commander_duel") + 1 >= ability:GetCooldown() + end + local npcBot = GetBot() + if npcBot:GetTeam() == npc:GetTeam() then + return IsTaunting(npc) + else + local players = self:Map(self:Range(0, 4), GetTeamMember) + local tauntingPlayer = self:First(players, function(t) + return IsTaunting(t) and t:GetAttackTarget() == npc + end) + return not IsTaunting(tauntingPlayer) + end +end + +function M:IsMuted(npc) + return npc:IsHexed() or self:HasAnyModifier(npc, self.muteModifiers) +end + +function M:IsHypnosed(npc) + return self:HasAnyModifier(npc, self.hypnosisModifiers) +end + +function M:IsFeared(npc) + return self:HasAnyModifier(npc, self.fearModifiers) +end + +M.IsSeverelyDisabled = function(self, npc) + return npc:IsStunned() or npc:IsHexed() or npc:IsRooted() or self:IsFeared(npc) or self:IsHypnosed(npc) + or self:IsNightmared(npc) + or npc:HasModifier("modifier_legion_commander_duel") and not self:IsDuelCaster(npc) or npc:HasModifier("modifier_axe_berserkers_call") + or npc:HasModifier("modifier_shadow_demon_purge_slow") + or npc:HasModifier("modifier_doom_bringer_doom") +end + +M.IsSeverelyDisabledOrSlowed = function(self, npc) + return self:IsSeverelyDisabled(npc) or self:GetMovementSpeedPercent(npc) <= 0.35 +end + +M.HasSeverelyDisableProjectiles = function(self, npc) + local projectiles = self:GetIncomingDodgeWorthProjectiles(npc) + return self:Any(projectiles, function(t) + return self:Contains(self.targetTrackingStunAbilities, t.ability:GetName()) + end) +end + +M.IsOrGoingToBeSeverelyDisabled = function(self, npc) + return self:IsSeverelyDisabled(npc) or self:HasSeverelyDisableProjectiles(npc) +end + +M.EtherealModifiers = { + "modifier_ghost_state", + "modifier_item_ethereal_blade_ethereal", + "modifier_necrolyte_death_seeker", + "modifier_necrolyte_sadist_active", + "modifier_pugna_decrepify", +} +M.IsEthereal = function(self, npc) + return self:HasAnyModifier(npc, self.EtherealModifiers) +end + +function M:NotBlasted(self, npc) + return not npc:HasModifier("modifier_ice_blast") +end + +M.CannotBeTargetted = function(self, npc) + return self:HasAnyModifier(npc, self.CannotBeTargettedModifiers) +end + +M.CanBeTargettedFunction = function(npc) return not M:CanBeTargetted(npc) end + +M.CannotBeAttacked = function(self, npc) + return self:IsEthereal(npc) or self:IsInvulnerable(npc) or self:CannotBeTargetted(npc) +end + +M.CanBeAttackedFunction = function(npc) return not M:CanBeAttacked(npc) end + +M.IsInvulnerable = function(self, npc) + return npc:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) return npc:HasModifier(t) end) +end + +M.MayNotBeSeen = function(self, npc) + if not npc:IsInvisible() or npc:HasModifier("modifier_item_dust") or npc:HasModifier("modifier_bounty_hunter_track") or npc:HasModifier("modifier_slardar_amplify_damage") or npc:HasModifier("modifier_truesight") then + return false + end + if self:HasAnyModifier(npc, self.permanentTrueSightRootModifiers) then + return false + end + local enemies = self:GetNearbyHeroes(npc) + return self:All(enemies, function(t) + if t:HasItem("item_gem") then + return false + end + if t:GetAttackTarget() == npc then + return false + end + if t:IsUsingAbility() then + local ability = t:GetCurrentActiveAbility() + if binlib.Test(ability:GetBehavior(), ABILITY_BEHAVIOR_UNIT_TARGET) and t:IsFacingLocation(npc:GetLocation(), 10) then + return false + end + end + return true + end) and not self:Any(npc:GetNearbyCreeps(1000, true), function(t) + return t:GetUnitName() == "npc_dota_necronomicon_warrior_3" + end) +end + +M.ShouldNotBeAttacked = function(self, npc) + return self:CannotBeAttacked(npc) or self:Any(self.IgnoreDamageModifiers, function(t) return npc:HasModifier(t) end) or self:Any(self.IgnorePhysicalDamageModifiers, function(t) return npc:HasModifier(t) end) +end + +M.PhysicalCanCastFunction = function(npc) + return not M:IsInvulnerable(npc) and not M:ShouldNotBeAttacked(npc) and not npc:IsMagicImmune() +end + +M.IsPhysicalOutputDisabled = function(self, npc) + return npc:IsDisarmed() or npc:IsBlind() and not npc:GetAvailableItem("item_monkey_king_bar") or self:IsEthereal(npc) +end + +M.GetHealthPercent = function(self, npc) + return npc:GetHealth() / npc:GetMaxHealth() +end + +function M:GetPhysicalHealth(t) + return t:GetHealth() * (1+0.06*t:GetArmor()) / (1 - t:GetEvasion()) +end + +function M:GetBuildingPhysicalHealth(t) + local h = self:GetPhysicalHealth(t) + if t:HasModifier("modifier_fountain_glyph") then + h = h + self:GetModifierRemainingDuration("modifier_fountain_glyph") * 200 + end + return h +end + +M.GetManaPercent = function(self, npc) + return npc:GetMana() / npc:GetMaxMana() +end + +M.GetHealthDeficit = function(self, npc) + return npc:GetMaxHealth() - npc:GetHealth() +end + +function M:GetManaDeficit(npc) + return npc:GetMaxMana() - npc:GetMana() +end + +function M:GetTargetIfGood(npc) + local target = npc:GetTarget() + if target~=nil and target:IsHero() and self:MayNotBeIllusion(npc, target) then + return target + end +end + +-- function M:GetAcrossAbilityStatusPerFrame(npc) +-- local g = {} +-- g.health = npc:GetHealth() +-- g.mana = npc:GetMana() +-- g.healthPercent = self:GetHealthPercent(npc) +-- g.manaPercent = self:GetManaPercent(npc) +-- g.attackRange = npc:GetAttackRange() +-- end + +function M:IndexOfBasicDispellablePositiveModifier(npc) + return self:Aggregate(nil, self.basicDispellWorthPositiveModifiers, function(seed, modifier, index) + if seed then + return seed + end + local b = npc:HasModifier(modifier) + if b then + return index + else + return nil + end + end) or -1 +end + +function M:HasBasicDispellablePositiveModifier(npc) + return self:Any(self.basicDispellWorthPositiveModifiers, function(t) return t:HasModifier(t) end) +end + +function M:DontInterruptAlly(npc) + return self:HasAnyModifier(npc, self.positiveForceMovementModifiers) or self:HasAnyModifier(npc, self.timeSensitivePositiveModifiers) or self:IsDuelCaster(npc) +end + +M.MidLaneTowers = { TOWER_MID_1, TOWER_MID_2, TOWER_MID_3 } +M.BotLaneTowers = { TOWER_BOT_1, TOWER_BOT_2, TOWER_BOT_3 } +M.TopLaneTowers = { TOWER_TOP_1, TOWER_TOP_2, TOWER_TOP_3 } + +function M:GetLaningTower(npc) + local team = npc:GetTeam() + local lane = npc:GetAssignedLane() + local function ToTower(t) return GetTower(team, t) end + local function TowerExists(t) return t:GetHealth() > 0 end + if lane == LANE_BOT then + local a = self:Map(self.BotLaneTowers, ToTower) + return self:First(a, TowerExists) + elseif lane == LANE_MID then + return self:First(self:Map(self.MidLaneTowers, ToTower), TowerExists) + elseif lane == LANE_TOP then + return self:First(self:Map(self.TopLaneTowers, ToTower), TowerExists) + end +end + +-- debug functions + +M.DebugTable = function(self, tb) + local msg = "{ " + local DebugRec + DebugRec = function(tc) + for k,v in pairs(tc) do + if type(v) == "number" or type(v) == "string" then + msg = msg..k.." = "..v..", " + elseif type(v) == "boolean" then + msg = msg..k.." = "..tostring(v)..", " + elseif type(v) == "table" then + msg = msg..k.." = ".."{ " + DebugRec(v) + msg = msg.."}, " + end + end + end + DebugRec(tb) + msg = msg.." }" + print(msg) +end + +M.DebugLongTable = function(self, tb) + for k,v in pairs(tb) do + if type(v) == "table" then + print(tostring(k).." = ") + self:DebugTable(v) + else + print(tostring(k).." = "..tostring(v)) + end + end +end + +M.DebugArray = function(self, tb) + for k,v in ipairs(tb) do + if type(v) == "table" then + self:DebugTable(v) + else + print(v) + end + end +end + +M.PrintAbilities = function(self, npcBot) + local abilityNames = "{\n" + for i = 0,23 do + local ability = npcBot:GetAbilityInSlot(i) + if ability ~= nil and ability:GetName() ~= "generic_hidden" then + abilityNames = abilityNames.."\t\""..ability:GetName().."\",\n" + end + end + abilityNames = abilityNames.."}" + print(npcBot:GetUnitName()) + print(abilityNames) +end + +-- ability function + +function M:NormalCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen, mustBeTargettable) + damageType = damageType or DAMAGE_TYPE_MAGICAL + if pierceMagicImmune == nil then + if damageType == DAMAGE_TYPE_MAGICAL then + pierceMagicImmune = false + else + pierceMagicImmune = true + end + end + if isPureDamageWithoutDisable == nil then + isPureDamageWithoutDisable = true + end + if self:IsInvulnerable(target) then + return false + end + if mustBeTargettable == nil then + mustBeTargettable = true + end + if mustBeTargettable and not self:CannotBeTargetted(target) then + return false + end + if not pierceMagicImmune and target:IsMagicImmune() then + return false + end + if targetMustBeSeen and not target:CanBeSeen() then + return false + end + if isPureDamageWithoutDisable and (damageType == DAMAGE_TYPE_PHYSICAL and self:ShouldNotBeAttacked(target) or damageType == DAMAGE_TYPE_MAGICAL and (target:IsMagicImmune() or self:Contains(self.IgnoreMagicalDamageModifiers, function(t) target:HasModifier(t) end))) then + return false + end + return true +end + +M.NormalCanCastFunction = function(target) return M:NormalCanCast(target) end + +function M:SpellCanCast(target, pierceMagicImmune, targetMustBeSeen, mustBeTargettable) + if targetMustBeSeen == nil then + targetMustBeSeen = true + end + if mustBeTargettable == nil then + mustBeTargettable = true + end + if target:IsInvulnerable() then + return false + end + if mustBeTargettable and self:CannotBeTargetted(target) then + return false + end + if not pierceMagicImmune and target:IsMagicImmune() then + return false + end + return true +end + +M.SpellCanCastFunction = function(target) return M:SpellCanCast(target) end + +function M:AllyCanCast(target, pierceMagicImmune) + if pierceMagicImmune == nil then + pierceMagicImmune = true + end + return not target:IsInvulnerable() and not self:CannotBeTargetted(target) +end + +M.AllyCanCastFunction = function(target) return M:AllyCanCast(target) end + +function M:NeutralCanCast(target) + +end + +function M:EnemyAllyCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen) + if self:IsOnSameTeam(target, GetBot()) then + return self:NormalCanCast(target, isPureDamageWithoutDisable, damageType, pierceMagicImmune, targetMustBeSeen) + else + return self:AllyCanCast(target, pierceMagicImmune, targetMustBeSeen) + end +end + +M.SpecialBonusAttributes = "special_bonus_attributes" +M.TalentNamePrefix = "special_bonus_" +M.IncorrectAbilityName = "incorrect_name" + +M.IsTalent = function(self, ability) + if ability == nil then + return false + end + if type(ability) ~= "string" then + ability = ability:GetName() + end + return ability ~= "special_bonus_attributes" and #ability >= #self.TalentNamePrefix and string.sub(ability, 1, #self.TalentNamePrefix) == self.TalentNamePrefix +end + +M.GetAbilities = function(self, npcBot) + local g = NewTable() + for i = 0,25 do + local ability = npcBot:GetAbilityInSlot(i) + if ability ~= nil and ability:GetName() ~= "generic_hidden" then + table.insert(g, ability) + end + end + return g +end + +M.GetAbilityNames = function(self, npcBot) + return self:Map(self:GetAbilities(npcBot), function(t) return t:GetName() end) +end + +M.GetTalents = function(self, npcBot) + return self:Filter(self:GetAbilities(npcBot), function(t) return self:IsTalent(t) end) +end + +M.GetAbilityLevelUpIndex = function(self, npcBot) + return npcBot:GetLevel() - npcBot:GetAbilityPoints() + 1 + npcBot.abilityTable.incorrectAbilityLevelUpNumber +end + +M.FillInAbilities = function(self, npcBot, abilityTable) + local abilities = self:GetAbilityNames(npcBot) + if #abilityTable == 19 then + table.insert(abilityTable, 17, self.SpecialBonusAttributes) + table.insert(abilityTable, 19, self.SpecialBonusAttributes) + table.insert(abilityTable, 21, self.SpecialBonusAttributes) + table.insert(abilityTable, 22, self.SpecialBonusAttributes) + table.insert(abilityTable, 23, self.SpecialBonusAttributes) + table.insert(abilityTable, 24, self.SpecialBonusAttributes) + end + if #abilityTable == 25 then + table.insert(abilityTable, 26, self.SpecialBonusAttributes) + end + for i = 1, 26 do + if abilityTable[i] == "nil" then + abilityTable[i] = self.SpecialBonusAttributes + end + if not self:Contains(abilities, abilityTable[i]) and abilityTable[i] ~= "talent" then + print("Bot script "..npcBot:GetUnitName().." contains incorrect ability name: "..abilityTable[i]) + abilityTable[i] = self.IncorrectAbilityName + end + end + if #abilityTable == 30 then + npcBot.abilityTable = abilityTable + abilityTable.incorrectAbilityLevelUpNumber = self:Count(abilityTable, function(ability, index) + return index < npcBot:GetLevel() - npcBot:GetAbilityPoints() + 1 and (ability == nil or not ability:CanAbilityBeUpgraded() or ability:GetName() == self.IncorrectAbilityName) + end) + return + end + + local talents = self:Map(self:GetTalents(npcBot), function(t) return t:GetName() end) + local levelUpTalents = self:Filter(abilityTable, function(t) return self:IsTalent(t) end) + local g = self:Concat(abilityTable, self:RemoveAll(talents, levelUpTalents)) + g.incorrectAbilityLevelUpNumber = self:Count(g, function(ability, index) + return index < npcBot:GetLevel() - npcBot:GetAbilityPoints() + 1 and (ability == nil or not ability:CanAbilityBeUpgraded() or ability:GetName() == self.IncorrectAbilityName) + end) + npcBot.abilityTable = g +end + +M.ExecuteAbilityLevelUp = function(self, npcBot) + local abilityTable = npcBot.abilityTable + if abilityTable.justLevelUpAbility then + if abilityTable.abilityPoints == npcBot:GetAbilityPoints() then + abilityTable.incorrectAbilityLevelUpNumber = abilityTable.incorrectAbilityLevelUpNumber + 1 + end + abilityTable.justLevelUpAbility = false + end + abilityTable.abilityPoints = npcBot:GetAbilityPoints() + if npcBot:GetAbilityPoints() < 1 + abilityTable.incorrectAbilityLevelUpNumber or GetGameState() ~= GAME_STATE_PRE_GAME and GetGameState() ~= GAME_STATE_GAME_IN_PROGRESS then + return + end + local abilityName = abilityTable[self:GetAbilityLevelUpIndex(npcBot)] + if abilityName == self.IncorrectAbilityName or abilityName == self.SpecialBonusAttributes then + abilityTable.incorrectAbilityLevelUpNumber = abilityTable.incorrectAbilityLevelUpNumber + 1 + end + npcBot:ActionImmediate_LevelAbility(abilityName) + abilityTable.justLevelUpAbility = true +end + +-- geometry + +M.IsVector = function(self, object) + return type(object)=="userdata" and type(object.x)=="number" and type(object.y)=="number" and type(object.z)=="number" +end +M.ToStringVector = function(self, object) + return string.format("(%d,%d,%d)",object.x,object.y,object.z) +end + +M.GetLine = function(self, a, b) + if a.x == b.x then + return { a = 1, b = 0, c = -a.x } + end + local k = (a.y-b.y)/(a.x-b.x) + local bb = a.y-k*a.x + return { a = k, b = -1, c = bb } +end + +M.GetPointToLineDistance = function(self, point, line) + local up = math.abs(line.a*point.x+line.b*point.y+line.c) + local down = math.sqrt(line.a*line.a+line.b*line.b) + return up/down +end + +M.GetPointToPointDistance = function(self, a, b) + return ((a.x-b.x)^2+(a.y-b.y)^2)^0.5 +end + +-- Get the location on the line determined by startPoint and endPoint, with distance from startPoint to the target location +M.GetPointFromLineByDistance = function(self, startPoint, endPoint, distance) + local distanceTo = self:GetPointToPointDistance(startPoint, endPoint) + local divide = (endPoint - startPoint) / distanceTo * distance + return startPoint + divide +end + +M.GetCos = function(self, b, c, a) + return (b^2+c*2-a*2)/2/b/c +end +M.GetLocationToLocationDistance = function(self, a, b) + return ((a.x-b.x)^2+(a.y-b.y)^2)^0.5 +end + +function M:GetDegree(loc1, loc2) + local y = loc2.y-loc1.y + local x = loc2.x-loc1.x + return math.atan2(y, x) * 180 / math.pi +end +-- Find the location to use a aoe at a single target with the least distance the hero needs to walk before casting +--M.FindAOELocationAtSingleTarget = function(self, npcBot, target, radius, castRange, castPoint) +-- if self:CanMove(target) then +-- radius = radius * 0.8 +-- end +-- local g +-- GeneratePath(npcBot:GetLocation(), target:GetLocation(), {}, function(distance, waypoints) +-- if waypoints == 0 then +-- waypoints = { npcBot:GetLocation(), target:GetLocation() } +-- end +-- for i = 1, #waypoints-1 do +-- local waypoint1 = waypoints[i] +-- local waypoint2 = waypoints[i+1] +-- local dis1 = GetUnitToLocationDistance(target, waypoint1) +-- local dis2 = GetUnitToLocationDistance(target, waypoint2) +-- if dis1 > dis2 then +-- if radius >= dis1 then +-- g = waypoint1 +-- return +-- elseif radius >= dis2 then +-- local waypointDis = self:GetLocationToLocationDistance(waypoint1, waypoint2) +-- local cosine = self:GetCos(dis2, waypointDis, dis1) +-- local walkDis = dis1*cosine - (dis1^2-radius*2)^0.5 +-- local targetLocation = self:GetPointFromLineByDistance(waypoint1, waypoint2, walkDis) +-- g = targetLocation +-- return +-- end +-- else +-- if radius >= dis1 then +-- g = waypoint1 +-- return +-- end +-- end +-- end +-- g = waypoints[#waypoints] +-- end) +-- return g +--end +M.FindAOELocationAtSingleTarget = function(self, npcBot, target, radius, castRange, castPoint) + radius = radius - 80 + local distance = GetUnitToUnitDistance(npcBot, target) + if distance < radius + castRange then + return self:GetPointFromLineByDistance(npcBot:GetLocation(), target:GetLocation(), castRange) + else + return self:GetPointFromLineByDistance(target:GetLocation(), npcBot:GetLocation(), radius) + end +end + +M.MinValue = function(self, coefficients, min, max) + max = max or 10000 + min = min or -10000 + local function Differential(coefficients) + local g = {} + for index, coefficient in pairs(coefficients) do + g[index-1] = coefficient*index + end + return g + end + local function Y(coefficients, x) + local g = 0 + for index, coefficient in pairs(coefficients) do + g = g + coefficient * x ^ index + end + return g + end + + local differential = Differential(coefficients) + if differential[1] ~= nil and differential[1] ~= 0 then + local zeroPoint = -differential[0]/differential[1] + if Y(coefficients, zeroPoint+0.1)>0 then + if zeroPoint > max then + return Y(coefficients, max) + elseif zeroPoint < min then + return Y(coefficients, min) + else + return Y(coefficients, zeroPoint) + end + else + if zeroPoint > max then + return Y(coefficients, min) + elseif zeroPoint < min then + return Y(coefficients, max) + else + local val1 = Y(coefficients, min) + local val2 = Y(coefficients, min) + return math.min(val1, val2) + end + end + else + return Y(coefficients, min) + end +end + +M.MaxValue = function(self, coefficients, min, max) + local g = {} + for index, coefficient in coefficients do + g[index] = -coefficient + end + return self:MinValue(g, -max, -min) +end + +M.GetCollapseInfo = function(self, obj1, obj2, timeLimit) + local x1 = obj1.location.x-obj2.location.x + local x2 = obj1.velocity.x-obj2.velocity.x + local coefficient0 = x1^2 + local coefficient1 = 2*x1*x2 + local coefficient3 = x2^2 + x1 = obj1.location.y-obj2.location.y + x2 = obj2.velocity.y-obj2.velocity.y + coefficient0 = coefficient0 +end + +M.PreventHealAtHealSuppressTarget = function(self, npcBot, oldConsiderFunction, ability) + return function() + local desire, target, targetTypeString = oldConsiderFunction() + if desire == 0 or target == nil or target == 0 or targetTypeString == "Location" then + return desire, target, targetTypeString + end + if npcBot:GetTeam() == target:GetTeam() then + desire = desire * self:GetTargetHealAmplifyPercent(target) + end + desire = self:TrimDesire(desire) + return desire, target, targetTypeString + end +end + +M.PURCHASE_ITEM_OUT_OF_STOCK=82 +M.PURCHASE_ITEM_INVALID_ITEM_NAME=33 +M.PURCHASE_ITEM_DISALLOWED_ITEM=78 +M.PURCHASE_ITEM_INSUFFICIENT_GOLD=63 +M.PURCHASE_ITEM_NOT_AT_SECRET_SHOP=62 +M.PURCHASE_ITEM_NOT_AT_HOME_SHOP=67 +M.PURCHASE_ITEM_SUCCESS=-1 + +-- specified ability is not actually an ability (2) +-- invalid order(3) unrecognised order name +-- invalid order(40) order not allowed for illusions +-- unit is dead (20) +-- target tree is not active (43) +-- ability is still in cooldown (15) +-- cannot cast ability on tree (34) +-- cannot cast ability on target +-- target is unselectable +-- order requires a physical item target, but specified target is not a physical item (9) +-- item cannot be used from stash (37) +-- does not have enough mana to cast ability (14) +-- item is still in cooldown (61) +-- can't cast attack ability on target, target is attack immune (32) +-- order invalid for units with attack ability DOTA_UNIT_CAP_NO_ATTACK (41) +-- can't cast on target, ability cannot target enemies (30) +-- can't cast on target, ability cannot target creeps (56) +-- unit can't perform command, unit has commands restricted (74) +-- hero does not have enough ability points to upgrade ability (13) +-- ability is hidden (60) +-- can't cast on target, ability cannot target teammates (29) +-- cannot attack or cast on target, target is unselectable (36) +-- unit cannot cast, unit is silenced (24) +-- cannot cast ability on NPC (19), unit is dead +-- unit cannot manipulate items (39) +-- target cannot be seen by the unit's team (26) + + +-- runtime errors: +--- Bad key for entity "npc_dota_creep_neutral": Out of range parsed value for field "teamnumber" (-1)! + + +M.IgnoreDamageModifiers = { + "modifier_abaddon_borrowed_time", + "modifier_item_aeon_disk_buff", + "modifier_winter_wyvern_winters_curse", + "modifier_winter_wyvern_winters_curse_aura", + "modifier_skeleton_king_reincarnation_scepter_active", +} + +M.CannotKillModifiers = { + "modifier_dazzle_shadow_grave", + "modifier_troll_warlord_battle_trance", +} + +M.CannotBeTargettedModifiers = { + "modifier_slark_shadow_dance", + "modifier_item_book_of_shadows", + "modifier_dark_willow_shadow_realm_buff", +} + +M.IgnorePhysicalDamageModifiers = { + "modifier_winter_wyvern_cold_embrace", +} +M.IgnoreMagicalDamageModifiers = { + "modifier_oracle_fates_edict", +} + +M.LastForAtLeastSeconds = function(self, predicate, time, infoTable) + if infoTable.lastTrueTime == nil then + infoTable.lastTrueTime = DotaTime() + end + if predicate() then + if DotaTime() - infoTable.lastTrueTime >= time then + return true + else + return false + end + else + infoTable.lastTrueTime = nil + return false + end +end + +M.GoodIllusionHero = { + "antimage","spectre","terrorblade","naga_siren", +} +M.ModerateIllusionHero = { + "abaddon","axe","chaos_knight","arc_warden","juggernaut","luna","medusa","morphling","phantom_lancer","sniper","wraith_king","phantom_assassin", +} +M.GetIllusionBattlePower = function(self, npc) + local name = self:GetHeroShortName(npc:GetUnitName()) + if npc:HasModifier("modifier_arc_warden_tempest_double") or npc:HasModifier("modifier_skeleton_king_reincarnation_active") then + return 0.8 + end + if npc:HasModifier("modifier_vengefulspirit_hybrid_special") then + return 1.05 + end + local t = 0.1 + if self:Contains(self.GoodIllusionHero, name) then + t = 0.25 + elseif self:Contains(self.ModerateIllusionHero, name) then + t = 0.4 + elseif t:IsRanged() then + t = t + t:GetAttackRange() / 600 + end + local inventory = self:Map(self:GetInventoryItems(npc), function(t) return t:GetName() end) + if self:Contains(inventory, "item_radiance") then + t = t+0.07 + end + if self:Contains(inventory, "item_diffusal_blade") then + t = t+0.05 + end + if self:Contains(inventory, "item_lesser_crit") then + t = t+0.04 + end + if self:Contains(inventory, "item_greater_crit") then + t = t+0.08 + end + if npc:HasModifier("modifier_special_bonus_mana_break") then-- mirana talent[5] + t = t+0.04 + end + return t +end + +M.GetNetWorth = function(self, npc, isEnemy) + if isEnemy then + local itemCost = self:Map(self:GetInventoryItems(npc), function(t) return GetItemCost(t:GetName()) end) + return self:Aggregate(0, itemCost, function(a, b) return a+b end) + else + return npc:GetNetWorth() + end +end + +function M:GetBattlePower(npc) + local power = 0 + local name = npc:GetUnitName() + if string.match(name, "npc_dota_hero") then + power = npc:GetNetWorth() + npc:GetLevel() * 1000 + if npc:GetLevel() >= 25 then + power = power + 1000 + end + if npc:GetLevel() >= 30 then + power = power + 1000 + end + elseif string.match(name, "npc_dota_lone_druid_bear") then + local heroLevel = GetHeroLevel(npc:GetPlayerID()) + power = name[#"npc_dota_lone_druid_bear"+1]*2000-1000 + power = power + heroLevel * 250 + power = power + npc:GetNetWorth() + end + if npc:HasModifier("modifier_item_assault_positive") and not npc:HasModifier("modifier_item_assault_positive_aura") then + power = power + 1500 + end + local items = self:GetInventoryItemsNames(npc) + if npc:HasModifier("modifier_item_pipe_aura") and not self:Contains(items, "item_pipe") then + power = power + 400 + end + if npc:HasModifier("modifier_item_vladmir_aura") and not self:Contains(items, "item_vladmir") then + power = power + 300 + end + if npc:HasModifier("modifier_item_guardian_greaves_aura") and not self:Contains(items, "item_guardian_greaves") then + power = power + 1000 + elseif npc:HasModifier("modifier_item_mekansm_aura") and not self:Contains(items, "item_mekansm") then + power = power + 500 + end + return power +end + +M.GetHeroGroupBattlePower = function(self, npcBot, heroes, isEnemy) + local function A(tb) + local battlePowerMap = self:Map(tb, function(t) return { t:GetUnitName(), self:GetBattlePower(t) } end) + battlePowerMap = self:SortByMaxFirst(battlePowerMap, function(t) return t[2] end) + battlePowerMap = self:Map(battlePowerMap, function(t, index) return t[2] * (1.15-0.15*index) end) + local g = NewTable() + for _, v in ipairs(battlePowerMap) do + g[v[1]] = v[2] + end + return g + end + local enemyNetWorthMap = A(self:GetEnemyHeroUnique(npcBot, heroes)) + local netWorth = 0 + local readNames = NewTable() + for _, enemy in pairs(heroes) do + local name = enemy:GetUnitName() + if not self:Contains(readNames, name) then + table.insert(readNames, name) + -- TODO: enemyNetWorthMap[name] should not be null + if enemyNetWorthMap[name] then + netWorth = netWorth + enemyNetWorthMap[name] + end + else + if enemyNetWorthMap[name] then + netWorth = netWorth + enemyNetWorthMap[name] * self:GetIllusionBattlePower(enemy) + end + end + end + return netWorth +end + +M.Outnumber = function(self, npcBot, friends, enemies) + return self:GetHeroGroupBattlePower(npcBot, friends, false) >= self:GetHeroGroupBattlePower(npcBot, enemies, true) * 1.8 +end + + +M.CannotBeKilledNormally = function(self, target) + return target:IsInvulnerable() or self:Any(self.IgnoreDamageModifiers, function(t) target:HasModifier(t) end) or target:HasModifier("modifier_dazzle_shallow_grave") +end + +M.HasScepter = function(self, npc) + return npc:HasScepter() or npc:HasModifier("modifier_wisp_tether_scepter") or npc:HasModifier("modifier_item_ultimate_scepter") or npc:HasModifier("modifier_item_ultimate_scepter_consumed_alchemist") +end + +-- ability record + +local locationAOEAbilities = { + cone = { + "lina_dragon_slave", + }, + circle = { + "lina_light_strike_array", + }, + isoscelesTrapezoid = { + "kunkka_tidebringer", + }, +} + +function M:RecordAbility(npc, index, target, castType, abilities) + local abilityRecords = npc.abilityRecords + if index ~= nil then + abilityRecords[index] = {} + if castType == "Location" then + abilityRecords[index].location = target + elseif castType == "Target" then + abilityRecords[index].target = target + elseif castType == "Tree" then + abilityRecords[index].targetTree = target + elseif self:IsVector(target) then + abilityRecords[index].location = target + elseif target ~= nil then + abilityRecords[index].target = target + end + abilityRecords.usingAbilityIndex = index + abilityRecords[index].beginCastTime = DotaTime() + return + end + if not npc:IsUsingAbility() and not npc:IsChanneling() then + if abilityRecords.usingAbilityIndex ~= nil and not abilities[abilityRecords.usingAbilityIndex]:IsCooldownReady() then + abilityRecords.lastUsedAbilityIndex = abilityRecords.usingAbilityIndex + abilityRecords.usingAbilityIndex = nil + abilityRecords.lastUsedAbilityTime = DotaTime() + end + end +end + +local frameNumber = 0 +local dotaTimer +local function FloatEqual(a, b) + return math.abs(a-b)<0.000001 +end + +-- tick + +function M:GetFrameNumber() + return frameNumber +end + +function M:EveryManyFrames(count, times) + times = times or 1 + return frameNumber % count < times +end + +local defaultReturn = NewTable() +local everySecondsCallRegistry = NewTable() + +function M:EveryManySeconds(second, oldFunction) + local functionName = tostring(oldFunction) + everySecondsCallRegistry[functionName.."lastCallTime"] = RandomFloat(0, second) + return function(...) + if everySecondsCallRegistry[functionName.."lastCallTime"] <= DotaTime() - second then + everySecondsCallRegistry[functionName.."lastCallTime"] = DotaTime() + return oldFunction(...) + else + return defaultReturn + end + end +end + +local singleForTeamRegistry = NewTable() + +function M:SingleForTeam(oldFunction) + local functionName = tostring(oldFunction)..GetTeam() + return function(...) + if singleForTeamRegistry[functionName] ~= frameNumber then + singleForTeamRegistry[functionName] = frameNumber + return oldFunction(...) + else + return defaultReturn + end + end +end + +local singleForAllBots = NewTable() + +function M:SingleForAllBots(oldFunction) + local functionName = tostring(oldFunction) + return function(...) + if singleForAllBots[functionName] ~= frameNumber then + singleForAllBots[functionName] = frameNumber + return oldFunction(...) + else + return defaultReturn + end + end +end + +local groupAnnounceTimes1 = 0 +function M:AnnounceGroups1(npcBot) + if groupAnnounceTimes1 == 0 then + npcBot:ActionImmediate_Chat("Thanks for choosing RMM AI. Join our new discord group at https://discord.gg/Agd632pvhA to put suggestions or devloping issues!", true) + groupAnnounceTimes1 = 1 + end +end +local groupAnnounceTimes2 = 0 +function M:AnnounceGroups2(npcBot) + if groupAnnounceTimes2 == 0 then + npcBot:ActionImmediate_Chat("Or join QQ group at 946823144!", true) + groupAnnounceTimes2 = 1 + end +end + +function M:CalledOnThisFrame(functionInvocationResult) + return functionInvocationResult ~= defaultReturn +end + +local slowFunctionRegistries = NewTable() +local coroutineRegistry = NewTable() +local coroutineExempt = NewTable() + +function M:TickFromDota() + local time = DotaTime() + local function ResumeCoroutine(thread) + local coroutineResult = { coroutine.resume(thread[1], time - dotaTimer) } + if not coroutineResult[1] then + error(coroutineResult[2]) + end + end + if dotaTimer == nil then + dotaTimer = time + return + end + if not FloatEqual(time, dotaTimer) then + frameNumber = frameNumber + 1 + self:ForEach(slowFunctionRegistries, function(t) t(time - dotaTimer) end) + local threadIndex = 1 + while threadIndex <= #coroutineRegistry do + local t = coroutineRegistry[threadIndex] + local exemptIndex + local exempt + self:ForEach(coroutineExempt, function(exemptPair, index) + if exemptPair[1] == t then + if exemptPair[2] == frameNumber then + exempt = true + end + exemptIndex = index + end + end) + if exemptIndex then + table.remove(coroutineExempt, exemptIndex) + end + if not exempt then + if coroutine.status(t) == "suspended" then + ResumeCoroutine(t) + threadIndex = threadIndex + 1 + elseif coroutine.status(t) == "dead" then + table.remove(coroutineRegistry, threadIndex) + else + threadIndex = threadIndex + 1 + end + end + end + dotaTimer = time + end +end + +function M:RegisterSlowFunction(oldFunction, calledWhenHowManyFrames, frameOffset, defaultReturn) + return function(...) + if frameNumber % calledWhenHowManyFrames == frameOffset then + return oldFunction(...) + else + return self:UnpackIfTable(defaultReturn) + end + end +end + +-- coroutine + +function M:ResumeUntilReturn(func) + local g = NewTable() + local thread = coroutine.create(func) + while true do + local values = { coroutine.resume(thread) } + if values[1] then + table.remove(values, 1) + table.insert(g, values) + else + error(values[2]) + break + end + end + return g +end + +function M:StartCoroutine(func) + local newCoroutine = coroutine.create(func) + table.insert(coroutineRegistry, newCoroutine) + table.insert(coroutineExempt, {newCoroutine, frameNumber}) + return newCoroutine +end + +function M:WaitForSeconds(seconds) + local function WaitFor(firstFrameTime) + local t = seconds - firstFrameTime + while t > 0 do + t = t - coroutine.yield() + end + end + return self:StartCoroutine(WaitFor) +end + +function M:StopCoroutine(thread) + self:Remove_Modify(coroutineExempt, function(t) return t[1] == thread end) + self:Remove_Modify(coroutineRegistry, thread) +end + +-- get data from ability +-- for example, to get value aoe_radius, use ability.aoe_radius rather than ability:GetSpecialValueInt("aoe_radius") + +local function GetDataFromAbility(ability, valueName) + local a = ability:GetSpecialValueInt(valueName) + return a==0 and ability:GetSpecialValueFloat(valueName) or a +end + +local function Append__Index(tb, __index) + local m = getmetatable(tb) + if m == nil then + m = {} + setmetatable(tb, m) + end + local oldIndex = m.__index + if oldIndex == nil then + m.__index = __index + elseif type(oldIndex) == "function" then + m.__index = function(ability, i) + local oldResult = { oldIndex(ability, i) } + if oldResult[1] == nil then + return __index(ability, i) + else + return M.Unpack(oldResult) + end + end + elseif type(oldIndex) == "table" then + if oldIndex == m then + m.__index = function(g, h) + local newResult = { __index(g, h) } + if newResult[1] == nil then + return oldIndex[h] + else + return M.Unpack(newResult) + end + end + else + Append__Index(oldIndex, __index) + end + end +end +Append__Index(CDOTABaseAbility_BotScript, GetDataFromAbility) + +function M:pcall(func, ...) + local result = { func(...) } + if result[1] then + table.remove(result, 1) + return self:Unpack(result) + else + error(result[2]) + DebugPause() + end +end + +-- M.debug = true +function M:DebugPause() + if self.debug then + DebugPause() + end +end + +return M diff --git a/util/ItemPurchaseSystem.lua b/util/ItemPurchaseSystem.lua index b91ecd9d..bf073743 100644 --- a/util/ItemPurchaseSystem.lua +++ b/util/ItemPurchaseSystem.lua @@ -342,7 +342,6 @@ function M.WeNeedTpscroll() npcBot:ActionImmediate_PurchaseItem( "item_tpscroll" ); elseif ( DotaTime() >= 20 * 60 ) then npcBot:ActionImmediate_PurchaseItem( "item_tpscroll" ); - npcBot:ActionImmediate_PurchaseItem( "item_tpscroll" ); end else npcBot:ActionImmediate_PurchaseItem( "item_tpscroll" ); @@ -658,13 +657,6 @@ M.IsConsumableItem = function(self, item) return AbilityExtensions:Contains(self.Consumables, string.sub(item, 6)) end - -local function HasScepter(npc) - return npc:HasScepter() or npc:HasModifier("modifier_item_ultimate_scepter") -end - - - M.CreateItemInformationTable = function(self, npcBot, itemTable) local function ExpandFirstLevel(item) if isLeaf(item) then diff --git a/util/ItemUsageSystem.lua b/util/ItemUsageSystem.lua index 5a35b4a1..df8e2de4 100644 --- a/util/ItemUsageSystem.lua +++ b/util/ItemUsageSystem.lua @@ -327,7 +327,6 @@ function M.UnImplementedItemUsage() return end if GetWantedPowerTreadsAttribute() ~= treads:GetPowerTreadsStat() then - print(npcBot:GetUnitName()..": power treads attribute "..treads:GetPowerTreadsStat()..", but I want "..GetWantedPowerTreadsAttribute()) npcBot:Action_UseAbility(treads) return true end @@ -601,7 +600,7 @@ function M.UnImplementedItemUsage() else if npcBot:GetHealth() <= 250 and not npcBot:WasRecentlyDamagedByAnyHero(0.8) then npcBot:Action_UseAbility(itemArmlet) - itemArmlet.lastOpenTime = DotaTime() + itemArmlet.lastOpenTime = nil end end else @@ -613,22 +612,27 @@ function M.UnImplementedItemUsage() end elseif AbilityExtensions:IsAttackingEnemies(npcBot) or AbilityExtensions:IsRetreating(npcBot) then if not IsUsingArmlet then - if #npcBot:GetNearbyHeroes(1599, true, BOT_MODE_NONE) > 0 or npcBot:WasRecentlyDamagedByAnyHero(2.5) then + if #npcBot:GetNearbyHeroes(1000, true, BOT_MODE_NONE) > 0 or npcBot:WasRecentlyDamagedByAnyHero(1.5) then npcBot:Action_UseAbility(itemArmlet) itemArmlet.lastOpenTime = DotaTime() return end else if npcBot:GetHealth() <= 300 then - local projectiles = npcBot:GetIncomingTrackingProjectiles() - if (#projectiles == 0 or AbilityExtensions:CannotBeKilledNormally(npcBot)) and DotaTime() - itemArmlet.lastOpenTime >= 0.6 then + local projectiles = AbilityExtensions:FilterNot(npcBot:GetIncomingTrackingProjectiles(), function(t) return AbilityExtensions:IsOnSameTeam(t, npcBot) end) + if (#projectiles == 0 or AbilityExtensions:CannotBeKilledNormally(npcBot)) and DotaTime() - itemArmlet.lastOpenTime >= 1 then npcBot:Action_UseAbility(itemArmlet) - npcBot:ActionQueue_UseAbility(itemArmlet) itemArmlet.lastOpenTime = DotaTime() return end end end + else + if IsUsingArmlet then + npcBot:Action_UseAbility(itemArmlet) + itemArmlet.lastOpenTime = nil + return + end end end]] @@ -838,12 +842,11 @@ function M.UnImplementedItemUsage() end local function NotSuitableForGuardianGreaves(t) - return not t:HasModifier("modifier_ice_blast") and not t:HasModifier("modifier_item_mekansm_noheal") and not t:HasModifier("modifier_item:guardian_greaves_noheal") + return AbilityExtensions:AllyCanCast(t) and not t:HasModifier("modifier_ice_blast") and not t:HasModifier("modifier_item_mekansm_noheal") and not t:HasModifier("modifier_item_guardian_greaves_noheal") end local guardian = IsItemAvailable("item_guardian_greaves") if guardian ~= nil and guardian:IsFullyCastable() then - local allys = npcBot:GetNearbyHeroes(1000, false, BOT_MODE_NONE) - allys = AbilityExtensions:Filter(allys, NotSuitableForGuardianGreaves) + local allys = AbilityExtensions:GetNearbyNonIllusionHeroes(900, false):Filter(allys, NotSuitableForGuardianGreaves) for _, ally in pairs(allys) do if ally:GetHealth() / ally:GetMaxHealth() < 0.35 and tableNearbyEnemyHeroes ~= nil and #tableNearbyEnemyHeroes > 0 then npcBot:Action_UseAbility(guardian)