require("libs.global_stuff")
require("libs.cast")

--// GLOBALS ==--
local floor = math.floor local atan2 = math.atan2 local ceil = math.ceil
local cos = math.cos local sin = math.sin local pi = math.pi
local screenSize = dota.GetScreenSize()
local rate = screenSize.x/1600
local rateY = screenSize.y/900
local Enum = GetEnums()
local utils = {}

utils.auto_attack = {}
utils.immunity = {}

utils.immunity.phys = {
	"modifier_omninight_guardian_angel",
	"modifier_winter_wyvern_cold_embrace",
	"modifier_item_aeon_disk_buff"
}

utils.stuns = { --remove
	modifier_stunned = true,
	modifier_bashed = true,
	modifier_batrider_flaming_lasso = true,
	modifier_bane_fiends_grip = true,
	modifier_pudge_dismember = true,
	modifier_shadow_shaman_shackles = true,
	modifier_beastmaster_primal_roar = true,
	modifier_nyx_assassin_impale  = true,
	modifier_sandking_impale = true, 
	modifier_storm_spirit_electric_vortex_pull = true,
	modifier_tidehunter_ravage = true,
	modifier_jakiro_ice_path_stun = true,
	modifier_medusa_stone_gaze_stone = true,
	modifier_elder_titan_echo_stomp = true,
	modifier_lion_impale = true,
	modifier_hoodwink_bushwhack_trap = true,
	modifier_dawnbreaker_fire_wreath_smash_stun = true,
	modifier_winter_wyvern_cold_embrace = true,
}

utils.stuns_fake = { --remove
	modifier_dark_willow_cursed_crown = true,
	modifier_item_meteor_hammer_burn = true,
	modifier_item_fallen_sky_burn = true,
	modifier_tusk_snowball_visible = true,
	modifier_tusk_snowball_target = true,
	modifier_kunkka_torrent_slow = true,
	modifier_special_mars_spear_burning_trail_burn = true,
}

utils.bkb = { 
	modifier_black_king_bar_immune = true,
	modifier_life_stealer_rage = true,
	modifier_juggernaut_blade_fury = true,
	modifier_legion_commander_press_the_attack_immunity = true,
	modifier_elder_titan_echo_stomp_magic_immune = true,
	
	modifier_pangolier_gyroshell = true,
	-- modifier_lion_mana_drain_immunity = true,
	modifier_pangolier_rollup = true,
	modifier_huskar_life_break_charge = true,
}

utils.disabled = { --remove
	modifier_bane_nightmare = true, 		-- 	Enum.MODIFIER_STATE_NIGHTMARED
	modifier_axe_berserkers_call = true, 	-- 	Enum.MODIFIER_STATE_TAUNTED
	modifier_lich_sinister_gaze = true, 	-- 	Enum.MODIFIER_STATE_FEARED
	modifier_legion_commander_duel = true, 	-- 	Enum.MODIFIER_STATE_TAUNTED
}

utils.ghost = {
	"modifier_pugna_decrepify",
	"modifier_leshrac_decrepify",
	"modifier_ghost_state",
	"modifier_item_ethereal_blade_ethereal",
	"modifier_necrolyte_ghost_shroud_active",
	"modifier_muerta_pierce_the_veil_buff",
}

utils.silence_broken = {
	"modifier_riki_smoke_screen",
	"modifier_disruptor_static_storm",
	"modifier_night_stalker_crippling_fear",
	"modifier_item_mask_of_madness_berserk",
	"modifier_item_unstable_wand_critter",
}

utils.hex = { --remove
	modifier_shadow_shaman_voodoo = true,
	modifier_lion_voodoo = true,
	modifier_sheepstick_debuff = true,
	modifier_item_princes_knife_hex = true,
	modifier_hexxed = true,
	modifier_item_unstable_wand_critter = true,
}

utils.invule = { --remove
	modifier_eul_cyclone = true,
	modifier_wind_waker = true,
	modifier_invoker_tornado = true,
	modifier_brewmaster_storm_cyclone = true,
	modifier_shadow_demon_disruption = true,
	modifier_obsidian_destroyer_astral_imprisonment_prison = true,
	modifier_puck_phase_shift = true,
	modifier_riki_tricks_of_the_trade_phase = true,
	modifier_witch_doctor_voodoo_switcheroo = true,
	modifier_meepo_petrify = true,
	modifier_naga_siren_song_of_the_siren = 7,	
	modifier_bane_nightmare_invulnerable = 1, -- 1.1
	modifier_chaos_knight_phantasm = 1,
	modifier_naga_siren_mirror_image = 1,
	modifier_earthspirit_petrify = 1,
	modifier_manta_phase = 1,
	modifier_phantom_lancer_doppelwalk_phase = 1, -- 1.1
	modifier_void_spirit_dissimilate_phase = 1, -- 1.1
}

utils.roots = { --remove
	modifier_crystal_maiden_frostbite = true,
	modifier_naga_siren_ensnare = true,
	modifier_dark_troll_warlord_ensnare = true,
	modifier_dark_willow_bramble_maze = true,
	modifier_ember_spirit_searing_chains = true,
	modifier_meepo_earthbind = true,
	modifier_oracle_fortunes_end_purge = true,
	modifier_rod_of_atos_debuff = true,
	modifier_lone_druid_spirit_bear_entangle_effect = true,
	modifier_techies_stasis_trap_stunned = true,
	modifier_treant_natures_guise_root = true,
	modifier_treant_overgrowth = true,
	modifier_abyssal_underlord_pit_of_malice_ensare = true,
	modifier_clumsy_net_ensnare = true,
	modifier_troll_warlord_berserkers_rage_ensnare = true,
	modifier_broodmother_silken_bola = true,
	modifier_gungnir_debuff = true,
	modifier_tinker_warp_grenade = true,
}

utils.status = {
	modifier_item_aeon_disk_buff = true,
	modifier_item_titan_sliver = true,
	modifier_item_enhancement_titanic = true,
	modifier_item_sange_and_yasha = true,
	modifier_ceremonial_robe_aura = true,
	modifier_item_ascetic_cap_buff = true,

	modifier_spirit_breaker_bulldoze = true,
	modifier_ursa_enrage = true,
	modifier_troll_warlord_rampage = true,
	modifier_tinker_defense_matrix = true,
	modifier_naga_siren_deluge_status_resistance = true,

	modifier_life_stealer_unfettered = 1,
	modifier_slardar_seaborn_sentinel_river = 1,
	modifier_brewmaster_liquid_courage_passive = 1,
	modifier_morphling_scepter_stats_drain_strength_buff = 1,
	modifier_night_stalker_hunter_in_the_night = 1,
	modifier_item_unrelenting_eye = 1,

	--debuff
	-- modifier_alchemist_corrosive_weaponry_debuff = 1,
	modifier_morphling_scepter_stats_drain_strength_debuff = 1,
}

utils.invis = {
	modifier_bounty_hunter_wind_walk = true,
	-- modifier_mirana_moonlight_shadow = true, 
	modifier_invisible = true,
	modifier_weaver_shukuchi = true,
	modifier_clinkz_wind_walk = true,
	modifier_treant_natures_guise_invis = true,
	modifier_item_glimmer_cape_fade = true,
	modifier_item_shadow_amulet_fade = true,
	modifier_rune_invis = true,
	modifier_windrunner_windrun_invis = true,
	modifier_oracle_false_promise_invis = true,
	modifier_item_trickster_cloak_invis = true,
	modifier_phantom_lancer_juxtapose_invisibility = true,
	modifier_kez_ravens_veil_buff = true,

	--чтобы не юзались мана боты в инвизе
	modifier_nyx_assassin_vendetta = true, 
	modifier_invoker_ghost_walk_self = true,
	modifier_templar_assassin_meld = true, 

	modifier_item_invisibility_edge_windwalk = 1,
	modifier_item_silver_edge_windwalk = 1,
}

utils.cast_point = { 
	modifier_item_yasha_and_kaya = {value="cast_speed_pct", defoult = 0},
	modifier_faceless_void_time_zone_effect = {value="bonus_cast_speed", defoult = 0},
	modifier_item_giant_maul_debuff = {value="cast_slow", defoult = 0},
	-- modifier_medusa_venomed_volley_slow = {value="cast_slow", defoult = 0},
	modifier_nevermore_frenzy = {value="cast_speed_pct", defoult = 1} -- бонусом а не процент
}

utils.max_movespeed = {
	"modifier_phantom_lancer_phantom_edge_boost",
	"modifier_centaur_stampede",
	"modifier_lycan_shapeshift_speed",
	"modifier_faceless_void_chronosphere_speed",
	"modifier_rune_haste",
}

utils.force_stuff = {
	"modifier_item_forcestaff_active",
	"modifier_item_hurricane_pike_active",
	"modifier_force_boots_active",
	"modifier_mirana_leap",
	"modifier_magnataur_skewer_movement",
	"modifier_slark_pounce",
	"modifier_phoenix_icarus_dive",
	"modifier_dawnbreaker_celestial_hammer_movement",
	"modifier_shredder_timber_chain",
	"modifier_zuus_heavenly_jump",
	"modifier_marci_lunge_tracking_motion",
	"modifier_marci_lunge_arc",
	"modifier_faceless_void_time_walk",
	"modifier_enchantress_bunny_hop",
	"modifier_pudge_meat_hook",
	"modifier_tusk_drinking_buddies_pull",
	"modifier_kez_grappling_claw_movement",
	"modifier_largo_catchy_lick_knockback",
	--modifier_pangolier_swashbuckle_stunned",
}

utils.pierce_linkin = {
	item_solar_crest  			= true,
	item_medallion_of_courage 	= true,
	item_spirit_vessel  		= true, 
	item_heavy_blade 			= true,
	item_urn_of_shadows  		= true,
	item_bullwhip  				= true,
	item_psychic_headband  		= true,
	item_book_of_shadows  		= true,
	item_force_boots  			= true,
	item_crippling_crossbow  	= true,
	item_essence_distiller  	= true,
}

