public static float GetClosestDistanceApproach(Spell spell, Vector2 pos, float speed, float delay, Vector2 heroPos, float extraDist) { var walkDir = (pos - heroPos).Normalized(); if (spell.spellType == SpellType.Line && spell.info.projectileSpeed != float.MaxValue) { var spellPos = spell.GetCurrentSpellPosition(true, delay); var spellStartPos = spell.currentSpellPosition; var spellEndPos = spell.GetSpellEndPosition(); var extendedPos = pos.ExtendDir(walkDir, ObjectCache.myHeroCache.boundingRadius + speed * delay / 1000); Vector2 cHeroPos; Vector2 cSpellPos; var cpa2 = MathUtils.GetCollisionDistanceEx( heroPos, walkDir * speed, ObjectCache.myHeroCache.boundingRadius, spellPos, spell.direction * spell.info.projectileSpeed, spell.radius + extraDist, out cHeroPos, out cSpellPos); var cHeroPosProjection = cHeroPos.ProjectOn(heroPos, extendedPos); var cSpellPosProjection = cSpellPos.ProjectOn(spellPos, spellEndPos); if (cSpellPosProjection.IsOnSegment && cHeroPosProjection.IsOnSegment && cpa2 != float.MaxValue) { return 0; } var cpa = MathUtilsCPA.CPAPointsEx(heroPos, walkDir * speed, spellPos, spell.direction * spell.info.projectileSpeed, pos, spellEndPos, out cHeroPos, out cSpellPos); cHeroPosProjection = cHeroPos.ProjectOn(heroPos, extendedPos); cSpellPosProjection = cSpellPos.ProjectOn(spellPos, spellEndPos); var checkDist = ObjectCache.myHeroCache.boundingRadius + spell.radius + extraDist; if (cSpellPosProjection.IsOnSegment && cHeroPosProjection.IsOnSegment) { return Math.Max(0, cpa - checkDist); } else { return checkDist; } //return MathUtils.ClosestTimeOfApproach(heroPos, walkDir * speed, spellPos, spell.direction * spell.info.projectileSpeed); } else if (spell.spellType == SpellType.Line && spell.info.projectileSpeed == float.MaxValue) { var spellHitTime = Math.Max(0, spell.endTime - EvadeUtils.TickCount - delay); //extraDelay var walkRange = heroPos.Distance(pos); var predictedRange = speed * (spellHitTime / 1000); var tHeroPos = heroPos + walkDir * Math.Min(predictedRange, walkRange); //Hero predicted pos var projection = tHeroPos.ProjectOn(spell.startPos, spell.endPos); return Math.Max(0, tHeroPos.Distance(projection.SegmentPoint) - (spell.radius + ObjectCache.myHeroCache.boundingRadius + extraDist)); //+ dodgeBuffer } else if (spell.spellType == SpellType.Circular) { var spellHitTime = Math.Max(0, spell.endTime - EvadeUtils.TickCount - delay); //extraDelay var walkRange = heroPos.Distance(pos); var predictedRange = speed * (spellHitTime / 1000); var tHeroPos = heroPos + walkDir * Math.Min(predictedRange, walkRange); //Hero predicted pos if (spell.info.spellName == "VeigarEventHorizon") { var wallRadius = 65; var midRadius = spell.radius - wallRadius; if (spellHitTime == 0) { return 0; } if (tHeroPos.Distance(spell.endPos) >= spell.radius) { return Math.Max(0, tHeroPos.Distance(spell.endPos) - midRadius - wallRadius); } else { return Math.Max(0, midRadius - tHeroPos.Distance(spell.endPos) - wallRadius); } } var closestDist = Math.Max(0, tHeroPos.Distance(spell.endPos) - (spell.radius + extraDist)); if (spell.info.extraEndTime > 0 && closestDist != 0) { var remainingTime = Math.Max(0, spell.endTime + spell.info.extraEndTime - EvadeUtils.TickCount - delay); var predictedRange2 = speed * (remainingTime / 1000); var tHeroPos2 = heroPos + walkDir * Math.Min(predictedRange2, walkRange); if (CheckMoveToDirection(tHeroPos, tHeroPos2)) { return 0; } } else { return closestDist; } } else if (spell.spellType == SpellType.Arc) { var spellPos = spell.GetCurrentSpellPosition(true, delay); var spellEndPos = spell.GetSpellEndPosition(); var pDir = spell.direction.Perpendicular(); spellPos = spellPos - pDir * spell.radius / 2; spellEndPos = spellEndPos - pDir * spell.radius / 2; var extendedPos = pos.ExtendDir(walkDir, ObjectCache.myHeroCache.boundingRadius); Vector2 cHeroPos; Vector2 cSpellPos; var cpa = MathUtilsCPA.CPAPointsEx(heroPos, walkDir * speed, spellPos, spell.direction * spell.info.projectileSpeed, pos, spellEndPos, out cHeroPos, out cSpellPos); var cHeroPosProjection = cHeroPos.ProjectOn(heroPos, extendedPos); var cSpellPosProjection = cSpellPos.ProjectOn(spellPos, spellEndPos); var checkDist = spell.radius + extraDist; if (cHeroPos.InSkillShot(spell, ObjectCache.myHeroCache.boundingRadius)) { if (cSpellPosProjection.IsOnSegment && cHeroPosProjection.IsOnSegment) { return Math.Max(0, cpa - checkDist); } else { return checkDist; } } } return 1; }