/// <summary> /// Gets Prediction result /// </summary> /// <param name="target">Target for spell</param> /// <param name="width">Spell width</param> /// <param name="delay">Spell delay</param> /// <param name="missileSpeed">Spell missile speed</param> /// <param name="range">Spell range</param> /// <param name="collisionable">Spell collisionable</param> /// <param name="type">Spell skillshot type</param> /// <param name="path">Waypoints of target</param> /// <param name="avgt">Average reaction time (in ms)</param> /// <param name="movt">Passed time from last movement change (in ms)</param> /// <param name="avgp">Average Path Lenght</param> /// <param name="from">Spell casted position</param> /// <param name="rangeCheckFrom"></param> /// <returns>Prediction result as <see cref="Prediction.Result" /></returns> internal static Result GetPrediction(Obj_AI_Base target, float width, float delay, float missileSpeed, float range, bool collisionable, SkillshotType type, List <Vector2> path, float avgt, float movt, float avgp, float anglediff, Vector2 from, Vector2 rangeCheckFrom) { Prediction.AssertInitializationMode(); Result result = new Result(); result.Input = new Input(target, delay, missileSpeed, width, range, collisionable, type, from.To3D2(), rangeCheckFrom.To3D2()); result.Unit = target; try { if (type == SkillshotType.SkillshotCircle) { range += width; } //to do: hook logic ? by storing average movement direction etc if (path.Count <= 1 && movt > 100 && (Environment.TickCount - PathTracker.EnemyInfo[target.NetworkId].LastAATick > 300 || !ConfigMenu.CheckAAWindUp)) //if target is not moving, easy to hit (and not aaing) { result.HitChance = HitChance.VeryHigh; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return(result); } if (target is AIHeroClient) { if (((AIHeroClient)target).IsChannelingImportantSpell()) { result.HitChance = HitChance.VeryHigh; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return(result); } if (Environment.TickCount - PathTracker.EnemyInfo[target.NetworkId].LastAATick < 300 && ConfigMenu.CheckAAWindUp) { if (target.AttackCastDelay * 1000 + PathTracker.EnemyInfo[target.NetworkId].AvgOrbwalkTime + avgt - width / 2f / target.MoveSpeed >= GetArrivalTime(target.ServerPosition.LSTo2D().LSDistance(from), delay, missileSpeed)) { result.HitChance = HitChance.High; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return(result); } } //to do: find a fuking logic if (avgp < 400 && movt < 100 && path.LSPathLength() <= avgp) { result.HitChance = HitChance.High; result.CastPosition = path.Last(); result.UnitPosition = result.CastPosition; result.Lock(); return(result); } } if (target.LSIsDashing()) //if unit is dashing { return(GetDashingPrediction(target, width, delay, missileSpeed, range, collisionable, type, from, rangeCheckFrom)); } if (Utility.IsImmobileTarget(target)) //if unit is immobile { return(GetImmobilePrediction(target, width, delay, missileSpeed, range, collisionable, type, from, rangeCheckFrom)); } result = WaypointAnlysis(target, width, delay, missileSpeed, range, collisionable, type, path, avgt, movt, avgp, anglediff, from); float d = result.CastPosition.LSDistance(target.ServerPosition.LSTo2D()); if (d >= (avgt - movt) * target.MoveSpeed && d >= avgp) { result.HitChance = HitChance.Medium; } result.Lock(); return(result); } finally { //check if movement changed while prediction calculations if (!target.GetWaypoints().SequenceEqual(path)) { result.HitChance = HitChance.Medium; } } }
/// <summary> /// Get Position on Unit's Path. /// </summary> /// <param name="input"> /// <see cref="PredictionInput" /> input /// </param> /// <param name="path">Path in Vector2 List</param> /// <param name="speed">Unit Speed</param> /// <returns><see cref="PredictionOutput" /> output</returns> internal static PredictionOutput GetPositionOnPath(PredictionInput input, List <Vector2> path, float speed = -1) { speed = Math.Abs(speed - -1) < float.Epsilon ? input.Unit.MoveSpeed : speed; if (path.Count <= 1) { return(new PredictionOutput { Input = input, UnitPosition = input.Unit.ServerPosition, CastPosition = input.Unit.ServerPosition, Hitchance = HitChance.VeryHigh }); } var pLength = path.LSPathLength(); var dist = input.Delay * speed - input.RealRadius; // Skillshots with only a delay if (pLength >= dist && Math.Abs(input.Speed - float.MaxValue) < float.Epsilon) { var tDistance = dist; for (var i = 0; i < path.Count - 1; i++) { var a = path[i]; var b = path[i + 1]; var d = a.Distance(b); if (d >= tDistance) { var direction = (b - a).LSNormalized(); var cp = a + direction * tDistance; var p = a + direction * (i == path.Count - 2 ? Math.Min(tDistance + input.RealRadius, d) : tDistance + input.RealRadius); return(new PredictionOutput { Input = input, CastPosition = cp.ToVector3(), UnitPosition = p.ToVector3(), Hitchance = HitChance.High }); } tDistance -= d; } } // Skillshot with a delay and speed. if (pLength >= dist && Math.Abs(input.Speed - float.MaxValue) > float.Epsilon) { var tDistance = dist; if ((input.Type == SkillshotType.SkillshotLine || input.Type == SkillshotType.SkillshotCone) && input.Unit.LSDistanceSquared(input.From) < 200 * 200) { tDistance += input.RealRadius; } path = path.CutPath(tDistance); var tT = 0f; for (var i = 0; i < path.Count - 1; i++) { var a = path[i]; var b = path[i + 1]; var tB = a.Distance(b) / speed; var direction = (b - a).LSNormalized(); a = a - speed * tT * direction; var sol = a.VectorMovementCollision(b, speed, input.From.ToVector2(), input.Speed, tT); var t = (float)sol[0]; var pos = (Vector2)sol[1]; if (pos.IsValid() && t >= tT && t <= tT + tB) { if (pos.LSDistanceSquared(b) < 20) { break; } var p = pos + input.RealRadius * direction; /*if (input.Type == SkillshotType.SkillshotLine) * { * var alpha = (input.From.ToVector2() - p).AngleBetween(a - b); * * if (alpha > 30 && alpha < 180 - 30) * { * var beta = (float)Math.Asin(input.RealRadius / p.Distance(input.From)); * var cp1 = input.From.ToVector2() + (p - input.From.ToVector2()).Rotated(beta); * var cp2 = input.From.ToVector2() + (p - input.From.ToVector2()).Rotated(-beta); * * pos = cp1.LSDistanceSquared(pos) < cp2.LSDistanceSquared(pos) ? cp1 : cp2; * } * }*/ return(new PredictionOutput { Input = input, CastPosition = pos.ToVector3(), UnitPosition = p.ToVector3(), Hitchance = HitChance.High }); } tT += tB; } } var position = path.Last().ToVector3(); return(new PredictionOutput { Input = input, CastPosition = position, UnitPosition = position, Hitchance = HitChance.Medium }); }
/// <summary> /// OnNewPath event for average reaction time calculations /// </summary> private static void AIHeroClient_OnNewPath(Obj_AI_Base sender, GameObjectNewPathEventArgs args) { if (!sender.IsEnemy || !sender.IsChampion() || args.IsDash) { return; } EnemyData enemy = EnemyInfo[sender.NetworkId]; if (args.Path.Length < 2) { if (!enemy.IsStopped) { enemy.StopTick = Environment.TickCount; enemy.LastWaypointTick = Environment.TickCount; enemy.IsStopped = true; enemy.Count = 0; enemy.AvgTick = 0; enemy.AvgPathLenght = 0; enemy.LastAngleDiff = 360; } } else { List <Vector2> wp = args.Path.Select(p => p.LSTo2D()).ToList(); List <Vector2> sample1 = new List <Vector2>(); wp.Insert(0, sender.ServerPosition.LSTo2D()); for (int i = 0; i < wp.Count - 1; i++) { Vector2 direction = (wp[i + 1] - wp[i]).Normalized(); sample1.Add(direction); } List <Vector2> sample2 = new List <Vector2>(); for (int i = 0; i < enemy.LastWaypoints.Count - 1; i++) { Vector2 direction = (enemy.LastWaypoints[i + 1] - enemy.LastWaypoints[i]).Normalized(); sample2.Add(direction); } if (sample1.Count() > 0 && sample2.Count() > 0) { float sample1_avg = sample1.Average(p => p.AngleBetween(Vector2.Zero)); float sample2_avg = sample2.Average(p => p.AngleBetween(Vector2.Zero)); enemy.LastAngleDiff = Math.Abs(sample2_avg - sample1_avg); } if (!enemy.LastWaypoints.SequenceEqual(wp)) { if (!enemy.IsStopped) { enemy.AvgTick = (enemy.Count * enemy.AvgTick + (Environment.TickCount - enemy.LastWaypointTick)) / ++enemy.Count; enemy.AvgPathLenght = ((enemy.Count - 1) * enemy.AvgPathLenght + wp.LSPathLength()) / enemy.Count; } enemy.LastWaypointTick = Environment.TickCount; enemy.IsStopped = false; enemy.LastWaypoints = wp; if (!enemy.IsWindupChecked) { if (Environment.TickCount - enemy.LastAATick < 300) { enemy.AvgOrbwalkTime = (enemy.AvgOrbwalkTime * enemy.OrbwalkCount + (enemy.LastWaypointTick - enemy.LastWindupTick)) / ++enemy.OrbwalkCount; } enemy.IsWindupChecked = true; } } } EnemyInfo[sender.NetworkId] = enemy; }
internal static PredictionOutput GetPositionOnPath(PredictionInput input, List<Vector2> path, float speed = -1) { speed = (Math.Abs(speed - (-1)) < float.Epsilon) ? input.Unit.MoveSpeed : speed; if (path.Count <= 1) { return new PredictionOutput { Input = input, UnitPosition = input.Unit.ServerPosition, CastPosition = input.Unit.ServerPosition, Hitchance = HitChance.VeryHigh }; } var pLength = path.LSPathLength(); //Skillshots with only a delay if (pLength >= input.Delay * speed - input.RealRadius && Math.Abs(input.Speed - float.MaxValue) < float.Epsilon) { var tDistance = input.Delay * speed - input.RealRadius; for (var i = 0; i < path.Count - 1; i++) { var a = path[i]; var b = path[i + 1]; var d = a.LSDistance(b); if (d >= tDistance) { var direction = (b - a).LSNormalized(); var cp = a + direction * tDistance; var p = a + direction * ((i == path.Count - 2) ? Math.Min(tDistance + input.RealRadius, d) : (tDistance + input.RealRadius)); return new PredictionOutput { Input = input, CastPosition = cp.To3D(), UnitPosition = p.To3D(), Hitchance = HitChance.High }; } tDistance -= d; } } //Skillshot with a delay and speed. if (pLength >= input.Delay * speed - input.RealRadius && Math.Abs(input.Speed - float.MaxValue) > float.Epsilon) { var d = input.Delay * speed - input.RealRadius; if (input.Type == SkillshotType.SkillshotLine || input.Type == SkillshotType.SkillshotCone) { if (input.From.LSDistance(input.Unit.ServerPosition, true) < 200 * 200) { d = input.Delay * speed; } } path = path.CutPath(d); var tT = 0f; for (var i = 0; i < path.Count - 1; i++) { var a = path[i]; var b = path[i + 1]; var tB = a.LSDistance(b) / speed; var direction = (b - a).LSNormalized(); a = a - speed * tT * direction; var sol = Geometry.VectorMovementCollision(a, b, speed, input.From.LSTo2D(), input.Speed, tT); var t = (float)sol[0]; var pos = (Vector2)sol[1]; if (pos.IsValid() && t >= tT && t <= tT + tB) { if (pos.LSDistance(b, true) < 20) break; var p = pos + input.RealRadius * direction; if (input.Type == SkillshotType.SkillshotLine && false) { var alpha = (input.From.LSTo2D() - p).LSAngleBetween(a - b); if (alpha > 30 && alpha < 180 - 30) { var beta = (float)Math.Asin(input.RealRadius / p.LSDistance(input.From)); var cp1 = input.From.LSTo2D() + (p - input.From.LSTo2D()).LSRotated(beta); var cp2 = input.From.LSTo2D() + (p - input.From.LSTo2D()).LSRotated(-beta); pos = cp1.LSDistance(pos, true) < cp2.LSDistance(pos, true) ? cp1 : cp2; } } return new PredictionOutput { Input = input, CastPosition = pos.To3D(), UnitPosition = p.To3D(), Hitchance = HitChance.High }; } tT += tB; } } var position = path.Last(); return new PredictionOutput { Input = input, CastPosition = position.To3D(), UnitPosition = position.To3D(), Hitchance = HitChance.Medium }; }
/// <summary> /// Gets Prediction result /// </summary> /// <param name="target">Target for spell</param> /// <param name="width">Spell width</param> /// <param name="delay">Spell delay</param> /// <param name="missileSpeed">Spell missile speed</param> /// <param name="range">Spell range</param> /// <param name="collisionable">Spell collisionable</param> /// <param name="type">Spell skillshot type</param> /// <param name="path">Waypoints of target</param> /// <param name="avgt">Average reaction time (in ms)</param> /// <param name="movt">Passed time from last movement change (in ms)</param> /// <param name="avgp">Average Path Lenght</param> /// <param name="from">Spell casted position</param> /// <param name="rangeCheckFrom"></param> /// <returns>Prediction result as <see cref="Prediction.Result" /></returns> internal static Result GetPrediction(Obj_AI_Base target, float width, float delay, float missileSpeed, float range, bool collisionable, SkillshotType type, List<Vector2> path, float avgt, float movt, float avgp, float anglediff, Vector2 from, Vector2 rangeCheckFrom) { Prediction.AssertInitializationMode(); Result result = new Result(); result.Input = new Input(target, delay, missileSpeed, width, range, collisionable, type, from.To3D2(), rangeCheckFrom.To3D2()); result.Unit = target; try { if (type == SkillshotType.SkillshotCircle) range += width; //to do: hook logic ? by storing average movement direction etc if (path.Count <= 1 && movt > 100 && (Environment.TickCount - PathTracker.EnemyInfo[target.NetworkId].LastAATick > 300 || !ConfigMenu.CheckAAWindUp)) //if target is not moving, easy to hit (and not aaing) { result.HitChance = EloBuddy.SDK.Enumerations.HitChance.High; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return result; } if (target is AIHeroClient) { if (((AIHeroClient)target).IsChannelingImportantSpell()) { result.HitChance = EloBuddy.SDK.Enumerations.HitChance.High; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return result; } if (Environment.TickCount - PathTracker.EnemyInfo[target.NetworkId].LastAATick < 300 && ConfigMenu.CheckAAWindUp) { if (target.AttackCastDelay * 1000 + PathTracker.EnemyInfo[target.NetworkId].AvgOrbwalkTime + avgt - width / 2f / target.MoveSpeed >= GetArrivalTime(target.ServerPosition.LSTo2D().LSDistance(from), delay, missileSpeed)) { result.HitChance = EloBuddy.SDK.Enumerations.HitChance.High; result.CastPosition = target.ServerPosition.LSTo2D(); result.UnitPosition = result.CastPosition; result.Lock(); return result; } } //to do: find a fuking logic if (avgp < 400 && movt < 100 && path.LSPathLength() <= avgp) { result.HitChance = EloBuddy.SDK.Enumerations.HitChance.High; result.CastPosition = path.Last(); result.UnitPosition = result.CastPosition; result.Lock(); return result; } } if (target.LSIsDashing()) //if unit is dashing return GetDashingPrediction(target, width, delay, missileSpeed, range, collisionable, type, from, rangeCheckFrom); if (Utility.IsImmobileTarget(target)) //if unit is immobile return GetImmobilePrediction(target, width, delay, missileSpeed, range, collisionable, type, from, rangeCheckFrom); result = WaypointAnlysis(target, width, delay, missileSpeed, range, collisionable, type, path, avgt, movt, avgp, anglediff, from); float d = result.CastPosition.LSDistance(target.ServerPosition.LSTo2D()); if (d >= (avgt - movt) * target.MoveSpeed && d >= avgp) result.HitChance = EloBuddy.SDK.Enumerations.HitChance.Medium; result.Lock(); return result; } finally { //check if movement changed while prediction calculations if (!target.GetWaypoints().SequenceEqual(path)) result.HitChance = EloBuddy.SDK.Enumerations.HitChance.Medium; } }