--добавить в Базовое комбо HaveModifierList(self,{"modifier_lycan_shapeshift","modifier_dragon_knight_dragon_form","modifier_muerta_gunslinger"
utils.fix_is_attacking = {
	npc_dota_hero_razor = 			{mod = "modifier_razor_static_link"},
	npc_dota_hero_dragon_knight = 	{mod = "modifier_dragon_knight_dragon_form"},
	npc_dota_hero_lycan = 			{mod = "modifier_lycan_shapeshift"},
	npc_dota_hero_viper = 			{act = Enum.ACT_DOTA_CAST_ABILITY_1},
	npc_dota_hero_muerta = 			{act = Enum.ACT_DOTA_CAST_ABILITY_3},
}


function CanInput()
	return not dota.IsChat() and not dota.IsShopSearch() and not dota.IsConOpen()
end 

--// Get ability
function FindAbility(self,ability_name)
	if IsItemName(ability_name) then
		return FindItem(self,ability_name)
	else 
		return FindSpellMax(self,ability_name)
	end 
end

function FindSpell(self,spellName) --FindSpellMax
	for i = 1, Enum.SLOT_SPELL_NORMAL do
		local ability = self:GetAbility(i)
		if ability then 
			if ability:GetName()==spellName then
				return ability
			end
		elseif i > 5 then
			break
		end 
	end
	return false
end

function FindReadySpell(self,itemName)
	local spll = FindSpell(self,itemName)
	if spll and CanBeCasted(spll) then
		return spll
	end
	return nil
end

function FindAnyReadySpell(self, max_slots)
	local mx_slots = max_slots or Enum.SLOT_SPELL_VISIBLE
	for z=1, mx_slots do
		local sp = self:GetAbility(z)
		if sp and not sp:IsHidden() and sp:CanBeExecuted() then
			return true
		end 
	end 
end


function FindReadyItem(self,itemName)
	local item = FindItem(self,itemName)
	if item and CanBeCasted(item) then
		return item
	end
	return nil
end

function GetAllItems(self,add_neutral)
	local inventory = self:GetInventory()
	local tab = {}
	for x = 0, Enum.SLOT_ITEM_NORMAL do		
		local item = inventory:GetItemInSlot(x)
		if item then 
			tab[#tab+1] = item
		end 
	end
	if add_neutral then 
		local item = inventory:GetItemInSlot(Enum.SLOT_NEUTRAL)
		if item then 
			tab[#tab+1] = item
		end 
	end 
	return tab
end 

function GetAllVisibleSpells(self)
	local tab = {}
	for i = 1, Enum.SLOT_SPELL_VISIBLE do 
		tab[#tab+1] = self:GetAbility(i)
	end 
	return tab
end

function FindItemWithBack(self,itemName,num)
	local inventory = self:GetInventory()
	for i = 0, (num or Enum.SLOT_ITEM_BACK_PACK) do
        local item = inventory:GetItemInSlot(i)
        if item and item:GetName() == itemName then
			return item
        end
	end
	return false
end

function FindItemNumber(self,itemName)
	local inventory = self:GetInventory()
	for i = 0, Enum.SLOT_ITEM_NORMAL do
        local item = inventory:GetItemInSlot(i)
        if item and item:GetName() == itemName then
			return i
        end
	end
	return false
end

local projectileItems = {"item_ethereal_blade","item_nullifier","item_rod_of_atos"}
function FindItemBreak(self,name_off,distance,sort,custom)
	local sorted = {}
	custom = custom or {}

	local all_items = GetAllItems(self,true)

	for i,item in ipairs(all_items) do
		local iName = item:GetName()
		if CanBeCasted(item) and (not distance or item:GetCastRange() > distance) 
		and item:GetAbilityTargetTeam() ~= Enum.ABILITY_TEAM_FRIENDLY 
		and IsBehaviour(item,Enum.BEHAVIOR_UNIT_TARGET) 
		and (not HasFlag(item:GetAbilityTargetType(),Enum.ABILITY_TARGET_TREE) or iName == "item_harpoon")
		and item:GetAbilityTargetType()~=Enum.ABILITY_TARGET_CREEP then			
			if (not name_off or name_off ~= iName) 
			and (not custom.no_projectile or not iName:include(projectileItems))
			and (not custom.no_eul or not IsEulName(iName))
			and (not custom.list or not custom.list["BC_SmartLinkin@"..iName:gsub("_%d+$", "")])
			and (iName ~= "item_hurricane_pike" or not distance or distance < item:GetSpecialValue("cast_range_enemy") )
			and not utils.pierce_linkin[iName] then
				if not sort then
					return item
				else 
					if iName == "item_wind_waker" then
						sorted[#sorted+1] = {obj=item, gold=2625+5}
					else
						sorted[#sorted+1] = {obj=item, gold=item:GetCost()}
					end
				end
			end
		end
	end
	if not sort then 
		return false
	elseif #sorted > 0 then
		table.sort(sorted, function(a,b) return a.gold<b.gold end)
		return sorted[1].obj
	end
end

function FindInvisItem(self,invis_sword)
	local name_fn = function(x) 
		return x == "item_glimmer_cape" or x == "item_shadow_amulet" or (invis_sword and (x == "item_invis_sword" or x == "item_silver_edge")) 
	end
	return FindItemFn(self,name_fn)
end

function PhysToItemsName(self)
	return self:GetItemPhysicalName()
end 

function GetNeutralTier(item)
	if not item then return 0 end
	return math.max(item:GetNetVarAbilityInt("m_iItemNeutralTierIndex")+1,item:GetNetVarAbilityInt("m_iTokenTier"))
end

--// Get Modifiers
function HaveModifier(self,name)
	return FindModifier(self,name) ~= nil
end

function FindModifier(self,name)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff and buff:GetName()==name then
			return buff
		end
	end
	return nil
end

function HaveModifierList(self,tab,is_buff) 
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local countList = #tab
	for i = count - 1,0,-1  do			
		local buff = modifiers:GetBuffByIndex(i)--:GetName()
		if buff and (is_buff == nil or buff:IsDebuff() ~= is_buff) then
			local name = buff:GetName()
			
			for i = 1, countList do
				if name == tab[i] then
					return true
				end
			end
			
		end
	end
	return nil
end

function HaveMod(modifiers,count,name)
	return FindMod(modifiers,count,name) ~= nil
end

function FindMod(modifiers,count,name)
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff:GetName()==name then
			return buff
		end
	end
	return nil
end 

function ValidModifier(self,name,time)
	local mod = FindModifier(self,name)
	return not mod or mod:GetRemainingTime()<(time or 1)
end 

function ValidModifierMore(self,name,time)
	local mod = FindModifier(self,name)
	return not mod or mod:GetRemainingTime()>time
end 

function ValidElapsedModifier(self,name,time)
	local mod = FindModifier(self,name)
	return not mod or mod:GetElapsedTime() > (time or 0.5)
end 

function CountModifier(self,name)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local counts = 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff and buff:GetName()==name then
			counts = counts + 1
		end
	end
	return counts
end


--// Visions
function IsRealFlying(self)
	return HaveModifierList(self,{"modifier_rattletrap_jetpack","modifier_night_stalker_darkness"})
end 

function HaveVision(self)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		if buff:IsDebuff() and name =="modifier_disruptor_thunder_strike" or
			name=="modifier_phantom_assassin_stiflingdagger" or
			name=="modifier_razor_link_vision" or
			name=="modifier_spirit_breaker_charge_of_darkness_vision" or
			name=="modifier_sniper_assassinate" or
			name=="modifier_grimstroke_soul_chain" or
			name=="modifier_silencer_last_word" or
			name=="modifier_vengefulspirit_wave_of_terror" or
			name=="modifier_spectre_spectral_dagger" or
			name=="modifier_mars_arena_of_blood_leash" or

			name=="modifier_item_shivas_guard_blast" or
			name=="modifier_void_spirit_aether_remnant_pull" or
			name=="modifier_crystal_maiden_crystal_nova" or
			name=="modifier_jakiro_ice_path_stun" or

			(name=="modifier_truesight" and buff:GetAbility() and buff:GetAbility():GetName() == "item_seer_stone") or

			name=="modifier_bounty_hunter_track" or
			name=="modifier_item_essence_distiller_vision" or
			name=="modifier_bloodseeker_thirst_vision" or
			name=="modifier_slardar_amplify_damage" 
			then
			return true
		end
	end
end

function HaveTrueSight(self,full,time)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		if name=="modifier_item_dustofappearance" or name=="modifier_truesight" or name=="modifier_bloodseeker_thirst_vision" then
			return true
		end
		if name=="modifier_bounty_hunter_track" or name=="modifier_slardar_amplify_damage" or name=="modifier_drowranger_wave_of_silence" or name == "modifier_item_essence_distiller_vision" then
			return (not time or buff:GetRemainingTime() > time)
		end
	end
	if full then 
		for i = 0, count - 1 do
			local buff = modifiers:GetBuffByIndex(i)
			local name = buff:GetName()
			if buff:IsDebuff() and name=="modifier_ember_spirit_searing_chains" or
				name=="modifier_faceless_void_chronosphere_freeze" or
				name=="modifier_disruptor_kinetic_field" or
				name=="modifier_pudge_dismember" or
				name=="modifier_naga_siren_ensnare" or
				name=="modifier_bane_fiends_grip" or
				name=="modifier_treant_overgrowth" or

				name=="modifier_sniper_assassinate" or
				name=="modifier_spirit_breaker_charge_of_darkness_vision" or
				name=="modifier_weaver_swarm_debuff" or

				name=="modifier_shadow_shaman_shackles" or
				name=="modifier_meepo_earthbind" 
				then
				return true
			end
		end
	end 
	return false
end

--// KeyValue
local kvCache = {}
local function GetKvCache(name)
	if not kvCache[name] then 
		kvCache[name] = {}
		local wrapper_name = name
		local loc = "npc_units.txt"
		if name:startWith("npc_dota_hero") then
			loc = "npc_heroes.txt"
		else
			local include = dota.FindKeyValues(loc,	name, "include_keys_from")
			if include ~= "null" then
				wrapper_name = include
			end
		end
		kvCache[name].apoint = tonumber(dota.FindKeyValues(loc,	wrapper_name, "AttackAnimationPoint")) 	or 0.75
		kvCache[name].turn = tonumber(dota.FindKeyValues(loc,	wrapper_name, "MovementTurnRate"))		or 0.6
		kvCache[name].pspeed = tonumber(dota.FindKeyValues(loc,	wrapper_name, "ProjectileSpeed"))
	end 
end 

local function GetTurnRateKV(self)
	local name = self:GetName()
	GetKvCache(name)
	if kvCache[name].turn == 0 then 
		kvCache[name].turn = 0.6
	end
	return kvCache[name].turn
end 

function GetAttackDelayKV(self,bonus)
	local name = self:GetName()
	GetKvCache(name)
	return kvCache[name].apoint/(self:GetAttackSpeed() + (bonus or 0))
end 

function GetAttacBackTimeKV(self)
	local name = self:GetName()
	GetKvCache(name)
	return (self:GetBaseAttackTime()-kvCache[name].apoint)/(self:GetAttackSpeed())
end 

function IsNewAttackReady(self,bonus)
	return dota.GetMapTime() - self:GetLastAttackTime() > GetAttacBackTimeKV(self) + (bonus or 0)
end

function GetProjectileSpeedKV(self)
	local name = self:GetName()	
	GetKvCache(name)
	return kvCache[name].pspeed
end 

--бонус / 100
function GetAttackTime(self,bonus)
	-- local true_attack_speed = math.clamp(self:GetBaseAttackSpeed()/100+self:GetAttackSpeed() + (bonus or 0), 0.2, 7)
	return self:GetBaseAttackTime()/(self:GetAttackSpeed() + (bonus or 0))
end 

local kvCacheItems = {}
function ItemIsSideShop(name)
	GetKvCacheItems(name)
	return kvCacheItems[name].secret_shop
end
function ItemHaveGold(name)
	GetKvCacheItems(name)
	return (kvCacheItems[name].cost or 0) < dota.GetLocalPlayer():GetGold()
end
function ItemGetId(name)
	GetKvCacheItems(name)
	return kvCacheItems[name].id
end
function ItemHaveRecipe(name)
	GetKvCacheItems(name)
	return kvCacheItems[name].recipe ~="null"
end
function ItemDissRules(name)
	GetKvCacheItems(name)
	return kvCacheItems[name].diss
end
function GetKvCacheItems(name)
	if not kvCacheItems[name] then 
		kvCacheItems[name] = {}
		kvCacheItems[name].id = tonumber(dota.FindKeyValues3("npc_ability_ids.txt","ItemAbilities","Locked",name))
		kvCacheItems[name].cost = tonumber(dota.FindKeyValues("npc_items.txt",name, "ItemCost")) or 0
		kvCacheItems[name].secret_shop = tonumber(dota.FindKeyValues("npc_items.txt",name, "SecretShop"))
		kvCacheItems[name].recipe = dota.FindKeyValues("npc_items.txt","item_recipe_"..name:sub(6), "ItemCost")
		kvCacheItems[name].diss = dota.FindKeyValues("npc_items.txt",name, "ItemDisassembleRule")
		if kvCacheItems[name].id then
			kvCacheItems[kvCacheItems[name].id] = kvCacheItems[name]
		end
	end 
end

--// Modifier Status: Stun,Hex

function GetStunTime(self,extra,restrict)
	if not IsStunned(self) then 
		if not extra or (not IsTaunt(self) and not IsFear(self) and not IsNightmared(self)) then
			return 0
		end
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local maximum = 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and buff:IsDebuff() then
				if IsStunned(buff) or (extra and (IsTaunt(buff) or IsFear(buff) or IsNightmared(buff)) ) then

					local name = buff:GetName()		

					if not restrict or not buff:GetAbility() or not restrict[buff:GetAbility():GetName()] then
						if name == "modifier_faceless_void_chronosphere_freeze" or name == "modifier_enigma_black_hole_pull" then
							local sp_time = buff:GetAbility() and buff:GetAbility():GetSpecialValue("duration") or 4
							maximum = math.max(sp_time-buff:GetElapsedTime(),maximum)
						elseif buff:GetDuration()>0.51 then
							maximum = math.max(buff:GetRemainingTime(),maximum)
						end
					end 				
				end
			end
		end
		return maximum
	else 

		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then
				local name =buff:GetName()
				if not utils.stuns_fake[name] then
					if utils.stuns[name] or (extra and utils.disabled[name]) then						
						if buff:GetDuration()>0.51 and buff:GetRemainingTime() > maximum then 
							maximum = buff:GetRemainingTime()
						end				
					elseif name == "modifier_faceless_void_chronosphere_freeze" or name == "modifier_rattletrap_cog_leash" or name == "modifier_enigma_black_hole_pull" then 
						return 4-buff:GetElapsedTime()
					end
					if buff:IsDebuff() then
						local ab = buff:GetAbility()
						if ab then 
							local time = ab:GetSpecialValue("stun_duration")
							if time and time > 0.51 then 
								if buff:GetRemainingTime() > maximum then 
									maximum = buff:GetRemainingTime()
								end 
							end 
						end 
					end
				end
			end
		end
		return maximum
	end
end

function GetStunTimeName(self,extra)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local maximum = 0
	local ability = nil

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and buff:IsDebuff() then
				if IsStunned(buff) or (extra and (IsTaunt(buff) or IsFear(buff) or IsNightmared(buff)) ) then
					local name = buff:GetName()					
					if name == "modifier_faceless_void_chronosphere_freeze" or name == "modifier_enigma_black_hole_pull" then
						local sp_time = buff:GetAbility() and buff:GetAbility():GetSpecialValue("duration") or 4
						if sp_time-buff:GetElapsedTime() > maximum then
							maximum = 4-buff:GetElapsedTime()
							ability = buff:GetAbility()
						end
					elseif buff:GetDuration()>0.51 then
						if buff:GetRemainingTime() > maximum then
							maximum = buff:GetRemainingTime()
							ability = buff:GetAbility()
						end
					end
				end
			end
		end
		return maximum,ability
	else
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then
				local name =buff:GetName()
				if not utils.stuns_fake[name] then
					if utils.stuns[name] or (extra and utils.disabled[name]) then						
						if buff:GetDuration()>0.51 and buff:GetRemainingTime() > maximum then 
							maximum = buff:GetRemainingTime()
							ability = buff:GetAbility()
						end				
					elseif name == "modifier_faceless_void_chronosphere_freeze" or name == "modifier_rattletrap_cog_leash" or name == "modifier_enigma_black_hole_pull" then 
						if 4-buff:GetElapsedTime() > maximum then 
							maximum = 4-buff:GetElapsedTime()
							ability = buff:GetAbility()
						end 
					end
					if buff:IsDebuff() then
						local ab = buff:GetAbility()
						if ab then 
							local time = ab:GetSpecialValue("stun_duration")
							if time and time > 0.51 then 
								if buff:GetRemainingTime() > maximum then 
									maximum = buff:GetRemainingTime()
									ability = ab
								end 
							end 
						end 
					end
				end
			end
		end
		return maximum,ability
	end
end

function GetInvulTime(self)
	if not IsInvul(self) then 
		return 0
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then
				local name = buff:GetName()					
				if name == "modifier_naga_siren_song_of_the_siren" then
					return 7.55 - buff:GetElapsedTime()
				elseif IsInvul(buff) and buff:GetDuration() ~= -1 then
					return buff:GetRemainingTime()
				end
			end
		end
		return 6
	else 

		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then 
				local tip = utils.invule[buff:GetName()]
				if tip then 
					if tip == 7 then 
						return 7.55 - buff:GetElapsedTime()
					else 
						return buff:GetRemainingTime()
					end
				end
			end
		end
		return 6
	end
end 

function GetHexTime(self)
	if not IsHexed(self) then
		return 0
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local maximum = 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and IsHexed(buff) then
				maximum = math.max(maximum,buff:GetRemainingTime())
			end
		end
		return maximum
	else

		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and buff:IsDebuff() and utils.hex[buff:GetName()]  then
				maximum = math.max(buff:GetRemainingTime(),maximum)
			end
		end
		return maximum
	end
end 

function GetRootTime(self)
	if not IsRooted(self) then 
		return 0
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local maximum = 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and buff:IsDebuff() and IsRooted(buff) then  
				maximum = math.max(maximum,buff:GetRemainingTime())
			end
		end
		return maximum
	else
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and buff:IsDebuff() and utils.roots[buff:GetName()] then  
				return buff:GetRemainingTime()
			end
		end
		return 0
	end
end 

function GetBKBTime(self)
	if not IsMagicImmune(self) then 
		return 0
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local maximum = 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and IsMagicImmune(buff) then 
				maximum = math.max(maximum,buff:GetRemainingTime())
			end
		end
		return maximum
	else 
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and utils.bkb[buff:GetName()] then 
				return buff:GetRemainingTime()
			end
		end
		return 4
	end
end 

function GetSilenceTime(self,wihtout_hex)
    if not IsSilenced(self) then
        return 0
	end		
	if Enum.MOD_FLAG_VERSION then
		local modifiers = self:GetModifierManager()
		local count = modifiers and modifiers:GetCurrentBuffCount() or 0
		local maximum = 0

		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and IsSilenced(buff) and (not wihtout_hex or not IsHexed(buff)) then 
				maximum = math.max(maximum,buff:GetRemainingTime())
			end
		end
		return maximum
	else 
		if not wihtout_hex then
			local time = GetHexTime(self)
			if time ~= 0 then 
				return time
			end
		end
		local modifiers = self:GetModifierManager()
		local count = modifiers and modifiers:GetCurrentBuffCount() or 0
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then 
				local spell = buff:GetAbility() 
				if spell then 
					local data = GetFastInfo(spell:GetName())
					if data and (data.IsSilence or data.IsSilenceEx) then
						return buff:GetRemainingTime()
					end
				end
			end
		end
		return 6
	end
end 

function GetTeleportingTime(self)
	for i,v in ipairs({"modifier_teleporting"}) do
		local mod = FindModifier(self,v)
		if mod then 
			return mod:GetRemainingTime()
		end 
	end
	local gate = FindModifier(self,"modifier_twin_gate_warp_channel")
	if gate then 
		return -1
	end
	return 0
end 

function GetCastingTime(self,low)
	local game_rules = dota.GameRules.GetInstance()
	local inventory = self:GetInventory()
	local item
	for i = 0, Enum.SLOT_ITEM_NORMAL do
		item = inventory:GetItemInSlot(i)
		if item and IsCasted(item) then
			return item:GetChannelStartTime() - game_rules:GetGameTime() + item:GetChannelTime()
		end
	end
	item = inventory:GetItemInSlot(Enum.SLOT_ITEM_TP) --tp
	if item and IsCasted(item) then
		return item:GetChannelStartTime() - game_rules:GetGameTime() + item:GetChannelTime()
	end 
	item = inventory:GetItemInSlot(Enum.SLOT_NEUTRAL) --neutral
	if item and IsCasted(item) then
		return item:GetChannelStartTime() - game_rules:GetGameTime() + item:GetChannelTime()
	end 
	for i = 1, Enum.SLOT_SPELL_VISIBLE + 1 do
		local item = self:GetAbility(i)
		if item and IsCasted(item) then 
			return item:GetChannelStartTime() - game_rules:GetGameTime() + item:GetChannelTime()
		end
	end
	return 0
end 


function ValidInvule(self,times)
	local invule_time = GetInvulTime(self)
	return invule_time == 0 or invule_time < (times or 0.3)
end 

function ValidStun(self, times, extra)
	local stun_time = GetStunTime(self,extra)
	return stun_time == 0 or stun_time < (times or 0.3)
end 

function ValidHex(self,times,only_hex)
	local hex_time = GetHexTime(self)
	return (hex_time == 0 or hex_time < (times or 0.3)) and (only_hex or ValidStun(self,times))
end 

function ValidRoot(self,times)
	local root_time = GetRootTime(self)
	return (root_time == 0 or root_time < (times or 0.3)) and ValidStun(self,times,true)
end 

function ValidInvulBKB(self,time)
	return ValidInvule(self, time) and GetBKBTime(self) < time
end 

function ValidComboStunHexTime(self,times)
	if ValidStun(self,times) then 
		if IsHexed(self) then 
			if CanMove(self) and self:GetMoveSpeed() > 250 then 
				return true 
			else
				return GetHexTime(self)<(times or 0.3)
			end
		else 
			return true
		end
	end
	return false
end 


function IsNoMoveTime(target,time)
	return GetStunTime(target,true)>time or GetHexTime(target)>time or GetRootTime(target)>time
end 


function IsMaxMoveSpeed(self)
	if HaveModifierList(self,utils.max_movespeed) then
		return true
	end
	return self:HasFlag(Enum.MODIFIER_STATE_UNSLOWABLE)
end 

function IsForceStuffed(self)
	return HaveModifierList(self,utils.force_stuff)
end 

function IsBrokenSilence(self)
	return HaveModifierList(self,utils.silence_broken)
end 

function IsGhosted(self)
	return HaveModifierList(self,utils.ghost)
end 


function IsBKBModifier(buff,name)
	if name and name ~= "" and name ~= "modifier_huskar_life_break_charge" then
		-- if Enum.MOD_FLAG_VERSION then
		-- 	return IsMagicImmune(buff)
		-- else
			return utils.bkb[name]
		-- end 
	end 
end


function GetInvulModifier(self,min_duration)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0

	if Enum.MOD_FLAG_VERSION then
		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff and IsInvul(buff) then 
				local name = buff:GetName()		
				if name == "modifier_naga_siren_song_of_the_siren" then
					return TempModifier(buff)
				elseif buff:GetDuration() > (min_duration or 1.2) then 
					return buff
				end 
			end
		end
		return
	else  

		for i = count - 1,0,-1  do
			local buff = modifiers:GetBuffByIndex(i)
			if buff then 
				local name = buff:GetName()						
				if utils.invule[name] then 
					if utils.invule[name] == 7 then
						return TempModifier(buff)
					elseif utils.invule[name]~=1 then
						return buff
					end
				end
			end
		end
	end
end 

function TempModifier(mod) --naga
	local temp = {}
	temp.name = mod:GetName()
	temp.ability = mod:GetAbility()
	temp.caster = mod:GetCaster()
	temp.time = mod:GetElapsedTime()
	function temp:GetName()	return temp.name end 
	function temp:GetAbility() return temp.ability end 
	function temp:GetCaster() return temp.caster end 
	function temp:GetRemainingTime() return 7.55 - mod:GetElapsedTime() end 
	function temp:HasFlag() return Enum.MODIFIER_STATE_INVULNERABLE end 
	return temp
end 

function IsGoodBuff(target,brew)
	local modifiers = target:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff then
			if not buff:IsDebuff() then
				if buff:GetRemainingTime() > 0.25 then
					local name = buff:GetName()
					if name == "modifier_item_aeon_disk_buff" or name:startWith("modifier_rune") then
						return true
					end
					local spell = buff:GetAbility() 
					if spell and spell:GetDispellableType() == Enum.SPELL_DISPELLABLE_YES then
						return true
					end
				end
			elseif brew then
				if buff:GetName() == "modifier_brewmaster_storm_cyclone" then
					return true
				end
			end
		end
	end
end

function HaveDebuff(target)
	local modifiers = target:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff and buff:IsDebuff() then
			return true
		end
	end
	return false
end

function GetHpProcent(self)
	return self:GetHealth()/self:GetMaxHealth()
end 

function GetMpProcent(self)
	return self:GetMana()/self:GetMaxMana()
end 

--return 0.1 for 90% resistance
function GetStatusResist(self)
	local resist = 1
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff then 
			local name = buff:GetName()
			if utils.status[name] then 
				if not buff:IsDebuff() then
					local spell = buff:GetAbility()
					if spell then
						if utils.status[name] == true then 
							resist =resist * (1-spell:GetSpecialValue("status_resistance")/100)

						elseif name == "modifier_life_stealer_unfettered" then
							resist = resist * (1-spell:GetSpecialValue("status_resist")/100)							
						
						elseif name == "modifier_item_unrelenting_eye" then
							resist = resist * (1-spell:GetSpecialValue("status_res_pct_increase_per_hero")/100)	

						elseif name == "modifier_slardar_seaborn_sentinel_river" then 
							if AghanimState(self) then  
								resist =resist * (1-spell:GetSpecialValue("puddle_status_resistance")/100) 
							end
						elseif name == "modifier_night_stalker_hunter_in_the_night" then 
							if not IsDay() then  
								local bonus = buff:GetAbility():GetSpecialValue("bonus_status_resist_night")
								if bonus ~= 0 then
									resist = resist * (1-bonus/100) 
								end
							end
						elseif name == "modifier_brewmaster_liquid_courage_passive" then  --status_resist
							if buff:GetRemainingTime() > 0 then
								resist = resist * (1-spell:GetSpecialValue("status_resist")/100)	
							end
						end
					end
				else
					local spell = buff:GetAbility()
					if spell then
						if utils.status[name] == true then 						
							resist =resist * (1+spell:GetSpecialValue("status_resistance")/100)
						elseif name == "modifier_alchemist_corrosive_weaponry_debuff" then 
							local val = (spell:GetSpecialValue("status_resist_per_stack")/100)*buff:GetStackCount()	
							resist = resist * (1+val) 
						elseif name == "modifier_morphling_scepter_stats_drain_strength_debuff" then 
							resist =resist * (1+spell:GetSpecialValue("scepter_status_resist")/100)
						elseif name == "modifier_morphling_scepter_stats_drain_strength_buff" then --псевдо дебафф
							resist =resist * (1-spell:GetSpecialValue("scepter_status_resist")/100)	
						end
					end
				end 
			end
		end
	end 
	return resist
end 


function IsCasting(self,check_in_phase,count_spells)
	local inventory = self:GetInventory()
	if not inventory then return false end

	local item
	for i = 0, Enum.SLOT_ITEM_NORMAL do
		item = inventory:GetItemInSlot(i)
		if item and IsCasted(item) then
			return true
		end
	end
	item = inventory:GetItemInSlot(Enum.SLOT_ITEM_TP)
	if item and IsCasted(item) then
		return true 
	end 
	item = inventory:GetItemInSlot(Enum.SLOT_NEUTRAL)
	if item and IsCasted(item) then
		return true 
	end 

	local max_spell_number = count_spells or (Enum.SLOT_SPELL_VISIBLE + 1)
	for i = 1, max_spell_number do
		local spell = self:GetAbility(i)
		if spell then 
			if IsCasted(spell) or (check_in_phase and spell:InAbilityPhase()) then
				return spell:GetName()~="lion_mana_drain"
			end 
		end
	end

	local list = {"modifier_snapfire_mortimer_kisses", "modifier_riki_tricks_of_the_trade_phase", "modifier_spirit_breaker_charge_of_darkness",
	"modifier_primal_beast_onslaught_windup","modifier_twin_gate_warp_channel", "modifier_primal_beast_onslaught_movement_adjustable","modifier_tinker_rearm"}
	if HaveModifierList(self, list) then
		return true
	end

	if self:GetActivity() == Enum.ACT_DOTA_GENERIC_CHANNEL_1 then
		for i = Enum.SLOT_SPELL_NORMAL + 4, Enum.SLOT_SPELL_MAX do 
			local spell = self:GetAbility(i)
			if spell then
				if IsCasted(spell) then 
					return true
				end
			else
				break
			end
		end
	end

	return false
end

function IsTp(self)
	return FindModifier(self,"modifier_teleporting")
end 

function IsTpTime(self)
	local mod = IsTp(self)
	if mod then
		return mod:GetRemainingTime()
	end
end 

function InAbilityPhased(self)
	for i = 1, Enum.SLOT_SPELL_VISIBLE do 
		local spell = self:GetAbility(i)
		if spell and spell:InAbilityPhase() then 
			return spell 
		end 
	end
	return false
end 

function IsLinkensProtected(self,if_am_false,is_2)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	local linkins = 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		if name=="modifier_antimage_counterspell" then	
			if if_am_false then 
				return false
			end
			return name
		elseif name=="modifier_item_sphere_target" then
			if is_2 then
				linkins = linkins + 1
			else
				return true	
			end		
		elseif name=="modifier_item_mirror_shield" then
			if if_am_false then 
				return false
			end
			local link = buff:GetAbility()
			if link and link:GetCooldown()==0 then
				return name
			end
		elseif name=="modifier_item_sphere" then	
			local link = buff:GetAbility()
			if link and CanBeCasted(link) then
				if is_2 then
					linkins = linkins + 1
				else
					return true
				end
			end
		end 
	end
	if is_2 and linkins ~= 0 then
		return linkins
	else
		return false
	end
end

function IsLinkensProtected2X(self)
	return IsLinkensProtected(self,true,true)
end 

function IsAmMirror(self,only_counterspell)
	if only_counterspell then 
		return HaveModifier(self,"modifier_antimage_counterspell")
	end 
	local is = IsLinkensProtected(self)
	return is and type(is)~="boolean"
end 

function IsLotusProtected(self)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		if name=="modifier_antimage_counterspell" or name == "modifier_item_lotus_orb_active" then	
			return name
		elseif (name == "modifier_item_mirror_shield") then 
			local link = buff:GetAbility()
			return link and link:GetCooldown()==0 and name
		end
	end
	return false
end

-- [target, self]
function SmartInLotus(target,self,via_mgc)
	local protect = IsLotusProtected(target)
	if protect then 
		if protect == "modifier_item_lotus_orb_active" then 
			return (not via_mgc and IsMagicImmune(self)) or IsLinkensProtected(self)
		else 
			return false
		end 
	end 
	return true
end 

function IsDoubleHit(self)
	local sabre = FindItem(self,"item_echo_sabre")
	if sabre then
		return sabre:GetCooldown()==0
	else 
		sabre = FindItem(self,"item_harpoon")
		return sabre and not HaveModifier(self,"modifier_item_harpoon_internal_cd")
	end
end

function ValidForMagicCombo(self,target,dmg,debuff)
	if dmg then 
		if IsMagicImmune(self) then 
			return true 
		end
		if HaveModifierList(self,{"modifier_antimage_counterspell","modifier_nyx_assassin_spiked_carapace"}) then
			return false
		end
	else
		local modifiers = target:GetModifierManager()
		local count = modifiers and modifiers:GetCurrentBuffCount() or 0
		for i = 0, count - 1 do
			local buff = modifiers:GetBuffByIndex(i)
			local name = buff:GetName()
			if (name=="modifier_antimage_counterspell" or name == "modifier_item_lotus_orb_active") and not IsMagicImmune(self) then	
				return false 
			elseif name == "modifier_slark_dark_pact_pulses" then 
				return false 
			end 
		end
	end
	return true
end 

function IsAionDiskProtected(self)
	local buff = FindModifier(self,"modifier_item_aeon_disk")
	if buff then 
		local aeo = buff:GetAbility()
		return aeo and aeo:GetCooldown()==0 and not UnderNull(self)
	end 
	return false 	 
end 

function NullCast(target)
	local buff = FindModifier(target,"modifier_item_nullifier_mute")
	return not buff or buff:GetRemainingTime() < 1.5
end 

function UnderNull(self)
	return HaveModifierList(self,{"modifier_item_nullifier_mute","modifier_shadow_demon_purge_slow"}) and not IsMagicImmune(self)
end 

function AghanimState(self)
	if not self then return false end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName():sub(10,25)
		if name =="item_ultimate_sc" then
			return true
		end
	end
	return false
end

function AghanimStateLow(self)
	return HaveModifier(self,"modifier_item_aghanims_shard")
end

--// NPC
function IsRanged(self)
	return self:GetDistanceType() == 2
end

function IsHero(self)
	return self:GetUnitType()==Enum.UNIT_TYPE_HERO
end
function IsHeroOrBear(self)
	return HasFlag(self:GetUnitType(),Enum.UNIT_TYPE_HERO)
end 
function IsSuperCreep(self)
	return HasFlag(self:GetUnitType(),Enum.UNIT_TYPE_HERO_CREEP)
end 
function IsCreeps(self)
	return HasFlag(self:GetUnitType(),Enum.UNIT_TYPE_CREEP)
end 

function IsRealOrMeepo(self)
	return not self:IsIllusion() or self:IsMeepoClones()
end


function CanDie(self,ignore_reinc,ignore_shaman) 
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local buffName = buff:GetName()
		if buffName=="modifier_dazzle_shallow_grave" or buffName=="modifier_troll_warlord_battle_trance"
		or buffName=="modifier_oracle_false_promise" or buffName=="modifier_skeleton_king_reincarnation_scepter_active" 
		or buffName=="modifier_item_helm_of_the_undying_active" 

		or (buffName=="modifier_obsidian_destroyer_equilibrium" and CanBeCastedOld(buff:GetAbility()) and AghanimState(buff:GetCaster()))
		or (buffName=="modifier_shadow_shaman_fowl_play" and CanBeCastedOld(buff:GetAbility()) and not ignore_shaman)
		or (buffName=="modifier_item_aegis" and not ignore_reinc)
		or (buffName=="modifier_skeleton_king_reincarnation" and CanBeCastedOld(buff:GetAbility()) and not ignore_reinc) 
		or (buffName=="modifier_item_aeon_disk" and CanBeCastedOld(buff:GetAbility())) then
			return false
		end
	end
	return true
end

function CantDie(self, time)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local buffName = buff:GetName()
		if buffName=="modifier_dazzle_shallow_grave" or buffName=="modifier_troll_warlord_battle_trance" 
		or buffName=="modifier_oracle_false_promise" or buffName=="modifier_abaddon_borrowed_time" then 
			return buff:GetRemainingTime() > (time or 0)
		end 
	end
end

function UnderBladeMail(target)
	return HaveModifierList(target,{"modifier_item_blade_mail_reflect","modifier_spectre_dispersion_boost"})
end 


function IsAttacking(v,fixed)
	if IsAttackAct(v:GetActivity()) then
		return true
	end
	if fixed then
		local fn = utils.fix_is_attacking[v:GetName()]
		if fn then
			if fn.mod and HaveModifier(v,fn.mod) then
				local last_hit = dota.GetMapTime() - v:GetLastAttackTime()
				return last_hit > 0.15 and last_hit < 0.6
			end
			return fn.act and v:GetActivity() == fn.act
		end
	end 
end 

function IsAttackAct(activity)
	return activity == Enum.ACT_ATTACK or activity == Enum.ACT_ATTACK2 or activity == Enum.ACT_CRIT
end 

function IsIdle(v)
	local activity = v:GetActivity()
	return activity == Enum.ACT_IDLE or  activity == Enum.ACT_IDLE_RARE
end 

function IsRunning(self)
	return self:GetActivity() == Enum.ACT_MOVE
end 

function IsRunningOn(target,self,time)
	return IsRunning(target) and WillToFace(target,self,Enum.FACE_ANGLE-5) and CanMove(target) and GetDistance2D(self,target)<target:GetMoveSpeed()*time
end

function GetTrueMinDmg(v)
	return v:GetDamageMin() + v:GetBonusDamage()
end 

function GetTrueMaxDmg(v)
	return v:GetDamageMax() + v:GetBonusDamage()
end 

function GetAvarageDmg(v)
	return (v:GetDamageMax() + v:GetDamageMin())/2 + v:GetBonusDamage()
end 

function GetArmor(v)
	return (1-v:GetPhysicalArmorReduction()/100)
end

function GetArmorResist(target,minus)
	local armor = target:GetPhysicalArmorValue() + minus
	return 1 - (0.06 * armor) / (1 + 0.06 * math.abs(armor))
end 



function IsMagicDmgImmune(self)
	return IsMagicImmuneOld(self) or HaveModifier(self,"modifier_item_aeon_disk_buff")
end

function IsPhysDmgImmune(self)
	if IsAttackImmune(self) then		
		return true
	else
		if HaveModifierList(self,utils.immunity.phys) then
			return true
		end
	end
	return false
end

function IsRooted(self)
	return self:HasFlag(Enum.MODIFIER_STATE_ROOTED)
end

function IsLeashed(self)
	return IsRooted(self) or self:HasFlag(Enum.MODIFIER_STATE_TETHERED)
end

function IsDisarmed(self)	
	return self:HasFlag(Enum.MODIFIER_STATE_DISARMED)
end

function IsAttackImmune(self)
	return self:HasFlag(Enum.MODIFIER_STATE_ATTACK_IMMUNE)
end

function IsAttackImmuneInvule(self)
	return IsAttackImmune(self) or IsInvul(self)
end

function IsNightmared(self)
	return self:HasFlag(Enum.MODIFIER_STATE_NIGHTMARED)
end

function IsSilenced(self)
	return self:HasFlag(Enum.MODIFIER_STATE_SILENCED)
end

function IsMuted(self)
	return self:HasFlag(Enum.MODIFIER_STATE_MUTED)
end

function IsStunned(self)
	return self:HasFlag(Enum.MODIFIER_STATE_STUNNED)
end

function IsTaunt(self)
	return self:HasFlag(Enum.MODIFIER_STATE_TAUNTED)
end 

function IsDisabled(self)
	return IsStunned(self) or IsTaunt(self) or IsFear(self)
end

function IsHexed(self)
	return self:HasFlag(Enum.MODIFIER_STATE_HEXED)
end
 
function IsInvisible(self,only_state)
	return self:HasFlag(Enum.MODIFIER_STATE_INVISIBLE) or (not only_state and RealInvisible(self))
end

function IsDenyable(self)
	return self:HasFlag(Enum.MODIFIER_STATE_SPECIALLY_DENIABLE)
end

function IsCommandRestrict(self)
	return self:HasFlag(Enum.MODIFIER_STATE_COMMAND_RESTRICTED)
end

function IsFear(self)
	return self:HasFlag(Enum.MODIFIER_STATE_FEARED)
end

function IsInvul(self)
	return self:HasFlag(Enum.MODIFIER_STATE_INVULNERABLE)
end

function IsMagicImmuneOld(self)
	return self:HasFlag(Enum.MODIFIER_STATE_MAGIC_IMMUNE) 
end 

function IsMagicImmuneNew(self)
	return self:HasFlag(Enum.MODIFIER_STATE_DEBUFF_IMMUNE) 
end 

function IsMagicImmune(self)
	return IsMagicImmuneOld(self) or IsMagicImmuneNew(self)
end

function IsMagicInvuleImmune(self)
	return IsMagicImmune(self) or IsInvul(self)
end

function IsUntargetable(self)
	return self:HasFlag(Enum.MODIFIER_STATE_UNTARGETABLE) or self:HasFlag(Enum.MODIFIER_STATE_UNTARGETABLE_ENEMY)
end 

function IsUntargetableAlly(self)
	return self:HasFlag(Enum.MODIFIER_STATE_UNTARGETABLE_ALLIED)
end 

function IsCantMiss(self)
	return self:HasFlag(Enum.MODIFIER_STATE_CANNOT_MISS)
end 

function IsPathingTree(self)
	return self:HasFlag(Enum.MODIFIER_STATE_ALLOW_PATHING_TROUGH_TREES)
end

function IsFakeAlly(self)
	return self:HasFlag(Enum.MODIFIER_STATE_FAKE_ALLY)
end

function IsNoCollisium(self)
	return self:HasFlag(Enum.MODIFIER_STATE_NO_UNIT_COLLISION)
end 

function IsFlying(self)
	return self:HasFlag(Enum.MODIFIER_STATE_FLYING) or self:HasFlag(Enum.MODIFIER_STATE_FLYING_FOR_PATHING_PURPOSES_ONLY)	
end

function IsPassiveDisabled(self)
	return self:HasFlag(Enum.MODIFIER_STATE_PASSIVES_DISABLED)
end

function IsOutOfGame(self,invul_fountain)
	return self:HasFlag(Enum.MODIFIER_STATE_OUT_OF_GAME) and (not invul_fountain or not HaveModifier(self,"modifier_fountain_invulnerability"))
end 

-- Custom Is
function IsDmgHitInvis(self)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		if buff and utils.invis[buff:GetName()] and utils.invis[buff:GetName()] == 1 then
			return true
		end
	end
	return false
end 
function RealInvisible(self,return_name)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		if buff and utils.invis[buff:GetName()] then
			return (return_name and InvisNameOr(buff)) or "item_bottle"
		end
	end
	return false
end 
function InvisNameOr(buff)
	local spell = buff:GetAbility()
	return spell and spell:GetName() or "item_bottle"
end

function CanInvisibleCast(self,only_state)
	if not IsInvisible(self,only_state) then 
		return true 
	end
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buffName = modifiers:GetBuffByIndex(i):GetName()
		if buffName=="modifier_treant_natures_guise" 
		or (buffName=="modifier_invisible" and self:GetName()=="npc_dota_hero_riki")
		or buffName == "modifier_slark_shadow_dance" 
		or buffName == "modifier_slark_depth_shroud" 
		or buffName == "modifier_nyx_assassin_burrow" 
		or buffName == "modifier_phantom_assassin_blur_active"  then
			return true
		end
	end
	return false
end

function IsLowPriority(self,check_meepo)
	if HaveModifierList(self,{"modifier_skeleton_king_reincarnation_scepter_active","modifier_item_helm_of_the_undying_active" }) then
		return true
	end
	if check_meepo then 
		return IsOutOfGame(self) and self:HasFlag(Enum.MODIFIER_STATE_CANNOT_BE_MOTION_CONTROL)
	end 
	return false
end 

function IsControllable(self,entity)
	return self:GetControllerIndex()==entity:GetControllerIndex()
end


function SfUltAmBLinkCast(self)
	local anim_list = {antimage_blink=true,queenofpain_blink=true,nevermore_requiem=true,legion_commander_duel=true}
	for i = 1, Enum.SLOT_SPELL_VISIBLE do 
		local spell = self:GetAbility(i)
		if spell and anim_list[spell:GetName()] and spell:InAbilityPhase() then 
			return true 
		end 
	end
	return false
end 

function IsSlarkQ(self, full)	
	if HaveModifier(self,"modifier_slark_dark_pact_pulses") then 
		return true 
	else 
		local darkpact = FindModifier(self,"modifier_slark_dark_pact")
		return darkpact and (full or darkpact:GetRemainingTime() < 0.75)
	end
end 

function IsRuptured(self)
	return HaveModifier(self,"modifier_bloodseeker_rupture")
end 

function IsAAUlt(self)
	return HaveModifier(self,"modifier_ice_blast") or HaveModifier(self,"modifier_doom_bringer_doom")
end 

function CanHealMana(self)
	return not HaveModifier(self,"modifier_antimage_empowered_mana_break_debuff")
end 

function IsPulling(self,target,true_harpoon)
	local harpoon = FindItem(self,"item_harpoon")
	if true_harpoon and harpoon and CanBeCasted(harpoon) then
		return true
	end
	return HaveModifier(target,"modifier_item_harpoon_pull") or not SleepCheck("base.item_harpoon") or (harpoon and TimeSinceCast(harpoon) < 1.5)
end 



function DmgProtection(self)
	return HaveModifierList(self,{
		"modifier_templar_assassin_refraction_absorb","modifier_nyx_assassin_spiked_carapace","modifier_abaddon_borrowed_time","modifier_winter_wyvern_winters_curse_aura"
	})
end 

function ProjectileWaitTime(self,brek,target,base_sleep)
	local info = GetFastInfo(brek:GetName())
	local extr_sleep = base_sleep
	if info and (info.Speed or info.SpeedEx) then
		local sp = info.SpeedEx or brek:GetSpecialValue(info.Speed)
		local pos = GetPredictionVector(self,target,sp,brek)
		extr_sleep = extr_sleep + GetDistance2D(self,pos) / sp * 1000 + 33
	end
	return extr_sleep
end



function CanMove(self)
	return not IsRooted(self) and (not IsStunned(self) or (IsRunning(self) and HaveModifier(self,"modifier_invoker_cold_snap")))
	and not HaveModifierList(self,{"modifier_invoker_deafening_blast_knockback","modifier_hoodwink_sharpshooter_windup"})
end

function CanCast(self)
	return not IsSilenced(self) and not IsDisabled(self) and not IsCommandRestrict(self) 
end

function CanUseItems(self)
	return not IsMuted(self) and not IsDisabled(self) and not IsCommandRestrict(self) 
end

function CanAttack(self)
	return not IsDisarmed(self) and not IsDisabled(self)
end

function CanAttackInGhost(self)
	if HaveModifier(self,"modifier_muerta_supernatural") then
		return true
	-- else 
	-- 	local brouch = FindItem(self,"item_revenants_brooch")
	-- 	return brouch and brouch:ToggleState()
	end
end

function CanAttackTarget(self,target,check_untarget,will_attack_ghost)
	if IsInvul(target) or not CanAttack(self) or (check_untarget and IsUntargetable(target)) then 
		return false 
	end 

	return not IsAttackImmune(target) or will_attack_ghost or CanAttackInGhost(self)
end


--// Ability functions ==--
function CanBeCasted(spell)
	if IsItem(spell) then 
		return spell:CanBeExecuted() and IsItemEnabled(spell)
	elseif spell:IsHidden() then 
		return spell:GetAbilityState() == -1
	else
		return spell:CanBeExecuted()
	end 
end

function CanBeCastedOld(spell)
	return spell and spell:GetCooldown() == 0 and (spell:GetManaCost() == 0 or spell:GetManaCost() <= spell:GetCaster():GetMana())
end

function CanBeCharged(spell)
	local name = spell:GetName()
	return not IsItemName(name) or (not spell:GetRequiresCharges() and name~="item_bottle") or spell:GetCurrentCharges() > 0
end 

function IsItemEnabled(self)
	return not self:GetPropertyBool("C_DOTA_Item","m_bItemEnabled")
end 

function IsCasted(spell)
	return spell:GetChannelStartTime() ~= 0
end

function FindStaticSpecialData(index,name,...)
	if not ...  then return nil end

	local ent = index
	if type(ent) == "number" then 
		ent = dota.GlobalInstanceManager:GetClientEntityList():GetClientBaseNPC(index)
		if ent and not ent:IsDotaNPC() then 
			ent = nil
		end 
	end 	
	
	local result = {}
	local arg = {...}
	if ent then 
		local spell = FindSpellMax(ent,name)
		if spell then			
			local lvl = math.max(1,spell:GetLevel())
			for i = 1, #arg do
				result[i] = spell:GetLevelSpecialValue(arg[i][1],lvl)
			end
			return unpack(result)
		end 
	end 
	for i = 1, #arg do
		result[i] = arg[i][2]
	end
	return unpack(result)
end 

---@param spell userdata
function IsItem(spell)
	return IsItemName(spell:GetName()) 
end 
---@param name string
function IsItemName(name)
	return name:sub(1,5) == "item_" 
end 

function IsStolen(spell)
	return spell:IsStolen() or spell:IsReplicated()
end

function BackpackOn(name)
	return name ~= "item_gem" and name ~= "item_aegis" and name ~= "item_rapier" 
end

function TimeSinceCast(spell)
	if not spell then return 9999 end
	return spell:GetCooldownLength(spell:GetLevel()) - spell:GetCooldown()
end 

function GetSmartInfo(spell,info)
	if not info then return 0 end 
	if type(info) == "number" then 
		return info 
	else 
		return spell:GetSpecialValue(info)
	end 
end

function SelfTargetAbilityTime(self,target,spell)
	return (spell and RealCastPoint(spell) or 0) + dota.GetLatency()/1000 + ((target and GetTurnTimeLatence(self,target)/1000) or 0) + 0.033
end 

function RealCastPoint(spell)
	local point = spell:GetCastPoint()
	if point > 0 and spell:IsStolen() then
		local override = spell:GetOverrideCastPoint()
		if override > 0 then
			point = override
		end
	end 
	if point > 0 then
		point = point*CheckCastPoint(spell:GetCaster())
	end 
	return point
end 

function CheckCastPoint(self)
	-- return 1
	if not self then 
		return 1
	end 
	local resist = 1
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		if buff then 
			local name = buff:GetName()
			local data = utils.cast_point[name]
			if data then 
				local spell = buff:GetAbility()
				if spell then 
					if buff:IsDebuff() then
						resist = resist * (1 + spell:GetSpecialValue(data.value)/100+(data.defoult))
					else
						resist = resist * (1 - spell:GetSpecialValue(data.value)/100+(data.defoult))
					end					
				end
			end
		end
	end 
	return resist
end 

function SpellCastTime(self, spell, target, extra_ping, saved_ticks, before_orders_ticks)

	local smooth_time = 0

	if newHum.IsNewHum() and saved_ticks > 1 and target and mouseSim.last_cast_pos then
		smooth_time = newHum.CalculateOrderTicks(saved_ticks, mouseSim.last_cast_pos) 
	else
		smooth_time = 1
	end

	extra_ping = (extra_ping or 0) + (smooth_time + before_orders_ticks)*Enum.TICK_TIME_MS

	local fast_cast = IsBehaviour(spell,Enum.BEHAVIOR_IMMEDIATE)
	local cast_time = (fast_cast and 1) or RealCastPoint(spell)*1000
	local time = cast_time + extra_ping
	local turn_time = 0
	if target and (target.x or target:GetIndex()~=self:GetIndex()) and not fast_cast then
		turn_time = GetTurnTimeLatence(self, target)
		time = time + turn_time
	end
	return time, turn_time
end

--// Vector functions ==--
function GetDistance2D(a,b)
	-- if not a or not b then
	-- 	error(debug.traceback())
	-- end
	if not a.x then a = a:GetNetworkOrigin() end	
	if not b.x then b = b:GetNetworkOrigin() end
	return ((a.x-b.x)^2+(a.y-b.y)^2)^.5
end

function GetHullDistanceAttack(a,b)
	local posa = a:GetAbsOrigin()
	local posb = b:GetAbsOrigin()
	return ((posa.x-posb.x)^2+(posa.y-posb.y)^2)^.5 - a:GetHullRadius() - b:GetHullRadius()
end

function GetHullDistance(a,b,item)
	-- if item and not IsItem(item) then
	-- 	return GetHullDistance2D(a,b)
	-- end 
	local posa = a:GetAbsOrigin()
	local posb = b:GetAbsOrigin()
	return ((posa.x-posb.x)^2+(posa.y-posb.y)^2)^.5 - b:GetHullRadius()
end

function GetDistance3D(a,b)
	if not a.x then a = a:GetNetworkOrigin() end	
	if not b.x then b = b:GetNetworkOrigin() end
	return ((a.x-b.x)^2+(a.y-b.y)^2+(a.z-b.z)^2)^.5
end

function GetAncientPosition(team)
	if not utils.base_ancient then 
		utils.base_ancient = {}
		for i,v in ipairs(dota.FindEntitiesWithName("C_DOTA_BaseNPC_Fort")) do 
			utils.base_ancient[v:GetTeamIndex()] = Otrezok(dota.Vector(0,0,0),v:GetAbsOrigin(),450)
		end 
		if not utils.base_ancient[2] then 
			utils.base_ancient[2] = dota.Vector(-5920, -5352,0)
		end
	end 
	if utils.base_ancient[team] then 
		return utils.base_ancient[team]
	end
	return dota.Vector(5527, 4999,0) 
end

function GetFantanPosition(team) 
	if not utils.base_pose then 
		utils.base_pose = {}
		for i,v in ipairs(dota.FindEntitiesWithName("C_DOTA_Unit_Fountain")) do 
			utils.base_pose[v:GetTeamIndex()] = Otrezok(dota.Vector(0,0,0),v:GetAbsOrigin(),450)
		end 
		if not utils.base_pose[2] then 
			utils.base_pose[2] = dota.Vector(-7093, -6542,0)
		end
	end 
	if utils.base_pose[team] then 
		return utils.base_pose[team]
	end
	return dota.Vector(7015, 6534,0) 
end 


function GetPredictionVector(self,target,projecile_speed,delay,minus)
	if not self.x then 
		if type(delay)~="number" then 
			delay = SelfTargetAbilityTime(self,target,delay) - 0.033
		end 
		self = self:GetNetworkOrigin() 		
	end
	
	local target_abs = target:GetNetworkOrigin()	
	local target_speed, spirit_breaker = GetTrueMoveSpeed(target)
	if (IsRunning(target) or spirit_breaker) and CanMove(target) then
		local distance = GetDistance2D(self,target_abs)
		local rotR = GetRotR(target) + GetTurnRad(target)
		if IsFlying(target) then
			local prediction = target_speed * (distance/(projecile_speed * (1 - (target_speed/projecile_speed)^2)^.5) + delay) - (minus or 0)
			return dota.Vector(target_abs.x + prediction * cos(rotR), target_abs.y + prediction * sin(rotR), target_abs.z)
		else 			
			distance = floor(target_speed * (distance/(projecile_speed * (1 - (target_speed/projecile_speed)^2)^.5) + delay)) - (minus or 0)
			local pos = dota.Vector(target_abs.x + distance * cos(rotR), target_abs.y + distance * sin(rotR), target_abs.z)
			local step = floor(math.max(distance/10,50))		
			return StepDistance(step, distance, target_abs, rotR) or pos
		end 
	else
		return target_abs	
	end
end

function GetSimplePrediction(target,delay,minus,minus_ilde,false_if_blok)
	local target_abs = target:GetNetworkOrigin()
	local target_speed, spirit_breaker = GetTrueMoveSpeed(target)
	if (IsRunning(target) or spirit_breaker) and CanMove(target) then
		local rotR =  GetRotR(target) + GetTurnRad(target)	
		local preDistance = target_speed * delay - (minus or 0)
		if IsFlying(target) then
			return dota.Vector(target_abs.x + preDistance * cos(rotR), target_abs.y + preDistance * sin(rotR), target_abs.z)
		else
			local pos = dota.Vector(target_abs.x + preDistance * cos(rotR), target_abs.y + preDistance * sin(rotR), target_abs.z)
			local distance = floor(preDistance)
			local step = floor(math.max(distance/10,50))
			local blocking_pos = StepDistance(step, distance, target_abs, rotR)
			if blocking_pos then
				return not false_if_blok and blocking_pos
			else
				return pos
			end
		end
	else
		if minus_ilde and minus_ilde ~= 0 then
			local rotR =  GetRotR(target) + GetTurnRad(target)
			return dota.Vector(target_abs.x - minus_ilde * cos(rotR), target_abs.y - minus_ilde * sin(rotR), target_abs.z)
		else
			return target_abs
		end
	end
end

function GetSimpleFastPrediction(target,delay)
	local target_abs = target:GetNetworkOrigin()	
	if IsRunning(target) then
		local rotR = GetRotR(target)+GetTurnRad(target)
		local moveSpeed = target:GetMoveSpeed()
        return dota.Vector(target_abs.x + delay * moveSpeed * cos(rotR), target_abs.y + delay * moveSpeed * sin(rotR), target_abs.z)
	else
		return target_abs
	end
end

function StepDistance(step, distance, absp, rot)	
	for i = step,distance,step do
		local npos = dota.Vector(absp.x + i * cos(rot), absp.y + i * sin(rot), absp.z)
		if not dota.IsFreePosition(npos) then -- or GetDistance2D(npos,dota.Vector(-2306,1778,0))<300 then  
			return dota.Vector(absp.x + (i-step) * cos(rot), absp.y + (i-step) * sin(rot), absp.z)				
		end 
	end
end 

function GetTrueMoveSpeed(self)
	local custom_speed = self:GetMoveSpeed()
	local spirit_breaker = false

	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	for i = 0, count - 1 do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		if name=="modifier_invoker_ice_wall_slow_debuff" or name=="modifier_invoker_cold_snap" then	
			custom_speed = 100
		-- elseif name == "modifier_lich_sinister_gaze" then 
		-- 	custom_speed = 50 
		elseif name == "modifier_spirit_breaker_charge_of_darkness" then 
			spirit_breaker = true
		end
	end
	return custom_speed, spirit_breaker
end 


function InFront(self,distance,turn)
	local rotR = GetRotR(self)+(turn or 0)
	local pos = self:GetNetworkOrigin() 	
	return dota.Vector(pos.x+distance*cos(rotR),pos.y+distance*sin(rotR),pos.z)
end 

function Otrezok(to,from,distance)
	if not to.x then 
		to = to:GetAbsOrigin()
	end 
	if not from.x then 
		from = from:GetAbsOrigin()
	end 
	local dis = GetDistance2D(to,from)	
	return dota.Vector((to.x - from.x) * distance / dis +  from.x,(to.y - from.y) * distance / dis +  from.y,from.z)
end 

function TimeToCloseMove(self,target,range)
	local dis = GetHullDistance(self,target)
	if dis > range then
		local min_move_speed = math.max(50,self:GetMoveSpeed())
		return (dis-range)/min_move_speed*1000
	else
		return 0
	end
end 

function FindRelativeAngle(self,pos,diff)
	if not pos.x then pos = pos:GetNetworkOrigin() end
	--local rot = GetRotR(self) + (diff or 0)
	local angle = math.abs(math.deg(atan2(pos.y-self:GetNetworkOrigin().y,pos.x-self:GetNetworkOrigin().x) - GetRotR(self)))
	if angle >180 then
		angle = 360 -angle
	end
	return angle
end

function FindRelativeAngleN(self,pos)
	if not pos.x then pos = pos:GetNetworkOrigin() end
	local angle = math.deg(atan2(pos.y-self:GetNetworkOrigin().y,pos.x-self:GetNetworkOrigin().x) - GetRotR(self))
	if angle >180 then
		angle = angle - 360
	elseif angle < -180 then
		 angle = angle + 360
	end
	return angle
end

function FindRotationRadian(self,pos)
	if not pos.x then pos = pos:GetNetworkOrigin() end
	local angle = math.abs(atan2(pos.y-self:GetNetworkOrigin().y,pos.x-self:GetNetworkOrigin().x) - GetRotR(self))	
	if angle > pi then
        angle = (pi * 2) - angle
	end
    return angle
end

function GetRotR(self)
	return self:GetRotation().y*pi/180	
	--m_angAbsRotation
end


function IsTurn(self)
	return GetTurn(self)~=0
end

function GetTurnRadEx(self)
	return self:GetAngleDiff()*pi/180
end

function GetTurn(self)
	local turn = self:GetAngleDiff()
	if turn == 0 then 
		return 0 
	elseif turn > 0 then 
		return turn +24
	else 
		return turn -24
	end
end 

function GetTurnRad(self)
	return GetTurn(self)*pi/180
end 

function TurnRate(pos,self)
	local angle = FindRotationRadian(self,pos)
	if angle <0.279 then 
		return 16 
	else 
		return 30* (angle) + 16
	end 
end 

function GetTurnTime(self,pos)
	local name = self:GetName():removeNpc()
	if name == "pangolier" then return 0 end
	if not pos then
		return 700
	end

	local rate = ModifierTurnRate(self)
	if not pos.x then pos = pos:GetNetworkOrigin() end

	return TurnRate(pos,self)/rate
end 

function GetTurnTimeLatence(self,pos)
	local name = self:GetName():removeNpc()
	if name == "pangolier" then return 0 end 

	local rate = ModifierTurnRate(self)
	if not pos.x then pos = pos:GetNetworkOrigin() end

	local npos = self:GetNetworkOrigin()
	local rotR = GetRotR(self)
	if IsRunning(self) then		
		local del = (dota.GetLatency()+33)/1000
		local sp = self:GetMoveSpeed()
		npos = dota.Vector(npos.x + sp * del * cos(rotR), npos.y + sp* del * sin(rotR), npos.z)
	end 
	local angle = math.abs(atan2(pos.y-npos.y,pos.x-npos.x) - rotR)	
	if angle > pi then
        angle = (pi * 2) - angle
	end		
	angle = math.max(0,angle-0.279)
	if angle == 0 then 
		return 16
	else 
		return (30* (angle))/rate+33
	end 
	
end 

function ModifierTurnRate(self)
	local rate = GetTurnRateKV(self)
	local modifiers = self:GetModifierManager()
	local count = modifiers and modifiers:GetCurrentBuffCount() or 0
	if count == 0 then
		return rate
	end
	if HaveMod(modifiers,count,"modifier_item_phase_boots_active") and not IsRanged(self) then 
		rate = 1
	end 
	for i = count - 1,0,-1  do
		local buff = modifiers:GetBuffByIndex(i)
		local name = buff:GetName()
		-- if name == "modifier_medusa_mystic_snake_slow" then  --turn_slow
		-- 	local ab = buff:GetAbility()
		-- 	if ab then
		-- 		rate = rate*(ab:GetSpecialValue("turn_slow")/100)
		-- 	end
		if name == "modifier_batrider_sticky_napalm" then 
			local ab = buff:GetAbility()
			if ab then
				rate = rate*(100+ab:GetSpecialValue("turn_rate_pct"))/100
			end	
		elseif name == "modifier_faceless_void_time_zone_projectile_effect" then 
			local ab = buff:GetAbility()
			if ab then
				if buff:IsDebuff() then
					rate = rate*(100+ab:GetSpecialValue("bonus_turn_speed"))/100
				else
					rate = rate*(100-ab:GetSpecialValue("bonus_turn_speed"))/100
				end
			end	
		-- elseif name == "modifier_nevermore_shadowraze_debuff" then 
		-- 	local ab = buff:GetAbility()
		-- 	if ab then
		-- 		for z = 2, math.min(buff:GetStackCount(),9) do 					
		-- 			rate = rate*(100+ab:GetSpecialValue("turn_rate_pct"))/100
		-- 		end
		-- 	end
		-- elseif name == "modifier_spider_legs" then 
		-- 	rate = rate + rate*0.3
		elseif name == "modifier_broodmother_spin_web" then 
			local ab = buff:GetAbility()
			if ab then
				rate = rate + ab:GetSpecialValue("bonus_turn_rate")
			end
		-- elseif name == "modifier_rattletrap_jetpack" then 
		-- 	return 0
		end 
	end
	return rate
end 

function ToFace(self,target,ang,diff)
	if not target.x then target = target:GetNetworkOrigin() end
	local angel = FindRelativeAngle(self,target,diff)
	return angel >= (360-ang) or angel <= ang
end

function WillToFace(self,target,int)
	local turn = GetTurn(self)
	local angle = int or Enum.FACE_ANGLE
	if turn == 0 then 
		angle = angle + 4
	end
	return math.abs(FindRelativeAngleN(self,target) - turn) < angle
end 

function ToFaceAngel(self,target,diff)
	if not target.x then target = target:GetNetworkOrigin() end
	local angel = FindRelativeAngle(self,target,diff)
	if angel>180 then 
		return  360-angel
	end
	return angel
end

function CenterVector(a,b)
	return dota.Vector((a.x+b.x)/2,(a.y+b.y)/2,0)
end


--// Usefull vectors
function distToSegment(p, start, finish)
	if not p.x then p = p:GetAbsOrigin() end 
	if not start.x then start = start:GetAbsOrigin() end 
	if not finish.x then finish = finish:GetAbsOrigin() end 
	
	local function dist2(v, w)
		return (v.x - w.x)^2 + (v.y - w.y)^2
	end
		
	local l2 = dist2(start, finish)  
	if (l2 == 0) then  
		return dist2(p, start)
	end	
	local  t = ((p.x - start.x) * (finish.x - start.x) + (p.y - start.y) * (finish.y - start.y)) / l2
	t = math.max(0, math.min(1, t))	
	return (dist2(p, dota.Vector(start.x + t * (finish.x - start.x),start.y + t * (finish.y - start.y),0)))^.5
end

function closestPointToSegment(K, A, B, minDistance)

	if not K then K = dota.Get3dPositionUnderCursor() end 
	if not A.x then A = A:GetAbsOrigin() end 
	if not B.x then B = B:GetAbsOrigin() end 
    -- Вектор от A до B
    local AB = dota.Vector(B.x - A.x, B.y - A.y, 0)
    -- Вектор от A до K
    local AK = dota.Vector(K.x - A.x, K.y - A.y, 0)

    -- Скалярное произведение AB и AK
    local dotProduct = AB.x * AK.x + AB.y * AK.y
    -- Длина отрезка AB в квадрате
    local lengthSquared = AB.x * AB.x + AB.y * AB.y

    -- Проекция AK на AB, ограниченная от 0 до 1
    local t = math.max(0, math.min(1, dotProduct / lengthSquared))


    local closestPoint = dota.Vector(A.x + t * AB.x, A.y + t * AB.y, 0)

	if minDistance and GetDistance2D(closestPoint,A) < minDistance then
		-- Нормализация вектора AB
		local ABLength = math.sqrt(AB.x * AB.x + AB.y * AB.y)
		local normalizedAB = dota.Vector(AB.x / ABLength, AB.y / ABLength, 0)
		-- Смещение точки на минимальную дистанцию
		closestPoint = dota.Vector(A.x + normalizedAB.x * minDistance, A.y + normalizedAB.y * minDistance, 0)
    end
	return closestPoint
end

function PointInTriangle(pt, v1, v2, v3)

	local function trigle( p1, p2, p3)
		return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)
	end

	local d1 = trigle(pt, v1, v2)
	local d2 = trigle(pt, v2, v3)
	local d3 = trigle(pt, v3, v1)

	local has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
	local has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)

	return not (has_neg and has_pos)
end 

function CrossLine(A, B, C, D) 
	local dx1 = B.x - A.x
	local dy1 = B.y - A.y
	local dx2 = D.x - C.x
	local dy2 = D.y - C.y
	local x = dy1 * dx2 - dy2 * dx1
	if x == dx2 then
		return false
	end 
	local y = C.x * D.y - C.y * D.x
	x = ((A.x * B.y - A.y * B.x) * dx2 - y * dx1) / x
	y = (dy2 * x - y) / dx2
	return	((A.x <= x and B.x >= x) or (B.x <= x and A.x >= x)) and ((C.x <= x and D.x >= x) or (D.x <= x and C.x >= x))
end

function subVector(vec1,vec2)
	return dota.Vector(vec1.x-vec2.x,vec1.y-vec2.y,vec1.z)
end

function CheckVectors(a,b)
	return a.x==b.x and a.y==b.y
end

function RotatePosition(center,angel,vec_point) 
	local radi = math.rad(angel)
	local x1 = vec_point.x-center.x
	local y1 = vec_point.y-center.y
	return dota.Vector(cos(radi) * x1 - sin(radi) * y1 + center.x,sin(radi) * x1 + cos(radi) * y1 + center.y,vec_point.z)
end 

function VectorAngle(vec1, vec2)
    -- Calculate the dot product of the two vectors
    local dotProduct = vec1.x * vec2.x + vec1.y * vec2.y

    -- Calculate the magnitudes (lengths) of the vectors
    local magnitudeVec1 = math.sqrt(vec1.x^2 + vec1.y^2)
    local magnitudeVec2 = math.sqrt(vec2.x^2 + vec2.y^2)

    -- Avoid division by zero by checking if magnitudes are greater than zero
    if magnitudeVec1 == 0 or magnitudeVec2 == 0 then
        return 0
    end

    -- Calculate the cosine of the angle using the dot product formula
    local cosAngle = dotProduct / (magnitudeVec1 * magnitudeVec2)

    -- Clamp the cosine value between -1 and 1 to avoid rounding errors
    cosAngle = math.max(-1, math.min(1, cosAngle))

    -- Calculate the angle in radians and convert it to degrees
    local angleRadians = math.acos(cosAngle)
    local angleDegrees = math.deg(angleRadians)

    return angleDegrees
end

function ParabolaOffset(base, from, to, progress, maxOffset)
    if not to.x then to = to:GetAbsOrigin() end
    if not from.x then from = from:GetAbsOrigin() end

    -- направление движения
    local dir = dota.Vector(to.x - from.x, to.y - from.y, 0)
    -- нормаль влево
    local norm = dota.Vector(-dir.y, dir.x, 0):Normalize()
    -- параболический множитель (0 в начале/конце, 1 в середине)
    local factor = 4 * progress * (1 - progress)

    return dota.Vector(
        base.x + norm.x * maxOffset * factor,
        base.y + norm.y * maxOffset * factor,
        base.z
    )
end

function VecNormalize(v)
    local len = math.sqrt(v.x * v.x + v.y * v.y)
    if len == 0 then
        return dota.Vector(0, 0, 0)
    end
    return dota.Vector(v.x / len, v.y / len, 0)
end


--// Combo function 
function CloseToMouse(self,tab,range,ignore_immune,maxDistance,custom)
	if not maxDistance then maxDistance = Enum.MAX_MOUSE_RANGE end
	if not custom then custom = {} end
	local mouse = dota.Get3dPositionUnderCursor()
	local team = self:GetTeamIndex()
	local enemy = {}	
	for i,v in ipairs(tab) do
		if not v:IsTeam(team) and v:IsVisible() and v:IsAlive()
			and GetDistance2D(v,mouse)<maxDistance 
			and GetDistance2D(self,v) < range 
			and (IsBearName(v:GetBaseClassName()) 
				or IsRealOrMeepo(v)
				or (custom.arc and (FindModifier(v,"modifier_arc_warden_tempest_double") or FindModifier(v,"modifier_vengefulspirit_hybrid_special"))) )		
			and (not ignore_immune or not IsMagicImmuneOld(v)) 
			and (not custom.no_invule or not IsInvul(v))
			and (not custom.no_untarget or not IsUntargetable(v))
			and (custom.no_priority or not IsLowPriority(v,true))
			and OnScreen(v:GetAbsOrigin()) then
			enemy[#enemy+1] = v
		end
	end
	if #enemy > 1 then
		table.sort( enemy, function (a,b) return GetDistance2D(mouse,a) < GetDistance2D(mouse,b) end)		
	end
	return enemy[1]	
end

function CloseToMouseAlly(self,tab,range,ignore_immune,maxDistance,custom)
	local on_screen = true 
	if not maxDistance then maxDistance = Enum.MAX_MOUSE_RANGE on_screen = false end
	if not custom then custom = {} end
	local mouse = dota.Get3dPositionUnderCursor()
	local my_hero = dota.GetLocalHero():GetIndex()
	local team = self:GetTeamIndex()
	local enemy = {}
	for i,v in ipairs(tab) do
		if v:IsTeam(team) and not v:IsMe(my_hero) and v:IsVisible() and v:IsAlive() and IsRealOrMeepo(v)
			and GetDistance2D(v,mouse)<maxDistance and GetDistance2D(self,v) < range 
			and (not custom.min_range or GetDistance2D(self,v)>custom.min_range) 
			and (not ignore_immune or not IsMagicImmuneOld(v)) 
			and not IsLowPriority(v) 
			and (on_screen or OnScreen(v:GetAbsOrigin())) then
			enemy[#enemy+1] = v
		end
	end
	if #enemy > 1 then
		table.sort( enemy, function (a,b) return GetDistance2D(mouse,a) < GetDistance2D(mouse,b) end)		
	end
	return enemy[1]	
end

function CloseToHero(self,tab,range,immune,ignore)
	local enemy = {}
	local team = self:GetTeamIndex()	
	for i,v in ipairs(tab) do
		if not v:IsTeam(team) and v:IsVisible() and v:IsAlive() 
		and IsRealOrMeepo(v) and GetDistance2D(self,v) < range 
		and (not immune or not IsMagicImmuneOld(v)) and not IsLowPriority(v,true) 
		and (not ignore or v:GetIndex()~=ignore) then
			enemy[#enemy+1] = v
		end
	end
	if #enemy > 1 then
		table.sort( enemy, function (a,b) return GetDistance2D(self,a) < GetDistance2D(self,b) end)		
	end
	return enemy[1]
end

function LowestHp(self,tab,range,ignore_immune,maxDistance,custom)
	if not maxDistance then maxDistance = Enum.MAX_MOUSE_RANGE end
	if not custom then custom = {} end
	local mouse = dota.Get3dPositionUnderCursor()
	local team = self:GetTeamIndex()
	local enemy = {}	
	for i,v in ipairs(tab) do
		if not v:IsTeam(team) and v:IsVisible() and v:IsAlive()
			and GetDistance2D(v,mouse)<maxDistance 
			and GetDistance2D(self,v) < range 
			and (IsBearName(v:GetBaseClassName()) 
				or IsRealOrMeepo(v)
				or (custom.arc and (FindModifier(v,"modifier_arc_warden_tempest_double") or FindModifier(v,"modifier_vengefulspirit_hybrid_special"))) )		
			and (not ignore_immune or not IsMagicImmuneOld(v)) 
			and (not custom.no_invule or not IsInvul(v))
			and (not custom.no_untarget or not IsUntargetable(v))
			and (custom.no_priority or not IsLowPriority(v,true))
			and OnScreen(v:GetAbsOrigin()) then
			enemy[#enemy+1] = v
		end
	end
	if #enemy > 1 then
		table.sort( enemy, function (a,b) return a:GetHealth() < b:GetHealth() end)		
	end
	return enemy[1]	
end


--// Dodge Evade function 
function FindAOE(unitsAround, radius, time)  --// enigma black hole
    if not unitsAround or #unitsAround <= 0 then return {false,0} end
    local enemyNum = #unitsAround
	if enemyNum == 1 then return {GetSimplePrediction(unitsAround[1],time),1} end

	local maX = 1
	local bestPos = unitsAround[1]:GetNetworkOrigin()
	for i = 1, enemyNum-1 do
		for j = i+1, enemyNum do
			if unitsAround[i] and unitsAround[j] then
				local pred1 = GetSimplePrediction(unitsAround[i],time)
				local pred2 = GetSimplePrediction(unitsAround[j],time)
				local center = CenterVector(pred1,pred2)

				local count = 0
				for z = 1, enemyNum do
					if GetDistance2D(unitsAround[z], center)<radius then
						count = count + 1
					end
				end

				if count > maX then
					maX = count
					bestPos = center
				end

			end
		end
	end
	return {bestPos,maX}
end

function FindAoeNear(target, unitsAround, radius, time)
	if not unitsAround or #unitsAround <= 0 then return {false,0} end
	if #unitsAround == 1 then return {GetSimplePrediction(unitsAround[1],time),1} end

	local pred_pos = GetSimplePrediction(target,time)
	local end_tab = {}

	for i,v in ipairs(unitsAround) do 
		local pred1 = GetSimplePrediction(v,time)
		if GetDistance2D(pred1,pred_pos)<radius*2 then
			end_tab[#end_tab+1] = pred1
		end		
	end 
	
	local function GetCenterVector(tarb)
		local totalX,totalY = 0,0
		for i,v in ipairs(tarb) do
			totalX = totalX + v.x
			totalY = totalY + v.y
		end
		return  dota.Vector(totalX / #tarb,totalY / #tarb, pred_pos.z)
	end
	
	local vec = GetCenterVector(end_tab)	
	local new_f = table.filter(end_tab,function(x) return GetDistance2D(x,vec)<radius end)	
	vec = GetCenterVector(new_f)
	return {vec,#new_f}
end 

function FindAOESpell(self,target,tab,spell,pierce_mgc)
	local pos = self:GetAbsOrigin()
	local selftexm = self:GetTeamIndex()
	local targetIdx = target:GetIndex()
	local enemy = table.filter(tab,function(x) 
		return (targetIdx==x:GetIndex()) or (x:IsAlive() and x:IsVisible() and not IsInvul(x) and not x:IsTeam(selftexm) 
		and not x:IsIllusion() and	(pierce_mgc or not IsMagicImmune(x)) and GetDistance2D(x,pos)<spell:GetCastRange()+spell:GetAOERadius() -25)
	end)
	if #enemy> 0 then
		local best = FindAoeNear(target, enemy,spell:GetAOERadius()-25,RealCastPoint(spell)+dota.GetLatency()/1000+0.1)
		if best[1] then
			return best[1],best[2]
		else 
			return nil,0
		end 
	end 
end

function FindLinearOld(self,units,range,width,infront,target)  --// shaker fissure
	local MyPos = self:GetNetworkOrigin()
	local Vec = InFront(self,range)
	local best
	local numbers = 0	
	local maxdis = 50
	for i = 0,360,10 do
		local Vec1 = RotatePosition(MyPos, i, Vec)
		local count = 0
		
		local new_my = MyPos		
		if infront then 
			new_my = Otrezok(Vec1,MyPos,width)
		end 
		
		if not target or distToSegment(target,new_my,Vec1)<width+24 then
			for i,v in ipairs(units) do
				local H = distToSegment(v,new_my,Vec1) < width+24
				if H then
					maxdis = math.max(maxdis,GetDistance2D(MyPos,v)+48)
					count = count + 1
				end
			end
		end
		
		if numbers <= count then						
			numbers = count
			best = Vec1					
		end
	end
	if best then --limed dstance (example sk Q)
		best = dota.Vector((best.x - MyPos.x) * maxdis / GetDistance2D(best,MyPos) + MyPos.x,(best.y - MyPos.y) * maxdis / GetDistance2D(best,MyPos) +  MyPos.y,0)
	end 
	return best,numbers
end 

function FindLinearNear(self,target,enemy,info,time) 
	local MyPos = self:GetNetworkOrigin()
	local Vec = InFront(self,info.range)
	local best_pos
	local best_count = 0
	local best_dist = 0
	local maxdis = 50
	local new_array_enemy = table.map(enemy,function(x) return (info.speed and GetPredictionVector(self,x,info.speed,time,25) or GetSimplePrediction(x,time)) end)

	for i = 0,360,10 do
		local Vec1 = RotatePosition(MyPos, i, Vec)
		local count = 0
		local dist_sum = 0
		
		local new_my = MyPos		
		
		if info.line then
			if not target or distToSegment(target,new_my,Vec1)<info.width-5 then
				for z,v in ipairs(new_array_enemy) do
					local H = distToSegment(v,new_my,Vec1)
					if H < info.width then
						maxdis = math.max(maxdis,GetDistance2D(MyPos,v))
						dist_sum = dist_sum + H
						count = count + 1
					end
				end
			end
		else 
			for z, v in ipairs(new_array_enemy) do
                local distance = GetDistance2D(MyPos, v)
				local vec1 = subVector(Vec1,MyPos)
				local vec2 = subVector(v,MyPos)
                local angleToEnemy = math.abs(VectorAngle(vec1,vec2))
                -- Check if within cone range and angle
                if distance <= info.range and angleToEnemy <= info.coneAngle / 2 then
                    maxdis = math.max(maxdis, distance)
                    count = count + 1
                end
            end
		end 

		if best_count < count or (best_count == count and dist_sum <= best_dist) then							
			best_count = count
			best_pos = Vec1					
			best_dist = dist_sum					
		end
	end
	if best_pos then --limed dstance (example sk Q)
		best_pos = dota.Vector((best_pos.x - MyPos.x) * maxdis / GetDistance2D(best_pos,MyPos) + MyPos.x,(best_pos.y - MyPos.y) * maxdis / GetDistance2D(best_pos,MyPos) +  MyPos.y,0)
	end 
	return best_pos,best_count
end 

function FindLinearSpell(self,target,tab,spell,info)
	local pos = self:GetAbsOrigin()
	local selftexm = self:GetTeamIndex()
	local targetIdx = target and target:GetIndex() or 0
	local enemy = table.filter(tab,function(x) 
		return (targetIdx ~= 0 and targetIdx==x:GetIndex()) or (x:IsAlive() and x:IsVisible() and not IsInvul(x) and not x:IsTeam(selftexm)
		and IsRealOrMeepo(x) and (info.viaMgc or not IsMagicImmune(x)) and GetDistance2D(x,pos)<info.range)
	end)
	if #enemy> 0 then
		local best,numbers = FindLinearNear(self, target, enemy, info, RealCastPoint(spell)+dota.GetLatency()/1000+0.1)
		if best then
			return best,numbers
		else 
			return nil,0
		end 
	end 
end


function DisableAutoAttack(num)
	if not num then num = 0	end

	if not utils.auto_attack[num] and dota.GetAttackMode() ~= num then
		if num == 1 and dota.GetAttackMode() == 0 then 
			return true 
		end
		utils.auto_attack[num] = dota.GetAttackMode()
		dota.SetVarInt("dota_player_units_auto_attack_mode",num)
		return false
	else 
		return true
	end 
end

function EnableAutoAttack(num)
	if not num then num = 0	end

	if utils.auto_attack[num] then
		dota.SetVarInt("dota_player_units_auto_attack_mode",utils.auto_attack[num])
		utils.auto_attack[num] = nil		
	end
end 


--// OS functions
function ReadLines(sPath)
	local file = io.open(sPath, "r")	
	if file then
		local tLines = {}
		for line in file:lines() do
			table.insert(tLines, line)
		end
		file:close()
		return tLines
	else
		WriteLines(sPath,{})
		return {}
	end
end

function WriteLines(sPath, tLines)
	local file = io.open(sPath, "w")
	if file then
		local text = ""
		for _, sLine in ipairs(tLines) do
			text = text..sLine.."\n"
		end
		file:write(text)
		file:close()
	end
end

function split(str, pat)
   local t = {}
   local fpat = "(.-)" .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= "" then
         table.insert(t,cap)
      end
      last_end = e+1
      s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
      cap = str:sub(last_end)
      table.insert(t, cap)
   end
   return t
end


--// Trash functions
function SecondsToClock(seconds,need_mili)
  local seconds = tonumber(seconds)
  if seconds <= 0 then
    return "0:00";
  else
    local mins = string.format("%2.f", floor(seconds/60))
    local secs = string.format("%02.f", floor(seconds - mins *60))
	if need_mili then 
		local mili = (tostring(seconds):find("%.") or 000)
		return mins..":"..secs..":"..tostring(seconds):sub((mili+1),(mili+2))
	else 
		return mins..":"..secs
	end
  end
end

function PrintFlags(self)
	local temp = ""
	for i = Enum.MODIFIER_STATE_ROOTED, Enum.MODIFIER_STATE_LAST-1 do 
		if self:HasFlag(i) then 
			temp = temp..","..i
		end 
	end 
	return temp
end

function PrintFlagsUnit(self)
	local temp = ""
	for i = Enum.MODIFIER_STATE_ROOTED, Enum.MODIFIER_STATE_LAST-1 do 
		if HasFlag(self:GetUnitType(),2^i) then 
			temp = temp..","..i
		end 
	end 
	return temp
end


g_allow_ping = true
function Ping(vec)
	if g_allow_ping and vec and vec.x and vec.y and not ObsByPass() then
		dota.PingMinimapAtLocation(vec.x,vec.y,vec.z)
	end
end

--// Msg
local cMsg = {text = nil,time = nil,color = nil,img = nil,img_rect = nil,hex = nil,pos = nil,enable = true}
local fontCenter = dota.CreateFont("Garamond", math.min(54*rate,65),720)

function Msg(text,color,img,visible_time, no_border)	
	if not cMsg.enable then 
		return
	end
	if not cMsg.text then 
		cMsg.text = DrawText(screenSize.x/2.05,screenSize.y/7,Enum.WHITE,"",fontCenter,true)
		cMsg.text.center = true 
	end
	text = tostring(text):upper()
	cMsg.text.color = color or Enum.WHITE
	cMsg.text.text = text
	cMsg.border = no_border
	if img then 		
		local h_size = 50*rate
		local star = img:sub(1,5)
		local size = (star == "item_" and 65*rate) or (star == "npc_d" and 75*rate) or h_size

		if star == "npc_d" and tonumber(img:sub(-1)) then --nevemore img bug
			img = img:sub(1,#img-1)
		end

		if not cMsg.img then 		
			local text_size_y = (h_size - dota.GetTextSize(fontCenter,text).y)/2
			cMsg.img = DrawRound(cMsg.text.x+dota.GetTextSize(fontCenter,text).x/1.8,cMsg.text.y-text_size_y,size,h_size,0xffffffee,GetTextureId2(img),true,8*rate)
		else 
			cMsg.img.color = 0xffffffee
			cMsg.img.w = size
			cMsg.img.textureId = GetTextureId2(img)
			cMsg.img.x = cMsg.text.x+dota.GetTextSize(fontCenter,text).x/1.8
		end 
	elseif cMsg.img then 
		cMsg.img.visible = false
		cMsg.img= nil
	end 
	local tick = dota.GetTick()
	cMsg.time = tick + (visible_time or 1750)
	cMsg.sleep = tick+30
	cMsg.hex = num2hex(color or Enum.WHITE):sub(1,6)
end	

function ShowMsg(tick)
	if cMsg.time then 
		local time_central = cMsg.time - tick
		if cMsg.img and IsMouseOnButton(cMsg.img) and time_central < 1500 then 
			cMsg.text.visible = false
			cMsg.text = nil
			cMsg.time = nil
			if cMsg.img then 
				cMsg.img.visible = false
				cMsg.img= nil
			end			
		else 
			if time_central < 500 then
				if time_central < 50 then
					cMsg.text.visible = false
					cMsg.text = nil
					cMsg.time = nil
					if cMsg.img then 
						cMsg.img.visible = false
						cMsg.img= nil
					end					
				elseif tick > cMsg.sleep then 
					cMsg.text.color = tonumber("0x"..cMsg.hex..floor(time_central/10*2))
					if cMsg.img and time_central < 250 then 
						cMsg.img.color = tonumber("0xffffff"..floor(time_central/10*2))
					end 
					cMsg.sleep = tick+30
				end 
			end	
		end
	end 
end 

function EnableCenterMessage(state)
	cMsg.enable = state
end 


--// SideMag
local fontSide = dota.CreateFont("Arial",15*rateY,800*rateY)

sideMessage = {}
sideMessage.id = 0
sideMessage.objs = {}

function sideMessage:CreateMessage(img1, typ, img2, txt, timer, show_time)
	if #sideMessage.objs > 12 then return end
	typ = typ or Enum.SIDE_MSG_WITH_ARROW

	local text = txt
	local hasText = text ~= nil and text ~= ""

	local paddingX, paddingY = 10 * rateY, 8 * rateY
	local spacing = 6 * rateY

	local img1_w, img1_h = 48 * rateY, 28 * rateY
	local img2_w, img2_h = 38 * rateY, 28 * rateY
	local icon_w = 25 * rateY

	if typ == Enum.SIDE_MSG_SMALL then
		img1_w, img1_h, img2_w, img2_h, icon_w = 20*rateY, 20*rateY, 20*rateY, 20*rateY, 20*rateY
	end

	local text_w, text_h = 0, 0
	if hasText then
		local txt_size = dota.GetTextSize(fontSide, text)
		text_w, text_h = txt_size.x, txt_size.y
	end

	local max_h = math.max(img1_h, img2_h, text_h) + paddingY * 2
	local total_w

	if typ == Enum.SIDE_MSG_SMALL then
		local content_w = img1_w + (img2 and (spacing + img2_w) or 0) + (hasText and (spacing + text_w) or 0)
		total_w = math.max(content_w + paddingX * 2, 150 * rateY)
	else
		total_w = img1_w + spacing
		if typ <= Enum.SIDE_MSG_TP then
			total_w = total_w + icon_w + spacing
		elseif hasText then
			total_w = total_w + text_w + spacing
		end
		if img2 then
			total_w = total_w + img2_w + spacing
		end
		total_w = math.max(total_w + paddingX * 2, 150 * rateY)
	end

	local data = {
		w = total_w,
		h = max_h,
		full_time = show_time or 3500,
		createTick = dota.GetTick(),
		elements = {},
	}

	local function centerY(h)
		return screenSize.y * 0.59 + (max_h - h) / 2
	end

	data.bg = DrawRect(screenSize.x, screenSize.y * 0.59, total_w, max_h, 0x999999cc, GetTextureId("NtUI/vgui/dashboard/dash_button_drop_bg.png"), true)

	local x = data.bg.x + paddingX

	if typ == Enum.SIDE_MSG_SMALL then
		local total_elements_w = img1_w + (img2 and (spacing + img2_w) or 0) + (hasText and (spacing + text_w) or 0)
		x = data.bg.x + (total_w - total_elements_w) / 2
	end

	--Главная
	data.elements[#data.elements + 1] = {
		obj = DrawRound(x, centerY(img1_h), img1_w, img1_h, 0xffffffee, GetTextureId2(img1,typ == Enum.SIDE_MSG_SMALL), true, 8 * rateY),
		relPos = dota.Vector(x - data.bg.x, centerY(img1_h) - data.bg.y, 0)
	}

	x = x + img1_w + spacing

	--Иконка
	if typ < Enum.SIDE_MSG_TP then
		local icon_t = {}
		icon_t[Enum.SIDE_MSG_BUY] 		= {path="panorama/images/hud/facets/icons/gold_png.vtex_c",size=icon_w,color=0xD3D3D3ff}
		icon_t[Enum.SIDE_MSG_WITH_ARROW] = {path="panorama/images/control_icons/arrow_chevron_gradient_png.vtex_c",size=icon_w}
		icon_t[Enum.SIDE_MSG_WITH_KILL] 	= {path="panorama/images/pings/ping_world_skullbones_psd.vtex_c",size=icon_w*1.5}
		icon_t[Enum.SIDE_MSG_WITH_CAST] 	= {path="panorama/images/events/crownfall/tokens/initiator_png.vtex_c",size=icon_w,color=0xD3D3D3ff}
		local x_icon = (data.bg.x + total_w / 2) - icon_t[typ].size / 2 + 5*rateY
		data.elements[#data.elements + 1] = {
			obj = DrawRect(x_icon, centerY(icon_t[typ].size), icon_t[typ].size, icon_t[typ].size, icon_t[typ].color or 0xffffffff, GetTextureId2(icon_t[typ].path), true),
			relPos = dota.Vector(x_icon - data.bg.x, centerY(icon_t[typ].size) - data.bg.y, 0)
		}
		x = x + icon_w + spacing
	end

	--Текст
	if hasText then
		if typ == Enum.SIDE_MSG_TP and img2 then
			local x_text = (data.bg.x + total_w / 2) - text_w / 2 + 5*rateY
			data.elements[#data.elements + 1] = {
				obj = DrawText(x_text, centerY(text_h), 0xffffffee, text, fontSide, true),
				relPos = dota.Vector(x_text - data.bg.x, centerY(text_h) - data.bg.y, 0)
			}
		else
			data.elements[#data.elements + 1] = {
				obj = DrawText(x, centerY(text_h), 0xffffffee, text, fontSide, true),
				relPos = dota.Vector(x - data.bg.x, centerY(text_h) - data.bg.y, 0)
			}
			x = x + text_w + spacing
		end
	end

	--Доп Картинка
	if img2 then
		local x_img2 = (typ == Enum.SIDE_MSG_SMALL and x) or data.bg.x + total_w - paddingX - img2_w
		data.elements[#data.elements + 1] = {
			obj = DrawRound(x_img2, centerY(img2_h), img2_w, img2_h, 0xffffffee, GetTextureId2(img2), true, 8 * rateY),
			relPos = dota.Vector(x_img2 - data.bg.x, centerY(img2_h) - data.bg.y, 0)
		}
	end

	-- draw timer if needed
	data.timer = timer
	if timer then
		data.elements[#data.elements + 1] = {
			obj = DrawRound(data.bg.x + paddingX, data.bg.y + data.h - 2 * rateY, data.w - 2 * paddingX, 2 * rateY, 0xffffffee, -1, true, 6 * rateY),
			relPos = dota.Vector(11 * rateY, data.h - 6 * rateY, 0),
			w = data.w - 2 * paddingX
		}
		data.bg.h = data.bg.h + 6 * rateY
	end

	function data:Destroy(i)

		local height = data.bg.h + 2 * rateY
		data.bg.visible = false
		data.bg = nil
		for i, v in ipairs(data.elements) do
			v.obj.visible = false
			v.obj = nil
		end
		table.remove(sideMessage.objs, i)
		-- Сдвигаем все ниже стоящие сообщения вверх
		for j = 1, i - 1 do
			if sideMessage.objs[j] then
				sideMessage.objs[j]:ShiftVec(dota.Vector(0, height, 0))
			end
		end
		data = nil
	end

	function data:ShiftVec(vec)
		data.bg.x = data.bg.x + vec.x
		data.bg.y = data.bg.y + vec.y
		for i, v in ipairs(data.elements) do
			v.obj.x = v.obj.x + vec.x
			v.obj.y = v.obj.y + vec.y
		end
	end

	function data:SetX(x)
		data.bg.x = x
		for i, v in ipairs(data.elements) do
			v.obj.x = v.relPos.x + x
		end
	end

	function data:SetVisible()
		local shop_open = not dota.IsHUDFlipped() and dota.IsShopOpen()
		data.bg.visible = not shop_open
		for i, v in ipairs(data.elements) do
			v.obj.visible = not shop_open
		end
	end

	function data:SetTimer(tick)
		if data.timer then
			local timer = data.elements[#data.elements]
			timer.obj.w = math.max(0, timer.w * (1 - (tick - data.createTick) / data.timer / 1000))
			timer.obj.visible = timer.obj.visible and timer.obj.w > 0
		end
	end

	sideMessage.objs[#sideMessage.objs + 1] = data

	for i, v in ipairs(sideMessage.objs) do
		if v then
			v:ShiftVec(dota.Vector(0, -data.bg.h - 2 * rateY, 0))
		end
	end

	return data
end

local function easeOutQuart(x)
	return 1 - math.pow(1 - x, 4)
end

function ShowSideMsg(tick)
	for i,v in ipairs(sideMessage.objs) do
		if v then
			v:SetVisible()
			v:SetTimer(tick)
			local span = tick - v.createTick
			if span < 350 then
				v:SetX(screenSize.x - (v.w-1) * easeOutQuart(span/350))
			elseif span < 350 + v.full_time then
				v:SetX(screenSize.x - v.w + 1)
			elseif span < 350 + v.full_time + 350 then
				v:SetX(screenSize.x - (v.w-1) * easeOutQuart((350 + v.full_time + 350 - span)/350))
			else
				v:Destroy(i)
				break	   
			end
		end
	end
end 


--// HudError 
function HudError(reason,sleep_time)
	if sleep_time then 
		if not SleepCheck("h_error"..reason) then 
			return 
		else 
			Sleep(sleep_time,"h_error"..reason)
		end 
	end
	dota.SendHudError(reason)
end 


function utilsReload()
	
	EnableAutoAttack(0)
	EnableAutoAttack(1)
	EnableAutoAttack(2)
	utils.auto_attack = {}
	
	RefreshCast()
	kvCache = {}
	
	for i,v in ipairs(sideMessage.objs) do
		v:Destroy(i)
	end
		
	sideMessage.id = 0
	sideMessage.objs = {} 
	collectgarbage("collect")
end	


--//= Not Used 
-- Other functions
function HandleToIndex(hand)
	local mask = bit.lshift(1, 14)-1
	return bit.band(hand,mask)
end 

function FrameControle()
	if cMsg and cMsg.time then ShowMsg(dota.GetTick()) end 
end 
