/// <summary> /// Gets Predicted position /// </summary> /// <param name="target">Target for spell</param> /// <param name="s">Spell to cast</param> /// <param name="path">Waypoint 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="hc">Predicted HitChance</param> /// <param name="rangeCheckFrom">Position where spell will be casted from</param> /// <returns>Predicted position and HitChance out value</returns> public static Vector2 GetPrediction(Obj_AI_Hero target, Spell s, List<Vector2> path, float avgt, float movt, out HitChance hc, Vector3 rangeCheckFrom) { if (!blInitialized) throw new InvalidOperationException("Prediction is not initalized"); if (target.MovImmobileTime() > 200 || target.AvgMovChangeTime() == 0 || Utility.IsImmobileTarget(target)) //if target is not moving, easy to hit { hc = HitChance.Immobile; return target.ServerPosition.To2D() + target.Direction.To2D().Perpendicular() * s.Width / 2; } if (target.IsDashing()) return GetDashingPrediction(target, s, out hc, rangeCheckFrom); float targetDistance = rangeCheckFrom.Distance(target.ServerPosition); float flyTime = 0f; if (s.Speed != 0) //skillshot with a missile { Vector2 Vt = (path[path.Count - 1] - path[0]).Normalized() * target.MoveSpeed; Vector2 Vs = (target.ServerPosition.To2D() - rangeCheckFrom.To2D()).Normalized() * s.Speed; Vector2 Vr = Vs - Vt; flyTime = targetDistance / Vr.Length(); if (path.Count > 5) //complicated movement flyTime = targetDistance / s.Speed; } float t = flyTime + s.Delay + Game.Ping / 1000f; float distance = t * target.MoveSpeed; //can be improved by checking area of circle if (s.Type == SkillshotType.SkillshotCircle) //haven't tested yet. distance -= s.Width / 2; hc = GetHitChance(t * 1000f, avgt, movt); for (int i = 0; i < path.Count - 1; i++) { float d = path[i + 1].Distance(path[i]); if (distance == d) return path[i + 1]; else if (distance < d) return path[i] + distance * (path[i + 1] - path[i]).Normalized(); else distance -= d; } hc = HitChance.Impossible; return path[path.Count - 1]; }
/// <summary> /// Gets Predicted position /// </summary> /// <param name="target">Target for spell</param> /// <param name="s">Spell to cast</param> /// <param name="path">Waypoint 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="hc">Predicted HitChance</param> /// <param name="rangeCheckFrom">Position where spell will be casted from</param> /// <returns>Predicted position and HitChance out value</returns> public static Vector2 GetPrediction(Obj_AI_Hero target, Spell s, List <Vector2> path, float avgt, float movt, out HitChance hc, Vector3 rangeCheckFrom) { if (!blInitialized) { throw new InvalidOperationException("Prediction is not initalized"); } if (target.MovImmobileTime() > 200 || target.AvgMovChangeTime() == 0 || Utility.IsImmobileTarget(target)) //if target is not moving, easy to hit { hc = HitChance.Immobile; return(target.ServerPosition.To2D() + target.Direction.To2D().Perpendicular() * s.Width / 2); } if (target.IsDashing()) { return(GetDashingPrediction(target, s, out hc, rangeCheckFrom)); } float targetDistance = rangeCheckFrom.Distance(target.ServerPosition); float flyTime = 0f; if (s.Speed != 0) //skillshot with a missile { Vector2 Vt = (path[path.Count - 1] - path[0]).Normalized() * target.MoveSpeed; Vector2 Vs = (target.ServerPosition.To2D() - rangeCheckFrom.To2D()).Normalized() * s.Speed; Vector2 Vr = Vs - Vt; flyTime = targetDistance / Vr.Length(); if (path.Count > 5) //complicated movement { flyTime = targetDistance / s.Speed; } } float t = flyTime + s.Delay + Game.Ping / 1000f; float distance = t * target.MoveSpeed; //can be improved by checking area of circle if (s.Type == SkillshotType.SkillshotCircle) //haven't tested yet. { distance -= s.Width / 2; } hc = GetHitChance(t * 1000f, avgt, movt); for (int i = 0; i < path.Count - 1; i++) { float d = path[i + 1].Distance(path[i]); if (distance == d) { return(path[i + 1]); } else if (distance < d) { return(path[i] + distance * (path[i + 1] - path[i]).Normalized()); } else { distance -= d; } } hc = HitChance.Impossible; return(path[path.Count - 1]); }
/// <summary> /// Gets Predicted position for arc /// </summary> /// <param name="target">Target for spell</param> /// <param name="s">Spell to cast</param> /// <param name="avgt">Average reaction time (in ms)</param> /// <param name="movt">Passed time from last movement change (in ms)</param> /// <param name="hc">Predicted HitChance</param> /// <param name="rangeCheckFrom">Position where spell will be casted from</param> /// <returns>Predicted position and HitChance out value</returns> public static Vector2 GetArcPrediction(Obj_AI_Hero target, Spell s, List<Vector2> path, float avgt, float movt, out HitChance hc, Vector3 rangeCheckFrom) { if (!blInitialized) throw new InvalidOperationException("Prediction is not initalized"); if (target.MovImmobileTime() > 200 || target.AvgMovChangeTime() == 0 || Utility.IsImmobileTarget(target)) //if target is not moving, easy to hit { hc = HitChance.Immobile; return target.ServerPosition.To2D(); } if (target.IsDashing()) return GetDashingPrediction(target, s, out hc, rangeCheckFrom); float targetDistance = rangeCheckFrom.Distance(target.ServerPosition); float flyTime = 0f; if (s.Speed != 0) { Vector2 Vt = (path[path.Count - 1] - path[0]).Normalized() * target.MoveSpeed; Vector2 Vs = (target.ServerPosition.To2D() - rangeCheckFrom.To2D()).Normalized() * s.Speed; Vector2 Vr = Vs - Vt; flyTime = targetDistance / Vr.Length(); if (path.Count > 5) flyTime = targetDistance / s.Speed; } float t = flyTime + s.Delay + Game.Ping / 1000f; float distance = t * target.MoveSpeed; hc = GetHitChance(t * 1000f, avgt, movt); #region arc collision test for (int i = 1; i < path.Count; i++) { Vector2 senderPos = ObjectManager.Player.ServerPosition.To2D(); Vector2 testPos = path[i]; float multp = (testPos.Distance(senderPos) / 875.0f); var dianaArc = new ShineCommon.Maths.Geometry.Polygon( ClipperWrapper.DefineArc(senderPos - new Vector2(875 / 2f, 20), testPos, (float)Math.PI * multp, 410, 200 * multp), ClipperWrapper.DefineArc(senderPos - new Vector2(875 / 2f, 20), testPos, (float)Math.PI * multp, 410, 320 * multp)); if (!ClipperWrapper.IsOutside(dianaArc, target.ServerPosition.To2D())) { hc = HitChance.VeryHigh; return testPos; } } #endregion for (int i = 0; i < path.Count - 1; i++) { float d = path[i + 1].Distance(path[i]); if (distance == d) return path[i + 1]; else if (distance < d) return path[i] + distance * (path[i + 1] - path[i]).Normalized(); else distance -= d; } hc = HitChance.Impossible; return path[path.Count - 1]; }
/// <summary> /// Gets Predicted position for arc /// </summary> /// <param name="target">Target for spell</param> /// <param name="s">Spell to cast</param> /// <param name="avgt">Average reaction time (in ms)</param> /// <param name="movt">Passed time from last movement change (in ms)</param> /// <param name="hc">Predicted HitChance</param> /// <param name="rangeCheckFrom">Position where spell will be casted from</param> /// <returns>Predicted position and HitChance out value</returns> public static Vector2 GetArcPrediction(Obj_AI_Hero target, Spell s, List <Vector2> path, float avgt, float movt, out HitChance hc, Vector3 rangeCheckFrom) { if (!blInitialized) { throw new InvalidOperationException("Prediction is not initalized"); } if (target.MovImmobileTime() > 200 || target.AvgMovChangeTime() == 0 || Utility.IsImmobileTarget(target)) //if target is not moving, easy to hit { hc = HitChance.Immobile; return(target.ServerPosition.To2D()); } if (target.IsDashing()) { return(GetDashingPrediction(target, s, out hc, rangeCheckFrom)); } float targetDistance = rangeCheckFrom.Distance(target.ServerPosition); float flyTime = 0f; if (s.Speed != 0) { Vector2 Vt = (path[path.Count - 1] - path[0]).Normalized() * target.MoveSpeed; Vector2 Vs = (target.ServerPosition.To2D() - rangeCheckFrom.To2D()).Normalized() * s.Speed; Vector2 Vr = Vs - Vt; flyTime = targetDistance / Vr.Length(); if (path.Count > 5) { flyTime = targetDistance / s.Speed; } } float t = flyTime + s.Delay + Game.Ping / 1000f; float distance = t * target.MoveSpeed; hc = GetHitChance(t * 1000f, avgt, movt); #region arc collision test for (int i = 1; i < path.Count; i++) { Vector2 senderPos = ObjectManager.Player.ServerPosition.To2D(); Vector2 testPos = path[i]; float multp = (testPos.Distance(senderPos) / 875.0f); var dianaArc = new ShineCommon.Maths.Geometry.Polygon( ClipperWrapper.DefineArc(senderPos - new Vector2(875 / 2f, 20), testPos, (float)Math.PI * multp, 410, 200 * multp), ClipperWrapper.DefineArc(senderPos - new Vector2(875 / 2f, 20), testPos, (float)Math.PI * multp, 410, 320 * multp)); if (!ClipperWrapper.IsOutside(dianaArc, target.ServerPosition.To2D())) { hc = HitChance.VeryHigh; return(testPos); } } #endregion for (int i = 0; i < path.Count - 1; i++) { float d = path[i + 1].Distance(path[i]); if (distance == d) { return(path[i + 1]); } else if (distance < d) { return(path[i] + distance * (path[i + 1] - path[i]).Normalized()); } else { distance -= d; } } hc = HitChance.Impossible; return(path[path.Count - 1]); }