local neutral = 300 local friend = myHero.team local foe = neutral - friend local mathhuge = math.huge local mathsqrt = math.sqrt local mathpow = math.pow local function GetDistanceSqr(Pos1, Pos2) local Pos2 = Pos2 or myHero.pos local dx = Pos1.x - Pos2.x local dz = (Pos1.z or Pos1.y) - (Pos2.z or Pos2.y) return dx^2 + dz^2 end local function GetDistance(Pos1, Pos2) return mathsqrt(GetDistanceSqr(Pos1, Pos2)) end local function GetDistance2D(p1,p2) local p2 = p2 or myHero return mathsqrt(mathpow((p2.x - p1.x),2) + mathpow((p2.y - p1.y),2)) end local function GetTarget(range) local target = nil if _G.EOWLoaded then target = EOW:GetTarget(range) elseif _G.SDK and _G.SDK.Orbwalker then target = _G.SDK.TargetSelector:GetTarget(range) else target = GOS:GetTarget(range) end return target end local function EnableOrb(bool) if _G.EOWLoaded then EOW:SetMovements(bool) EOW:SetAttacks(bool) elseif _G.SDK and _G.SDK.Orbwalker then _G.SDK.Orbwalker:SetMovement(bool) _G.SDK.Orbwalker:SetAttack(bool) else GOS.BlockMovement = not bool GOS.BlockAttack = not bool end end local function ClosestMinion(range,team) local bestMinion = nil local closest = math.huge for i = 1, Game.MinionCount() do local minion = Game.Minion(i) if GetDistance(minion.pos) < range and minion.team == team then local Distance = GetDistance(minion.pos, mousePos) if Distance < closest then bestMinion = minion closest = Distance end end end return bestMinion end local function ClosestHero(range,team) local bestHero = nil local closest = mathhuge for i = 1, Game.HeroCount() do local hero = Game.Hero(i) if GetDistance(hero.pos) < range and hero.team == team and not hero.dead then local Distance = GetDistance(hero.pos, mousePos) if Distance < closest then bestHero = hero closest = Distance end end end return bestHero end --[""] = {Q = , W = , E = , R = }, local ranges = { ["Aatrox"] = {Q = 650, W = 1, E = 100, R = 1}, ["Ahri"] = {Q = 880, W = 700, E = 975, R = 450}, ["Alistar"] = {Q = 365, W = 650, E = 350, R = 1}, ["Amumu"] = {Q = 1100, W = 300, E = 350, R = 550}, ["Anivia"] = {Q = 1075, W = 1000, E = 650, R = 750}, ["Annie"] = {Q = 625, W = 625, E = 1, R = 600}, ["Ashe"] = {Q = 1, W = 1200, E = mathhuge, R = mathhuge}, ["Blitzcrank"] = {Q = 925, W = 1, E = 1, R = 1}, ["Brand"] = {Q = 1050, W = 900, E = 625, R = 750}, ["Braum"] = {Q = 1000, W = 650, E = 1, R = 1250}, ["Caitlyn"] = {Q = 1250, W = 800, E = 750, R = 3000}, ["Chogath"] = {Q = 950, W = 650, E = 1, R = 175 + myHero.boundingRadius}, ["Darius"] = {Q = 425, W = 1, E = 535, R = 460}, ["DrMundo"] = {Q = 925, W = 325, E = 1, R = 1}, ["Fizz"] = {Q = 550, W = 1, E = 730, R = 1300}, ["Galio"] = {Q = 825, W = 350, E = 650, R = 5500}, ["Garen"] = {Q = 1, W = 1, E = 325, R = 400}, ["Heimerdinger"] = {Q = 350, W = 1325, E = 970, R = 1}, ["Illaoi"] = {Q = 850, W = 1, E = 900, R = 450}, ["Jax"] = {Q = 700, W = 1, E = 300, R = 1}, ["Karma"] = {Q = 950, W = 675, E = 800, R = 1}, ["Kayle"] = {Q = 650, W = 900, E = 525, R = 900}, ["Kayn"] = {Q = 350, W = 900, E = 1, R = 750}, ["KogMaw"] = {Q = 1175, W = 1, E = 1280, R = 1200}, ["Lulu"] = {Q = 925, W = 650, E = 650, R = 900}, ["Maokai"] = {Q = 600, W = 525, E = 1100, R = 3000}, ["MasterYi"] = {Q = 600, W = 1, E = 1, R = 1}, ["MissFortune"] = {Q = 650, W = 1, E = 1000, R = 1400}, ["Morgana"] = {Q = 1175, W = 900, E = 800, R = 625}, ["Nami"] = {Q = 875, W = 725, E = 800, R = 2750}, ["Nunu"] = {Q = 125, W = 700, E = 550, R = 650}, ["Olaf"] = {Q = 1000, W = 1, E = 325, R = 1}, ["Pantheon"] = {Q = 600, W = 600, E = 600, R = 5500}, ["Poppy"] = {Q = 430, W = 400, E = 425, R = 1900}, ["Riven"] = {Q = 260, W = 125, E = 325, R = 900}, ["Ryze"] = {Q = 1000, W = 615, E = 615, R = 3000}, ["Singed"] = {Q = 1, W = 1000, E = 125, R = 1}, ["Sivir"] = {Q = 1250, W = 1, E = 1, R = 1}, ["Sona"] = {Q = 825, W = 1000, E = 430, R = 900}, ["Soraka"] = {Q = 800, W = 550, E = 925, R = 1}, ["TahmKench"] = {Q = 800, W = 250, E = 1, R = mathhuge}, ["Thresh"] = {Q = 1100, W = 950, E = 400, R = 450}, ["Tryndamere"] = {Q = 1, W = 850, E = 660, R = 1}, ["Viktor"] = {Q = 600, W = 700, E = 1025, R = 700}, ["Vladimir"] = {Q = 600, W = 150, E = 600, R = 700}, ["Volibear"] = {Q = 1, W = 400, E = 425, R = 500}, ["Warwick"] = {Q = 350, W = 4000, E = 375, R = 25000}, ["XinZhao"] = {Q = 1, W = 900, E = 650, R = 550}, ["Yorick"] = {Q = 1, W = 600, E = 700, R = 600}, ["Ziggs"] = {Q = 1400, W = 1000, E = 900, R = 5300}, ["Zilean"] = {Q = 900, W = 1, E = 550, R = 900}, } local delays = { ["Aatrox"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Ahri"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Alistar"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Amumu"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Anivia"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Annie"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Ashe"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Blitzcrank"] = {Q = 0.25, W = 0.1, E = 0.25, R = 0.25}, ["Brand"] = {Q = 0.25, W = 0.625, E = 0.25, R = 0.25}, ["Braum"] = {Q = 0.25, W = 0.1, E = 0.1, R = 0.5}, ["Caitlyn"] = {Q = 0.625, W = 0.25, E = 0.25, R = 0.25}, ["Chogath"] = {Q = 0.5, W = 0.5, E = 0.25, R = 0.25}, ["Darius"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["DrMundo"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Fizz"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Galio"] = {Q = 0.25, W = 0.25, E = 0.45, R = 0.25}, ["Garen"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Heimerdinger"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Illaoi"] = {Q = 0.75, W = 0.25, E = 0.25, R = 0.25}, ["Jax"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Karma"] = {Q = 0.25, W = 0.25, E = 0.1, R = 0.1}, ["Kayle"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Kayn"] = {Q = 0.15, W = 0.55, E = 0.25, R = 0.25}, ["KogMaw"] = {Q = 0.25, W = 0.1, E = 0.25, R = 0.85}, ["Lulu"] = {Q = 0.25, W = 0.25, E = 0.1, R = 0.25}, ["Maokai"] = {Q = 0.375, W = 0.25, E = 0.25, R = 0.25}, ["MasterYi"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["MissFortune"] = {Q = 0.25, W = 0.25, E = 0.5, R = 0.01}, ["Morgana"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Nami"] = {Q = 0.75, W = 0.25, E = 0.1, R = 0.5}, ["Nunu"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Olaf"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Pantheon"] = {Q = 0.25, W = 0.25, E = 0.389, R = 0.25}, ["Poppy"] = {Q = 0.32, W = 0.25, E = 0.25, R = 0.33}, ["Riven"] = {Q = 0.267, W = 0.25, E = 0.25, R = 0.25}, ["Ryze"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Singed"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Sivir"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Sona"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Soraka"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["TahmKench"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Thresh"] = {Q = 0.50, W = 0.25, E = 0.25, R = 0.75}, ["Tryndamere"] = {Q = 0.25, W = 0.25, E = 0.01, R = 0.25}, ["Viktor"] = {Q = 0.25, W = 0.25, E = 0.1, R = 0.25}, ["Vladimir"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.389}, ["Volibear"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, ["Warwick"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.1}, ["XinZhao"] = {Q = 0.25, W = 0.5, E = 0.25, R = 0.25}, ["Yorick"] = {Q = 0.25, W = 0.25, E = 0.33, R = 0.25}, ["Ziggs"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.30}, ["Zilean"] = {Q = 0.25, W = 0.25, E = 0.25, R = 0.25}, } local widths = { ["Aatrox"] = {Q = 275, W = 1, E = 100, R = 1}, ["Ahri"] = {Q = 100, W = 1, E = 60, R = 1}, ["Alistar"] = {Q = 1, W = 1, E = 1, R = 1}, ["Amumu"] = {Q = 80, W = 1, E = 1, R = 1}, ["Anivia"] = {Q = 250, W = 1, E = 1, R = 400}, ["Annie"] = {Q = 1, W = 50, E = 1, R = 290}, ["Ashe"] = {Q = 1, W = 58, E = 1, R = 130}, ["Blitzcrank"] = {Q = 70, W = 1, E = 1, R = 1}, ["Brand"] = {Q = 60, W = 250, E = 1, R = 1}, ["Braum"] = {Q = 60, W = 1, E = 1, R = 115}, ["Caitlyn"] = {Q = 90, W = 75, E = 70, R = 1}, ["Chogath"] = {Q = 350, W = 60, E = 1, R = 1}, ["Darius"] = {Q = 1, W = 1, E = 50, R = 1}, ["DrMundo"] = {Q = 60, W = 1, E = 1, R = 1}, ["Fizz"] = {Q = 1, W = 1, E = 330, R = 80}, ["Galio"] = {Q = 150, W = 1, E = 160, R = 1}, ["Garen"] = {Q = 1, W = 1, E = 1, R = 1}, ["Heimerdinger"] = {Q = 1, W = 60, E = 135, R = 1}, ["Illaoi"] = {Q = 100, W = 1, E = 50, R = 1}, ["Jax"] = {Q = 1, W = 1, E = 1, R = 1}, ["Karma"] = {Q = 60, W = 1, E = 1, R = 1}, ["Kayle"] = {Q = 1, W = 1, E = 1, R = 1}, ["Kayn"] = {Q = 350, W = 90, E = 1, R = 1}, ["KogMaw"] = {Q = 70, W = 1, E = 120, R = 200}, ["Lulu"] = {Q = 60, W = 1, E = 1, R = 1}, ["Maokai"] = {Q = 110, W = 1, E = 1, R = 1}, ["MasterYi"] = {Q = 1, W = 1, E = 1, R = 1}, ["MissFortune"] = {Q = 1, W = 1, E = 400, R = 40}, ["Morgana"] = {Q = 70, W = 325, E = 1, R = 1}, ["Nami"] = {Q = 160, W = 1, E = 1, R = 250}, ["Nunu"] = {Q = 1, W = 1, E = 1, R = 1}, ["Olaf"] = {Q = 90, W = 1, E = 1, R = 1}, ["Pantheon"] = {Q = 1, W = 1, E = 80, R = 1}, ["Poppy"] = {Q = 85, W = 1, E = 1, R = 100}, ["Riven"] = {Q = 135, W = 1, E = 1, R = 50}, ["Ryze"] = {Q = 55, W = 1, E = 1, R = 1}, ["Singed"] = {Q = 1, W = 265, E = 1, R = 1}, ["Sivir"] = {Q = 90, W = 1, E = 1, R = 1}, ["Sona"] = {Q = 1, W = 1, E = 1, R = 140}, ["Soraka"] = {Q = 235, W = 1, E = 300, R = 1}, ["TahmKench"] = {Q = 35, W = 1, E = 1, R = 1}, ["Thresh"] = {Q = 70, W = 25, E = 1, R = 1}, ["Tryndamere"] = {Q = 1, W = 1, E = 225, R = 1}, ["Viktor"] = {Q = 1, W = 1, E = 80, R = 290}, ["Vladimir"] = {Q = 1, W = 1, E = 1, R = 350}, ["Volibear"] = {Q = 1, W = 1, E = 1, R = 1}, ["Warwick"] = {Q = 1, W = 1, E = 1, R = 45}, ["XinZhao"] = {Q = 1, W = 45, E = 1, R = 1}, ["Yorick"] = {Q = 1, W = 300, E = 25, R = 1}, ["Ziggs"] = {Q = 75, W = 325, E = 325, R = 275}, ["Zilean"] = {Q = 180, W = 1, E = 1, R = 1}, } local speeds = { ["Aatrox"] = {Q = 450, W = 1, E = 1250, R = 1}, ["Ahri"] = {Q = 2500, W = 1, E = 1550, R = 1}, ["Alistar"] = {Q = 1, W = 1, E = 1, R = 1}, ["Amumu"] = {Q = 2000, W = 1, E = 1, R = 1}, ["Anivia"] = {Q = 1800, W = 1, E = 1, R = mathhuge}, ["Annie"] = {Q = 1, W = mathhuge, E = 1, R = mathhuge}, ["Ashe"] = {Q = 1, W = 1500, E = 1, R = 1600}, ["Blitzcrank"] = {Q = 2800, W = 1, E = 1, R = 1}, ["Brand"] = {Q = 1600, W = mathhuge, E = 1, R = 1}, ["Braum"] = {Q = 1700, W = 1, E = 1, R = 1400}, ["Caitlyn"] = {Q = 2200, W = mathhuge, E = 1600, R = 1}, ["Chogath"] = {Q = mathhuge, W = mathhuge, E = 1, R = 1}, ["Darius"] = {Q = 1, W = 1, E = mathhuge, R = 1}, ["DrMundo"] = {Q = 2000, W = 1, E = 1, R = 1}, ["Fizz"] = {Q = 1, W = 1, E = mathhuge, R = 1300}, ["Galio"] = {Q = 1150, W = 1, E = 1400, R = 1}, ["Garen"] = {Q = 1, W = 1, E = 1, R = 1}, ["Heimerdinger"] = {Q = 1, W = 2050, E = 1200, R = 1}, ["Illaoi"] = {Q = mathhuge, W = 1, E = 1900, R = 1}, ["Jax"] = {Q = 1, W = 1, E = 1, R = 1}, ["Karma"] = {Q = 1700, W = 1, E = 1, R = 1}, ["Kayle"] = {Q = 1, W = 1, E = 1, R = 1}, ["Kayn"] = {Q = mathhuge, W = mathhuge, E = 1, R = 1}, ["KogMaw"] = {Q = 1650, W = 1, E = 1400, R = mathhuge}, ["Lulu"] = {Q = 1450, W = 1, E = 1, R = 1}, ["Maokai"] = {Q = 1600, W = 1, E = 1, R = 1}, ["MasterYi"] = {Q = 1, W = 1, E = 1, R = 1}, ["MissFortune"] = {Q = 1, W = 1, E = mathhuge, R = mathhuge}, ["Morgana"] = {Q = 1200, W = mathhuge, E = 1, R = 1}, ["Nami"] = {Q = mathhuge, W = 1, E = 1, R = 850}, ["Nunu"] = {Q = 1, W = 1, E = 1, R = 1}, ["Olaf"] = {Q = 1600, W = 1, E = 1, R = 1}, ["Pantheon"] = {Q = 1, W = 1, E = mathhuge, R = 1}, ["Poppy"] = {Q = mathhuge, W = 1, E = 1, R = 1600}, ["Riven"] = {Q = mathhuge, W = 1, E = 1, R = 1600}, ["Ryze"] = {Q = 1700, W = 1, E = 1, R = 1}, ["Singed"] = {Q = 1, W = mathhuge, E = 1, R = 1}, ["Sivir"] = {Q = 1350, W = 1, E = 1, R = 1}, ["Sona"] = {Q = 1, W = 1, E = 1, R = 2400}, ["Soraka"] = {Q = 1150, W = 1, E = mathhuge, R = 1}, ["TahmKench"] = {Q = 2800, W = 1, E = 1, R = 1}, ["Thresh"] = {Q = 2900, W = 1500, E = 1, R = 1}, ["Tryndamere"] = {Q = 1, W = 1, E = 1300, R = 1}, ["Viktor"] = {Q = 1, W = 1, E = 1850, R = 1}, ["Vladimir"] = {Q = 1, W = 1, E = 1, R = mathhuge}, ["Volibear"] = {Q = 1, W = 1, E = 1, R = 1}, ["Warwick"] = {Q = 1, W = 1, E = 1, R = 1800}, ["XinZhao"] = {Q = 1, W = mathhuge, E = 1, R = 1}, ["Yorick"] = {Q = 1, W = mathhuge, E = 2100, R = 1}, ["Ziggs"] = {Q = 2800, W = 2000, E = 1800, R = 1600}, ["Zilean"] = {Q = 2050, W = 1, E = 1, R = 1}, } class "Helper" function Helper:__init() self:LoadSpells() self:LoadMenu() Callback.Add("Tick", function() self:Tick() end) Callback.Add("Draw", function() self:Draw() end) end function Helper:LoadSpells() local myName = myHero.charName Q = { Range = ranges[myName].Q, Delay = delays[myName].Q, Width = widths[myName].Q, Speed = speeds[myName].Q} W = { Range = ranges[myName].W, Delay = delays[myName].W, Width = widths[myName].W, Speed = speeds[myName].W} E = { Range = ranges[myName].E, Delay = delays[myName].E, Width = widths[myName].E, Speed = speeds[myName].E} R = { Range = ranges[myName].R, Delay = delays[myName].R, Width = widths[myName].R, Speed = speeds[myName].R} end function Helper:LoadMenu() Helper = MenuElement({type = MENU, id = "Helper", name = "Rugal Caster "..myHero.charName}) local heroName = myHero.charName Helper:MenuElement({id = heroName, name = "Helper " ..heroName, type = MENU}) Helper[heroName]:MenuElement({type = MENU, id = "Spells", name = "Spells"}) Helper[heroName].Spells:MenuElement({type = MENU, id = "Q", name = "Q"}) Helper[heroName].Spells.Q:MenuElement({id = "Key", name = "Key", key = string.byte("Q")}) Helper[heroName].Spells.Q:MenuElement({id = "How", name = "Spell type", drop = {"Skillshot","Point Target","Cursor","No target","Self Cast","Ally Cast","Self + Ally Cast"}}) Helper[heroName].Spells.Q:MenuElement({id = "Collision", name = "Collision?", value = true}) Helper[heroName].Spells.Q:MenuElement({id = "Search", name = "Search Range", min = 50, max = 3500, value = 250, step = 10}) Helper[heroName].Spells.Q:MenuElement({id = "Range", name = "Draw Range", value = true}) Helper[heroName].Spells.Q:MenuElement({id = "SearchRange", name = "Draw Search Range", value = true}) Helper[heroName].Spells:MenuElement({type = MENU, id = "W", name = "W"}) Helper[heroName].Spells.W:MenuElement({id = "Key", name = "Key", key = string.byte("W")}) Helper[heroName].Spells.W:MenuElement({id = "How", name = "Spell type", drop = {"Skillshot","Point Target","Cursor","No target","Self Cast","Ally Cast","Self + Ally Cast"}}) Helper[heroName].Spells.W:MenuElement({id = "Collision", name = "Collision?", value = true}) Helper[heroName].Spells.W:MenuElement({id = "Search", name = "Search Range", min = 50, max = 3500, value = 250, step = 10}) Helper[heroName].Spells.W:MenuElement({id = "Range", name = "Draw Range", value = true}) Helper[heroName].Spells.W:MenuElement({id = "SearchRange", name = "Draw Search Range", value = true}) Helper[heroName].Spells:MenuElement({type = MENU, id = "E", name = "E"}) Helper[heroName].Spells.E:MenuElement({id = "Key", name = "Key", key = string.byte("E")}) Helper[heroName].Spells.E:MenuElement({id = "How", name = "Spell type", drop = {"Skillshot","Point Target","Cursor","No target","Self Cast","Ally Cast","Self + Ally Cast"}}) Helper[heroName].Spells.E:MenuElement({id = "Collision", name = "Collision?", value = true}) Helper[heroName].Spells.E:MenuElement({id = "Search", name = "Search Range", min = 50, max = 3500, value = 250, step = 10}) Helper[heroName].Spells.E:MenuElement({id = "Range", name = "Draw Range", value = true}) Helper[heroName].Spells.E:MenuElement({id = "SearchRange", name = "Draw Search Range", value = true}) Helper[heroName].Spells:MenuElement({type = MENU, id = "R", name = "R"}) Helper[heroName].Spells.R:MenuElement({id = "Key", name = "Key", key = string.byte("R")}) Helper[heroName].Spells.R:MenuElement({id = "How", name = "Spell type", drop = {"Skillshot","Point Target","Cursor","No target","Self Cast","Ally Cast","Self + Ally Cast"}}) Helper[heroName].Spells.R:MenuElement({id = "Collision", name = "Collision?", value = true}) Helper[heroName].Spells.R:MenuElement({id = "Search", name = "Search Range", min = 50, max = 3500, value = 250, step = 10}) Helper[heroName].Spells.R:MenuElement({id = "Range", name = "Draw Range", value = true}) Helper[heroName].Spells.R:MenuElement({id = "SearchRange", name = "Draw Search Range", value = true}) end function Helper:Tick() self:Spells() end function Helper:Spells() local target = GetTarget(25000) ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- local howQ = Helper[myHero.charName].Spells.Q.How:Value() local usekey = Helper[myHero.charName].Spells.Q.Key:Value() if howQ == 1 then if usekey then if target and HPred:CanTarget(target) and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then local hitChance, aimPosition = HPred:GetHitchance(myHero.pos, target, Q.Range, Q.Delay, Q.Speed, Q.Width, Helper[myHero.charName].Spells.Q.Collision:Value(), nil) if hitChance and hitChance >= 1 and HPred:GetDistance(myHero.pos, aimPosition) <= Q.Range then Control.CastSpell(HK_Q, aimPosition) end else Control.CastSpell(HK_Q, Game.mousePos()) end end elseif howQ == 2 then if usekey then if target and GetDistance(target.pos) < Q.Range and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then Control.CastSpell(HK_Q, target) else local foeminion = ClosestMinion(Q.Range,foe) if foeminion and GetDistance(foeminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then Control.CastSpell(HK_Q, foeminion) else local neutralminion = ClosestMinion(Q.Range,neutral) if neutralminion and GetDistance(neutralminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then Control.CastSpell(HK_Q, neutralminion) end end end end elseif howQ == 3 then if usekey then Control.CastSpell(HK_Q, Game.cursorPos()) end elseif howQ == 4 then if usekey then Control.CastSpell(HK_Q) end elseif howQ == 5 then if usekey then Control.CastSpell(HK_Q,myHero) end elseif howQ == 5 then if usekey then local ally = ClosestHero(Q.Range,friend) if ally and not ally.isMe and GetDistance(ally.pos) < Q.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then Control.CastSpell(HK_Q, ally) end end elseif howQ == 6 then if usekey then local ally = ClosestHero(Q.Range,friend) if ally and GetDistance(ally.pos) < Q.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.Q.Search:Value() then Control.CastSpell(HK_Q, ally) end end end ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- local howW = Helper[myHero.charName].Spells.W.How:Value() local usekey = Helper[myHero.charName].Spells.W.Key:Value() if howW == 1 then if usekey then if target and HPred:CanTarget(target) and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then local hitChance, aimPosition = HPred:GetHitchance(myHero.pos, target, W.Range, W.Delay, W.Speed, W.Width, Helper[myHero.charName].Spells.W.Collision:Value(), nil) if hitChance and hitChance >= 1 and HPred:GetDistance(myHero.pos, aimPosition) <= W.Range then Control.CastSpell(HK_W, aimPosition) end else Control.CastSpell(HK_W, Game.mousePos()) end end elseif howW == 2 then if usekey then if target and GetDistance(target.pos) < W.Range and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then Control.CastSpell(HK_W, target) else local foeminion = ClosestMinion(W.Range,foe) if foeminion and GetDistance(foeminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then Control.CastSpell(HK_W, foeminion) else local neutralminion = ClosestMinion(W.Range,neutral) if neutralminion and GetDistance(neutralminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then Control.CastSpell(HK_W, neutralminion) end end end end elseif howW == 3 then if usekey then Control.CastSpell(HK_W, Game.cursorPos()) end elseif howW == 4 then if usekey then Control.CastSpell(HK_W) end elseif howW == 5 then if usekey then Control.CastSpell(HK_W,myHero) end elseif howW == 5 then if usekey then local ally = ClosestHero(W.Range,friend) if ally and not ally.isMe and GetDistance(ally.pos) < W.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then Control.CastSpell(HK_W, ally) end end elseif howW == 6 then if usekey then local ally = ClosestHero(W.Range,friend) if ally and GetDistance(ally.pos) < W.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.W.Search:Value() then Control.CastSpell(HK_W, ally) end end end ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- local howE = Helper[myHero.charName].Spells.E.How:Value() local usekey = Helper[myHero.charName].Spells.E.Key:Value() if howE == 1 then if usekey then if target and HPred:CanTarget(target) and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then local hitChance, aimPosition = HPred:GetHitchance(myHero.pos, target, E.Range, E.Delay, E.Speed, E.Width, Helper[myHero.charName].Spells.E.Collision:Value(), nil) if hitChance and hitChance >= 1 and HPred:GetDistance(myHero.pos, aimPosition) <= E.Range then Control.CastSpell(HK_E, aimPosition) end else Control.CastSpell(HK_E, Game.mousePos()) end end elseif howE == 2 then if usekey then if target and GetDistance(target.pos) < E.Range and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then Control.CastSpell(HK_E, target) else local foeminion = ClosestMinion(E.Range,foe) if foeminion and GetDistance(foeminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then Control.CastSpell(HK_E, foeminion) else local neutralminion = ClosestMinion(E.Range,neutral) if neutralminion and GetDistance(neutralminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then Control.CastSpell(HK_E, neutralminion) end end end end elseif howE == 3 then if usekey then Control.CastSpell(HK_E, Game.cursorPos()) end elseif howE == 4 then if usekey then Control.CastSpell(HK_E) end elseif howE == 5 then if usekey then Control.CastSpell(HK_E,myHero) end elseif howE == 5 then if usekey then local ally = ClosestHero(E.Range,friend) if ally and not ally.isMe and GetDistance(ally.pos) < E.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then Control.CastSpell(HK_E, ally) end end elseif howE == 6 then if usekey then local ally = ClosestHero(E.Range,friend) if ally and GetDistance(ally.pos) < E.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.E.Search:Value() then Control.CastSpell(HK_E, ally) end end end ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- local howR = Helper[myHero.charName].Spells.R.How:Value() local usekey = Helper[myHero.charName].Spells.R.Key:Value() if howR == 1 then if usekey then if target and HPred:CanTarget(target) and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then local hitChance, aimPosition = HPred:GetHitchance(myHero.pos, target, R.Range, R.Delay, R.Speed, R.Width, Helper[myHero.charName].Spells.R.Collision:Value(), nil) if hitChance and hitChance >= 1 and HPred:GetDistance(myHero.pos, aimPosition) <= R.Range then Control.CastSpell(HK_R, aimPosition) end else Control.CastSpell(HK_R, Game.mousePos()) end end elseif howR == 2 then if usekey then if target and GetDistance(target.pos) < R.Range and GetDistance(target.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then Control.CastSpell(HK_R, target) else local foeminion = ClosestMinion(R.Range,foe) if foeminion and GetDistance(foeminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then Control.CastSpell(HK_R, foeminion) else local neutralminion = ClosestMinion(R.Range,neutral) if neutralminion and GetDistance(neutralminion.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then Control.CastSpell(HK_R, neutralminion) end end end end elseif howR == 3 then if usekey then Control.CastSpell(HK_R, Game.cursorPos()) end elseif howR == 4 then if usekey then Control.CastSpell(HK_R) end elseif howR == 5 then if usekey then Control.CastSpell(HK_R,myHero) end elseif howR == 5 then if usekey then local ally = ClosestHero(R.Range,friend) if ally and not ally.isMe and GetDistance(ally.pos) < R.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then Control.CastSpell(HK_R, ally) end end elseif howR == 6 then if usekey then local ally = ClosestHero(R.Range,friend) if ally and GetDistance(ally.pos) < R.Range and GetDistance(ally.pos,Game.mousePos()) < Helper[myHero.charName].Spells.R.Search:Value() then Control.CastSpell(HK_R, ally) end end end ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- end function Helper:Draw() if myHero.dead then return end if Helper[myHero.charName].Spells.Q.Range:Value() then Draw.Circle(myHero.pos, Q.Range, 3, Draw.Color(255, 000, 000, 255)) end if Helper[myHero.charName].Spells.W.Range:Value() then Draw.Circle(myHero.pos, W.Range, 3, Draw.Color(255, 000, 255, 000)) end if Helper[myHero.charName].Spells.E.Range:Value() then Draw.Circle(myHero.pos, E.Range, 3, Draw.Color(255, 255, 255, 000)) end if Helper[myHero.charName].Spells.R.Range:Value() then Draw.Circle(myHero.pos, R.Range, 3, Draw.Color(255, 255, 000, 000)) end if Helper[myHero.charName].Spells.Q.SearchRange:Value() then Draw.Circle(Game.mousePos(), Helper[myHero.charName].Spells.Q.Search:Value(), 1, Draw.Color(255, 000, 000, 255)) end if Helper[myHero.charName].Spells.W.SearchRange:Value() then Draw.Circle(Game.mousePos(), Helper[myHero.charName].Spells.W.Search:Value(), 1, Draw.Color(255, 000, 255, 000)) end if Helper[myHero.charName].Spells.E.SearchRange:Value() then Draw.Circle(Game.mousePos(), Helper[myHero.charName].Spells.E.Search:Value(), 1, Draw.Color(255, 255, 255, 000)) end if Helper[myHero.charName].Spells.R.SearchRange:Value() then Draw.Circle(Game.mousePos(), Helper[myHero.charName].Spells.R.Search:Value(), 1, Draw.Color(255, 255, 000, 000)) end end local loaded = false Callback.Add("Load", function() if loaded == false then Helper() loaded = true end end) --[[ GENERAL API HPred:GetReliableTarget(source, range, delay, speed, radius, timingAccuracy, checkCollision) Usage Purpose Wrapper method to find an enemy who can be very reliably hit. Includes hourglas, teleport, blink, dash, CC and more. Returns target, aimPosition Param Description Source: Where the spell will be cast from Range: How far away to search for targets (max spell range) Delay: How long it will take for the spell to cast Speed: How fast the spell will travel Radius: How wide the spell is for hitbox calculations TimingAccuracy: General accuracy setting. This is how long after a hourglass/teleport/etc we can allow the spell to hit. I suggest ~0.25 seconds CheckCollision: Determines if a linear skillshot can be body blocked by minions or other heroes HPred:GetUnreliableTarget(source, range, delay, speed, radius, checkCollision, minimumHitChance, whitelist) Usage Purpose Finds any target in range who can be hit with at least 'minimumHitChance' of accuracy. Used as a wrapper for HPred:GetHitchance Returns target, aimPosition Param Description Source: Where the spell will be cast from Range: How far away to search for targets (max spell range) Delay: How long it will take for the spell to cast Speed: How fast the spell will travel Radius: How wide the spell is for hitbox calculations CheckCollision: Determines if a linear skillshot can be body blocked by minions or other heroes MinimumHitChance: How confident must we be that the target can be hit for the target to qualify. Recommend accuracy of 3 on almost all skills -1 = Invalid Target 1 = Standard accuracy 2 = Target is standing still or has changed movement path within the past 0.25 seconds 3 = The target is auto attacking and our spell will give them little time to react in order to dodge 4 = The target is using a spell and our spell will give them virtually no time in order to dodge 5 = The target should not be able to dodge without using movement skills HPred:GetHitchance(source, target, range, delay, speed, radius, checkCollision) Usage Purpose Determines the hitchance of a spell on a specified target and where to aim the spell Returns hitChance, aimPosition Param Description Source: Where the spell will be cast from Target: What entity are we trying to hit Range: How far away to search for targets (max spell range) Delay: How long it will take for the spell to cast Speed: How fast the spell will travel Radius: How wide the spell is for hitbox calculations CheckCollision: Determines if a linear skillshot can be body blocked by minions or other heroes HPred:GetLineTargetCount(source, aimPos, delay, speed, width, targetAllies) Usage Purpose Determines how many targets will be hit if we cast a linear spell at a specified location - Can specify if the targets are enemies or allies Returns total target count HPred:GetGuarenteedTarget(source, range, delay, speed, radius, timingAccuracy, checkCollision) UsagePurpose Simplified version of GetReliableTarget - will only check for hourglass, revive, teleport and CCd targets. Useful for high priority skills where you don't want to cast it every time an enemy dashes ]] class "HPred" local _atan = math.atan2 local _pi = math.pi local _min = math.min local _abs = math.abs local _sqrt = math.sqrt local _huge = math.huge local _insert = table.insert local _sort = table.sort local _find = string.find local _sub = string.sub local _len = string.len local LocalDrawLine = Draw.Line; local LocalDrawColor = Draw.Color; local LocalDrawCircle = Draw.Circle; local LocalDrawText = Draw.Text; local LocalControlIsKeyDown = Control.IsKeyDown; local LocalControlMouseEvent = Control.mouse_event; local LocalControlSetCursorPos = Control.SetCursorPos; local LocalControlKeyUp = Control.KeyUp; local LocalControlKeyDown = Control.KeyDown; local LocalGameCanUseSpell = Game.CanUseSpell; local LocalGameLatency = Game.Latency; local LocalGameTimer = Game.Timer; local LocalGameHeroCount = Game.HeroCount; local LocalGameHero = Game.Hero; local LocalGameMinionCount = Game.MinionCount; local LocalGameMinion = Game.Minion; local LocalGameTurretCount = Game.TurretCount; local LocalGameTurret = Game.Turret; local LocalGameWardCount = Game.WardCount; local LocalGameWard = Game.Ward; local LocalGameObjectCount = Game.ObjectCount; local LocalGameObject = Game.Object; local LocalGameMissileCount = Game.MissileCount; local LocalGameMissile = Game.Missile; local LocalGameParticleCount = Game.ParticleCount; local LocalGameParticle = Game.Particle; local LocalGameIsChatOpen = Game.IsChatOpen; local LocalGameIsOnTop = Game.IsOnTop; local _tickFrequency = .2 local _nextTick = Game.Timer() local _reviveLookupTable = { ["LifeAura.troy"] = 4, ["ZileanBase_R_Buf.troy"] = 3, ["Aatrox_Base_Passive_Death_Activate"] = 3 --TwistedFate_Base_R_Gatemarker_Red --String match would be ideal.... could be different in other skins } --Stores a collection of spells that will cause a character to blink --Ground targeted spells go towards mouse castPos with a maximum range --Hero/Minion targeted spells have a direction type to determine where we will land relative to our target (in front of, behind, etc) --Key = Spell name --Value = range a spell can travel, OR a targeted end position type, OR a list of particles the spell can teleport to local _blinkSpellLookupTable = { ["EzrealArcaneShift"] = 475, ["RiftWalk"] = 500, --Ekko and other similar blinks end up between their start pos and target pos (in front of their target relatively speaking) ["EkkoEAttack"] = 0, ["AlphaStrike"] = 0, --Katarina E ends on the side of her target closest to where her mouse was... ["KatarinaE"] = -255, --Katarina can target a dagger to teleport directly to it: Each skin has a different particle name. This should cover all of them. ["KatarinaEDagger"] = { "Katarina_Base_Dagger_Ground_Indicator","Katarina_Skin01_Dagger_Ground_Indicator","Katarina_Skin02_Dagger_Ground_Indicator","Katarina_Skin03_Dagger_Ground_Indicator","Katarina_Skin04_Dagger_Ground_Indicator","Katarina_Skin05_Dagger_Ground_Indicator","Katarina_Skin06_Dagger_Ground_Indicator","Katarina_Skin07_Dagger_Ground_Indicator" ,"Katarina_Skin08_Dagger_Ground_Indicator","Katarina_Skin09_Dagger_Ground_Indicator" }, } local _blinkLookupTable = { "global_ss_flash_02.troy", "Lissandra_Base_E_Arrival.troy", "LeBlanc_Base_W_return_activation.troy" --TODO: Check if liss/leblanc have diff skill versions. MOST likely dont but worth checking for completion sake --Zed uses 'switch shadows'... It will require some special checks to choose the shadow he's going TO not from... --Shaco deceive no longer has any particles where you jump to so it cant be tracked (no spell data or particles showing path) } local _cachedBlinks = {} local _cachedRevives = {} local _cachedTeleports = {} --Cache of all TARGETED missiles currently running local _cachedMissiles = {} local _incomingDamage = {} --Cache of active enemy windwalls so we can calculate it when dealing with collision checks local _windwall local _windwallStartPos local _windwallWidth local _OnVision = {} function HPred:OnVision(unit) if unit == nil or type(unit) ~= "userdata" then return end if _OnVision[unit.networkID] == nil then _OnVision[unit.networkID] = {visible = unit.visible , tick = GetTickCount(), pos = unit.pos } end if _OnVision[unit.networkID].visible == true and not unit.visible then _OnVision[unit.networkID].visible = false _OnVision[unit.networkID].tick = GetTickCount() end if _OnVision[unit.networkID].visible == false and unit.visible then _OnVision[unit.networkID].visible = true _OnVision[unit.networkID].tick = GetTickCount() _OnVision[unit.networkID].pos = unit.pos end return _OnVision[unit.networkID] end --This must be called manually - It's not on by default because we've tracked down most of the freeze issues to this. function HPred:Tick() --Update missile cache --DISABLED UNTIL LATER. --self:CacheMissiles() --Limit how often tick logic runs if _nextTick > Game.Timer() then return end _nextTick = Game.Timer() + _tickFrequency --Update hero movement history for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t then if t.isEnemy then HPred:OnVision(t) end end end --Do not run rest of logic until freeze issues are fully tracked down if true then return end --Remove old cached teleports for _, teleport in pairs(_cachedTeleports) do if teleport and Game.Timer() > teleport.expireTime + .5 then _cachedTeleports[_] = nil end end --Update teleport cache HPred:CacheTeleports() --Record windwall HPred:CacheParticles() --Remove old cached revives for _, revive in pairs(_cachedRevives) do if Game.Timer() > revive.expireTime + .5 then _cachedRevives[_] = nil end end --Remove old cached blinks for _, revive in pairs(_cachedRevives) do if Game.Timer() > revive.expireTime + .5 then _cachedRevives[_] = nil end end for i = 1, LocalGameParticleCount() do local particle = LocalGameParticle(i) --Record revives if particle and not _cachedRevives[particle.networkID] and _reviveLookupTable[particle.name] then _cachedRevives[particle.networkID] = {} _cachedRevives[particle.networkID]["expireTime"] = Game.Timer() + _reviveLookupTable[particle.name] local target = HPred:GetHeroByPosition(particle.pos) if target.isEnemy then _cachedRevives[particle.networkID]["target"] = target _cachedRevives[particle.networkID]["pos"] = target.pos _cachedRevives[particle.networkID]["isEnemy"] = target.isEnemy end end --Record blinks if particle and not _cachedBlinks[particle.networkID] and _blinkLookupTable[particle.name] then _cachedBlinks[particle.networkID] = {} _cachedBlinks[particle.networkID]["expireTime"] = Game.Timer() + _reviveLookupTable[particle.name] local target = HPred:GetHeroByPosition(particle.pos) if target.isEnemy then _cachedBlinks[particle.networkID]["target"] = target _cachedBlinks[particle.networkID]["pos"] = target.pos _cachedBlinks[particle.networkID]["isEnemy"] = target.isEnemy end end end end function HPred:GetEnemyNexusPosition() --This is slightly wrong. It represents fountain not the nexus. Fix later. if myHero.team == 100 then return Vector(14340, 171.977722167969, 14390); else return Vector(396,182.132507324219,462); end end function HPred:GetGuarenteedTarget(source, range, delay, speed, radius, timingAccuracy, checkCollision) --Get hourglass enemies local target, aimPosition =self:GetHourglassTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get reviving target local target, aimPosition =self:GetRevivingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get teleporting enemies local target, aimPosition =self:GetTeleportingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get stunned enemies local target, aimPosition =self:GetImmobileTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end end function HPred:GetReliableTarget(source, range, delay, speed, radius, timingAccuracy, checkCollision) --TODO: Target whitelist. This will target anyone which is definitely not what we want --For now we can handle in the champ script. That will cause issues with multiple people in range who are goood targets though. --Get hourglass enemies local target, aimPosition =self:GetHourglassTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get reviving target local target, aimPosition =self:GetRevivingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get channeling enemies --local target, aimPosition =self:GetChannelingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) -- if target and aimPosition then -- return target, aimPosition --end --Get teleporting enemies local target, aimPosition =self:GetTeleportingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get instant dash enemies local target, aimPosition =self:GetInstantDashTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get dashing enemies local target, aimPosition =self:GetDashingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius, midDash) if target and aimPosition then return target, aimPosition end --Get stunned enemies local target, aimPosition =self:GetImmobileTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) if target and aimPosition then return target, aimPosition end --Get blink targets local target, aimPosition =self:GetBlinkTarget(source, range, speed, delay, checkCollision, radius) if target and aimPosition then return target, aimPosition end end --Will return how many allies or enemies will be hit by a linear spell based on current waypoint data. function HPred:GetLineTargetCount(source, aimPos, delay, speed, width, targetAllies) local targetCount = 0 for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and self:CanTargetALL(t) and ( targetAllies or t.isEnemy) then local predictedPos = self:PredictUnitPosition(t, delay+ self:GetDistance(source, t.pos) / speed) local proj1, pointLine, isOnSegment = self:VectorPointProjectionOnLineSegment(source, aimPos, predictedPos) if proj1 and isOnSegment and (self:GetDistanceSqr(predictedPos, proj1) <= (t.boundingRadius + width) * (t.boundingRadius + width)) then targetCount = targetCount + 1 end end end return targetCount end --Will return the valid target who has the highest hit chance and meets all conditions (minHitChance, whitelist check, etc) function HPred:GetUnreliableTarget(source, range, delay, speed, radius, checkCollision, minimumHitChance, whitelist, isLine) local _validTargets = {} for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and self:CanTarget(t, true) and (not whitelist or whitelist[t.charName]) then local hitChance, aimPosition = self:GetHitchance(source, t, range, delay, speed, radius, checkCollision, isLine) if hitChance >= minimumHitChance then _insert(_validTargets, {aimPosition,hitChance, hitChance * 100 + self:CalculateMagicDamage(t, 400)}) end end end _sort(_validTargets, function( a, b ) return a[3] >b[3] end) if #_validTargets > 0 then return _validTargets[1][2], _validTargets[1][1] end end function HPred:GetHitchance(source, target, range, delay, speed, radius, checkCollision, isLine) if isLine == nil and checkCollision then isLine = true end local hitChance = 1 local aimPosition = self:PredictUnitPosition(target, delay + self:GetDistance(source, target.pos) / speed) local interceptTime = self:GetSpellInterceptTime(source, aimPosition, delay, speed) local reactionTime = self:PredictReactionTime(target, .1, isLine) --Check if they are walking the same path as the line or very close to it if isLine then local pathVector = aimPosition - target.pos local castVector = (aimPosition - myHero.pos):Normalized() if pathVector.x + pathVector.z ~= 0 then pathVector = pathVector:Normalized() if pathVector:DotProduct(castVector) < -.85 or pathVector:DotProduct(castVector) > .85 then if speed > 3000 then reactionTime = reactionTime + .25 else reactionTime = reactionTime + .15 end end end end --If they are standing still give a higher accuracy because they have to take actions to react to it if not target.pathing or not target.pathing.hasMovePath then hitChancevisionData = 2 end local origin,movementRadius = self:UnitMovementBounds(target, interceptTime, reactionTime) --Our spell is so wide or the target so slow or their reaction time is such that the spell will be nearly impossible to avoid if movementRadius - target.boundingRadius <= radius /2 then origin,movementRadius = self:UnitMovementBounds(target, interceptTime, 0) if movementRadius - target.boundingRadius <= radius /2 then hitChance = 4 else hitChance = 3 end end --If they are casting a spell then the accuracy will be fairly high. if the windup is longer than our delay then it's quite likely to hit. --Ideally we would predict where they will go AFTER the spell finishes but that's beyond the scope of this prediction if target.activeSpell and target.activeSpell.valid then if target.activeSpell.startTime + target.activeSpell.windup - Game.Timer() >= delay then hitChance = 5 else hitChance = 3 end end local visionData = HPred:OnVision(target) if visionData and visionData.visible == false then local hiddenTime = visionData.tick -GetTickCount() if hiddenTime < -1000 then hitChance = -1 else local targetSpeed = self:GetTargetMS(target) local unitPos = target.pos + Vector(target.pos,target.posTo):Normalized() * ((GetTickCount() - visionData.tick)/1000 * targetSpeed) local aimPosition = unitPos + Vector(target.pos,target.posTo):Normalized() * (targetSpeed * (delay + (self:GetDistance(myHero.pos,unitPos)/speed))) if self:GetDistance(target.pos,aimPosition) > self:GetDistance(target.pos,target.posTo) then aimPosition = target.posTo end hitChance = _min(hitChance, 2) end end --Check for out of range if not self:IsInRange(source, aimPosition, range) then hitChance = -1 end --Check minion block if hitChance > 0 and checkCollision then if self:IsWindwallBlocking(source, aimPosition) then hitChance = -1 elseif self:CheckMinionCollision(source, aimPosition, delay, speed, radius) then hitChance = -1 end end return hitChance, aimPosition end function HPred:PredictReactionTime(unit, minimumReactionTime) local reactionTime = minimumReactionTime --If the target is auto attacking increase their reaction time by .15s - If using a skill use the remaining windup time if unit.activeSpell and unit.activeSpell.valid then local windupRemaining = unit.activeSpell.startTime + unit.activeSpell.windup - Game.Timer() if windupRemaining > 0 then reactionTime = windupRemaining end end return reactionTime end function HPred:GetDashingTarget(source, range, delay, speed, dashThreshold, checkCollision, radius, midDash) local target local aimPosition for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and t.isEnemy and t.pathing.hasMovePath and t.pathing.isDashing and t.pathing.dashSpeed>500 then local dashEndPosition = t:GetPath(1) if self:IsInRange(source, dashEndPosition, range) then --The dash ends within range of our skill. We now need to find if our spell can connect with them very close to the time their dash will end local dashTimeRemaining = self:GetDistance(t.pos, dashEndPosition) / t.pathing.dashSpeed local skillInterceptTime = self:GetSpellInterceptTime(source, dashEndPosition, delay, speed) local deltaInterceptTime =skillInterceptTime - dashTimeRemaining if deltaInterceptTime > 0 and deltaInterceptTime < dashThreshold and (not checkCollision or not self:CheckMinionCollision(source, dashEndPosition, delay, speed, radius)) then target = t aimPosition = dashEndPosition return target, aimPosition end end end end end function HPred:GetHourglassTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and t.isEnemy then local success, timeRemaining = self:HasBuff(t, "zhonyasringshield") if success then local spellInterceptTime = self:GetSpellInterceptTime(source, t.pos, delay, speed) local deltaInterceptTime = spellInterceptTime - timeRemaining if spellInterceptTime > timeRemaining and deltaInterceptTime < timingAccuracy and (not checkCollision or not self:CheckMinionCollision(source, interceptPosition, delay, speed, radius)) then target = t aimPosition = t.pos return target, aimPosition end end end end end function HPred:GetRevivingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for _, revive in pairs(_cachedRevives) do if revive.isEnemy then local interceptTime = self:GetSpellInterceptTime(source, revive.pos, delay, speed) if interceptTime > revive.expireTime - Game.Timer() and interceptTime - revive.expireTime - Game.Timer() < timingAccuracy then target = revive.target aimPosition = revive.pos return target, aimPosition end end end end function HPred:GetInstantDashTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and t.isEnemy and t.activeSpell and t.activeSpell.valid and _blinkSpellLookupTable[t.activeSpell.name] then local windupRemaining = t.activeSpell.startTime + t.activeSpell.windup - Game.Timer() if windupRemaining > 0 then local endPos local blinkRange = _blinkSpellLookupTable[t.activeSpell.name] if type(blinkRange) == "table" then --Find the nearest matching particle to our mouse --local target, distance = self:GetNearestParticleByNames(t.pos, blinkRange) --if target and distance < 250 then -- endPos = target.pos --end elseif blinkRange > 0 then endPos = Vector(t.activeSpell.placementPos.x, t.activeSpell.placementPos.y, t.activeSpell.placementPos.z) endPos = t.activeSpell.startPos + (endPos- t.activeSpell.startPos):Normalized() * _min(self:GetDistance(t.activeSpell.startPos,endPos), range) else local blinkTarget = self:GetObjectByHandle(t.activeSpell.target) if blinkTarget then local offsetDirection --We will land in front of our target relative to our starting position if blinkRange == 0 then if t.activeSpell.name == "AlphaStrike" then windupRemaining = windupRemaining + .75 --TODO: Boost the windup time by the number of targets alpha will hit. Need to calculate the exact times this is just rough testing right now end offsetDirection = (blinkTarget.pos - t.pos):Normalized() --We will land behind our target relative to our starting position elseif blinkRange == -1 then offsetDirection = (t.pos-blinkTarget.pos):Normalized() --They can choose which side of target to come out on , there is no way currently to read this data so we will only use this calculation if the spell radius is large elseif blinkRange == -255 then if radius > 250 then endPos = blinkTarget.pos end end if offsetDirection then endPos = blinkTarget.pos - offsetDirection * blinkTarget.boundingRadius end end end local interceptTime = self:GetSpellInterceptTime(source, endPos, delay,speed) local deltaInterceptTime = interceptTime - windupRemaining if self:IsInRange(source, endPos, range) and deltaInterceptTime < timingAccuracy and (not checkCollision or not self:CheckMinionCollision(source, endPos, delay, speed, radius)) then target = t aimPosition = endPos return target,aimPosition end end end end end function HPred:GetBlinkTarget(source, range, speed, delay, checkCollision, radius) local target local aimPosition for _, particle in pairs(_cachedBlinks) do if particle and self:IsInRange(source, particle.pos, range) then local t = particle.target local pPos = particle.pos if t and t.isEnemy and (not checkCollision or not self:CheckMinionCollision(source, pPos, delay, speed, radius)) then target = t aimPosition = pPos return target,aimPosition end end end end function HPred:GetChannelingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t then local interceptTime = self:GetSpellInterceptTime(source, t.pos, delay, speed) if self:CanTarget(t) and self:IsInRange(source, t.pos, range) and self:IsChannelling(t, interceptTime) and (not checkCollision or not self:CheckMinionCollision(source, t.pos, delay, speed, radius)) then target = t aimPosition = t.pos return target, aimPosition end end end end function HPred:GetImmobileTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for i = 1, LocalGameHeroCount() do local t = LocalGameHero(i) if t and self:CanTarget(t) and self:IsInRange(source, t.pos, range) then local immobileTime = self:GetImmobileTime(t) local interceptTime = self:GetSpellInterceptTime(source, t.pos, delay, speed) if immobileTime - interceptTime > timingAccuracy and (not checkCollision or not self:CheckMinionCollision(source, t.pos, delay, speed, radius)) then target = t aimPosition = t.pos return target, aimPosition end end end end function HPred:CacheTeleports() --Get enemies who are teleporting to towers for i = 1, LocalGameTurretCount() do local turret = LocalGameTurret(i); if turret and turret.isEnemy and not _cachedTeleports[turret.networkID] then local hasBuff, expiresAt = self:HasBuff(turret, "teleport_target") if hasBuff then self:RecordTeleport(turret, self:GetTeleportOffset(turret.pos,223.31),expiresAt) end end end --Get enemies who are teleporting to wards for i = 1, LocalGameWardCount() do local ward = LocalGameWard(i); if ward and ward.isEnemy and not _cachedTeleports[ward.networkID] then local hasBuff, expiresAt = self:HasBuff(ward, "teleport_target") if hasBuff then self:RecordTeleport(ward, self:GetTeleportOffset(ward.pos,100.01),expiresAt) end end end --Get enemies who are teleporting to minions for i = 1, LocalGameMinionCount() do local minion = LocalGameMinion(i); if minion and minion.isEnemy and not _cachedTeleports[minion.networkID] then local hasBuff, expiresAt = self:HasBuff(minion, "teleport_target") if hasBuff then self:RecordTeleport(minion, self:GetTeleportOffset(minion.pos,143.25),expiresAt) end end end end function HPred:RecordTeleport(target, aimPos, endTime) _cachedTeleports[target.networkID] = {} _cachedTeleports[target.networkID]["target"] = target _cachedTeleports[target.networkID]["aimPos"] = aimPos _cachedTeleports[target.networkID]["expireTime"] = endTime + Game.Timer() end function HPred:CalculateIncomingDamage() _incomingDamage = {} local currentTime = Game.Timer() for _, missile in pairs(_cachedMissiles) do if missile then local dist = self:GetDistance(missile.data.pos, missile.target.pos) if missile.name == "" or currentTime >= missile.timeout or dist < missile.target.boundingRadius then _cachedMissiles[_] = nil else if not _incomingDamage[missile.target.networkID] then _incomingDamage[missile.target.networkID] = missile.damage else _incomingDamage[missile.target.networkID] = _incomingDamage[missile.target.networkID] + missile.damage end end end end end function HPred:GetIncomingDamage(target) local damage = 0 if _incomingDamage[target.networkID] then damage = _incomingDamage[target.networkID] end return damage end local _maxCacheRange = 3000 --Right now only used to cache enemy windwalls function HPred:CacheParticles() if _windwall and _windwall.name == "" then _windwall = nil end for i = 1, LocalGameParticleCount() do local particle = LocalGameParticle(i) if particle and self:IsInRange(particle.pos, myHero.pos, _maxCacheRange) then if _find(particle.name, "W_windwall%d") and not _windwall then --We don't care about ally windwalls for now local owner = self:GetObjectByHandle(particle.handle) if owner and owner.isEnemy then _windwall = particle _windwallStartPos = Vector(particle.pos.x, particle.pos.y, particle.pos.z) local index = _len(particle.name) - 5 local spellLevel = _sub(particle.name, index, index) -1 --Simple fix if type(spellLevel) ~= "number" then spellLevel = 1 end _windwallWidth = 150 + spellLevel * 25 end end end end end function HPred:CacheMissiles() local currentTime = Game.Timer() for i = 1, LocalGameMissileCount() do local missile = LocalGameMissile(i) if missile and not _cachedMissiles[missile.networkID] and missile.missileData then --Handle targeted missiles if missile.missileData.target and missile.missileData.owner then local missileName = missile.missileData.name local owner = self:GetObjectByHandle(missile.missileData.owner) local target = self:GetObjectByHandle(missile.missileData.target) if owner and target and _find(target.type, "Hero") then --The missile is an auto attack of some sort that is targeting a player if (_find(missileName, "BasicAttack") or _find(missileName, "CritAttack")) then --Cache it all and update the count _cachedMissiles[missile.networkID] = {} _cachedMissiles[missile.networkID].target = target _cachedMissiles[missile.networkID].data = missile _cachedMissiles[missile.networkID].danger = 1 _cachedMissiles[missile.networkID].timeout = currentTime + 1.5 local damage = owner.totalDamage if _find(missileName, "CritAttack") then --Leave it rough we're not that concerned damage = damage * 1.5 end _cachedMissiles[missile.networkID].damage = self:CalculatePhysicalDamage(target, damage) end end end end end end function HPred:CalculatePhysicalDamage(target, damage) local targetArmor = target.armor * myHero.armorPenPercent - myHero.armorPen local damageReduction = 100 / ( 100 + targetArmor) if targetArmor < 0 then damageReduction = 2 - (100 / (100 - targetArmor)) end damage = damage * damageReduction return damage end function HPred:CalculateMagicDamage(target, damage) local targetMR = target.magicResist * myHero.magicPenPercent - myHero.magicPen local damageReduction = 100 / ( 100 + targetMR) if targetMR < 0 then damageReduction = 2 - (100 / (100 - targetMR)) end damage = damage * damageReduction return damage end function HPred:GetTeleportingTarget(source, range, delay, speed, timingAccuracy, checkCollision, radius) local target local aimPosition for _, teleport in pairs(_cachedTeleports) do if teleport.expireTime > Game.Timer() and self:IsInRange(source,teleport.aimPos, range) then local spellInterceptTime = self:GetSpellInterceptTime(source, teleport.aimPos, delay, speed) local teleportRemaining = teleport.expireTime - Game.Timer() if spellInterceptTime > teleportRemaining and spellInterceptTime - teleportRemaining <= timingAccuracy and (not checkCollision or not self:CheckMinionCollision(source, teleport.aimPos, delay, speed, radius)) then target = teleport.target aimPosition = teleport.aimPos return target, aimPosition end end end end function HPred:GetTargetMS(target) local ms = target.pathing.isDashing and target.pathing.dashSpeed or target.ms return ms end function HPred:Angle(A, B) local deltaPos = A - B local angle = _atan(deltaPos.x, deltaPos.z) * 180 / _pi if angle < 0 then angle = angle + 360 end return angle end --Returns where the unit will be when the delay has passed given current pathing information. This assumes the target makes NO CHANGES during the delay. function HPred:PredictUnitPosition(unit, delay) local predictedPosition = unit.pos local timeRemaining = delay local pathNodes = self:GetPathNodes(unit) for i = 1, #pathNodes -1 do local nodeDistance = self:GetDistance(pathNodes[i], pathNodes[i +1]) local nodeTraversalTime = nodeDistance / self:GetTargetMS(unit) if timeRemaining > nodeTraversalTime then --This node of the path will be completed before the delay has finished. Move on to the next node if one remains timeRemaining = timeRemaining - nodeTraversalTime predictedPosition = pathNodes[i + 1] else local directionVector = (pathNodes[i+1] - pathNodes[i]):Normalized() predictedPosition = pathNodes[i] + directionVector * self:GetTargetMS(unit) * timeRemaining break; end end return predictedPosition end function HPred:IsChannelling(target, interceptTime) if target.activeSpell and target.activeSpell.valid and target.activeSpell.isChanneling then return true end end function HPred:HasBuff(target, buffName, minimumDuration) local duration = minimumDuration if not minimumDuration then duration = 0 end local durationRemaining for i = 1, target.buffCount do local buff = target:GetBuff(i) if buff.duration > duration and buff.name == buffName then durationRemaining = buff.duration return true, durationRemaining end end end --Moves an origin towards the enemy team nexus by magnitude function HPred:GetTeleportOffset(origin, magnitude) local teleportOffset = origin + (self:GetEnemyNexusPosition()- origin):Normalized() * magnitude return teleportOffset end function HPred:GetSpellInterceptTime(startPos, endPos, delay, speed) local interceptTime = Game.Latency()/2000 + delay + self:GetDistance(startPos, endPos) / speed return interceptTime end --Checks if a target can be targeted by abilities or auto attacks currently. --CanTarget(target) --target : gameObject we are trying to hit function HPred:CanTarget(target, allowInvisible) return target.isEnemy and target.alive and target.health > 0 and (allowInvisible or target.visible) and target.isTargetable end --Derp: dont want to fuck with the isEnemy checks elsewhere. This will just let us know if the target can actually be hit by something even if its an ally function HPred:CanTargetALL(target) return target.alive and target.health > 0 and target.visible and target.isTargetable end --Returns a position and radius in which the target could potentially move before the delay ends. ReactionTime defines how quick we expect the target to be able to change their current path function HPred:UnitMovementBounds(unit, delay, reactionTime) local startPosition = self:PredictUnitPosition(unit, delay) local radius = 0 local deltaDelay = delay -reactionTime- self:GetImmobileTime(unit) if (deltaDelay >0) then radius = self:GetTargetMS(unit) * deltaDelay end return startPosition, radius end --Returns how long (in seconds) the target will be unable to move from their current location function HPred:GetImmobileTime(unit) local duration = 0 for i = 0, unit.buffCount do local buff = unit:GetBuff(i); if buff.count > 0 and buff.duration> duration and (buff.type == 5 or buff.type == 8 or buff.type == 21 or buff.type == 22 or buff.type == 24 or buff.type == 11 or buff.type == 29 or buff.type == 30 or buff.type == 39 ) then duration = buff.duration end end return duration end --Returns how long (in seconds) the target will be slowed for function HPred:GetSlowedTime(unit) local duration = 0 for i = 0, unit.buffCount do local buff = unit:GetBuff(i); if buff.count > 0 and buff.duration > duration and buff.type == 10 then duration = buff.duration return duration end end return duration end --Returns all existing path nodes function HPred:GetPathNodes(unit) local nodes = {} table.insert(nodes, unit.pos) if unit.pathing.hasMovePath then for i = unit.pathing.pathIndex, unit.pathing.pathCount do path = unit:GetPath(i) table.insert(nodes, path) end end return nodes end --Finds any game object with the correct handle to match (hero, minion, wards on either team) function HPred:GetObjectByHandle(handle) local target for i = 1, LocalGameHeroCount() do local enemy = LocalGameHero(i) if enemy and enemy.handle == handle then target = enemy return target end end for i = 1, LocalGameMinionCount() do local minion = LocalGameMinion(i) if minion and minion.handle == handle then target = minion return target end end for i = 1, LocalGameWardCount() do local ward = LocalGameWard(i); if ward and ward.handle == handle then target = ward return target end end for i = 1, LocalGameTurretCount() do local turret = LocalGameTurret(i) if turret and turret.handle == handle then target = turret return target end end for i = 1, LocalGameParticleCount() do local particle = LocalGameParticle(i) if particle and particle.handle == handle then target = particle return target end end end function HPred:GetHeroByPosition(position) local target for i = 1, LocalGameHeroCount() do local enemy = LocalGameHero(i) if enemy and enemy.pos.x == position.x and enemy.pos.y == position.y and enemy.pos.z == position.z then target = enemy return target end end end function HPred:GetObjectByPosition(position) local target for i = 1, LocalGameHeroCount() do local enemy = LocalGameHero(i) if enemy and enemy.pos.x == position.x and enemy.pos.y == position.y and enemy.pos.z == position.z then target = enemy return target end end for i = 1, LocalGameMinionCount() do local enemy = LocalGameMinion(i) if enemy and enemy.pos.x == position.x and enemy.pos.y == position.y and enemy.pos.z == position.z then target = enemy return target end end for i = 1, LocalGameWardCount() do local enemy = LocalGameWard(i); if enemy and enemy.pos.x == position.x and enemy.pos.y == position.y and enemy.pos.z == position.z then target = enemy return target end end for i = 1, LocalGameParticleCount() do local enemy = LocalGameParticle(i) if enemy and enemy.pos.x == position.x and enemy.pos.y == position.y and enemy.pos.z == position.z then target = enemy return target end end end function HPred:GetEnemyHeroByHandle(handle) local target for i = 1, LocalGameHeroCount() do local enemy = LocalGameHero(i) if enemy and enemy.handle == handle then target = enemy return target end end end --Finds the closest particle to the origin that is contained in the names array function HPred:GetNearestParticleByNames(origin, names) local target local distance = 999999 for i = 1, LocalGameParticleCount() do local particle = LocalGameParticle(i) if particle then local d = self:GetDistance(origin, particle.pos) if d < distance then distance = d target = particle end end end return target, distance end --Returns the total distance of our current path so we can calculate how long it will take to complete function HPred:GetPathLength(nodes) local result = 0 for i = 1, #nodes -1 do result = result + self:GetDistance(nodes[i], nodes[i + 1]) end return result end --I know this isn't efficient but it works accurately... Leaving it for now. function HPred:CheckMinionCollision(origin, endPos, delay, speed, radius, frequency) if not frequency then frequency = radius end local directionVector = (endPos - origin):Normalized() local checkCount = self:GetDistance(origin, endPos) / frequency for i = 1, checkCount do local checkPosition = origin + directionVector * i * frequency local checkDelay = delay + self:GetDistance(origin, checkPosition) / speed if self:IsMinionIntersection(checkPosition, radius, checkDelay, radius * 3) then return true end end return false end function HPred:IsMinionIntersection(location, radius, delay, maxDistance) if not maxDistance then maxDistance = 500 end for i = 1, LocalGameMinionCount() do local minion = LocalGameMinion(i) if minion and self:CanTarget(minion) and self:IsInRange(minion.pos, location, maxDistance) then local predictedPosition = self:PredictUnitPosition(minion, delay) if self:IsInRange(location, predictedPosition, radius + minion.boundingRadius) then return true end end end return false end function HPred:VectorPointProjectionOnLineSegment(v1, v2, v) assert(v1 and v2 and v, "VectorPointProjectionOnLineSegment: wrong argument types (3 expected)") local cx, cy, ax, ay, bx, by = v.x, (v.z or v.y), v1.x, (v1.z or v1.y), v2.x, (v2.z or v2.y) local rL = ((cx - ax) * (bx - ax) + (cy - ay) * (by - ay)) / ((bx - ax) * (bx - ax) + (by - ay) * (by - ay)) local pointLine = { x = ax + rL * (bx - ax), y = ay + rL * (by - ay) } local rS = rL < 0 and 0 or (rL > 1 and 1 or rL) local isOnSegment = rS == rL local pointSegment = isOnSegment and pointLine or { x = ax + rS * (bx - ax), y = ay + rS * (by - ay) } return pointSegment, pointLine, isOnSegment end --Determines if there is a windwall between the source and target pos. function HPred:IsWindwallBlocking(source, target) if _windwall then local windwallFacing = (_windwallStartPos-_windwall.pos):Normalized() return self:DoLineSegmentsIntersect(source, target, _windwall.pos + windwallFacing:Perpendicular() * _windwallWidth, _windwall.pos + windwallFacing:Perpendicular2() * _windwallWidth) end return false end --Returns if two line segments cross eachother. AB is segment 1, CD is segment 2. function HPred:DoLineSegmentsIntersect(A, B, C, D) local o1 = self:GetOrientation(A, B, C) local o2 = self:GetOrientation(A, B, D) local o3 = self:GetOrientation(C, D, A) local o4 = self:GetOrientation(C, D, B) if o1 ~= o2 and o3 ~= o4 then return true end if o1 == 0 and self:IsOnSegment(A, C, B) then return true end if o2 == 0 and self:IsOnSegment(A, D, B) then return true end if o3 == 0 and self:IsOnSegment(C, A, D) then return true end if o4 == 0 and self:IsOnSegment(C, B, D) then return true end return false end --Determines the orientation of ordered triplet --0 = Colinear --1 = Clockwise --2 = CounterClockwise function HPred:GetOrientation(A,B,C) local val = (B.z - A.z) * (C.x - B.x) - (B.x - A.x) * (C.z - B.z) if val == 0 then return 0 elseif val > 0 then return 1 else return 2 end end function HPred:IsOnSegment(A, B, C) return B.x <= _max(A.x, C.x) and B.x >= _min(A.x, C.x) and B.z <= _max(A.z, C.z) and B.z >= _min(A.z, C.z) end --Gets the slope between two vectors. Ignores Y because it is non-needed height data. Its all 2d math. function HPred:GetSlope(A, B) return (B.z - A.z) / (B.x - A.x) end function HPred:GetEnemyByName(name) local target for i = 1, LocalGameHeroCount() do local enemy = LocalGameHero(i) if enemy and enemy.isEnemy and enemy.charName == name then target = enemy return target end end end function HPred:IsPointInArc(source, origin, target, angle, range) local deltaAngle = _abs(HPred:Angle(origin, target) - HPred:Angle(source, origin)) if deltaAngle < angle and self:IsInRange(origin,target,range) then return true end end function HPred:GetDistanceSqr(p1, p2) if not p1 or not p2 then local dInfo = debug.getinfo(2) print("Undefined GetDistanceSqr target. Please report. Method: " .. dInfo.name .. " Line: " .. dInfo.linedefined) return _huge end return (p1.x - p2.x) * (p1.x - p2.x) + ((p1.z or p1.y) - (p2.z or p2.y)) * ((p1.z or p1.y) - (p2.z or p2.y)) end function HPred:IsInRange(p1, p2, range) if not p1 or not p2 then local dInfo = debug.getinfo(2) print("Undefined IsInRange target. Please report. Method: " .. dInfo.name .. " Line: " .. dInfo.linedefined) return false end return (p1.x - p2.x) * (p1.x - p2.x) + ((p1.z or p1.y) - (p2.z or p2.y)) * ((p1.z or p1.y) - (p2.z or p2.y)) < range * range end function HPred:GetDistance(p1, p2) if not p1 or not p2 then local dInfo = debug.getinfo(2) print("Undefined GetDistance target. Please report. Method: " .. dInfo.name .. " Line: " .. dInfo.linedefined) return _huge end return _sqrt(self:GetDistanceSqr(p1, p2)) end