/// <summary>
            ///     Returns an Area-of-Effect line prediction from a prediction input source.
            /// </summary>
            /// <param name="input">
            ///     <see cref="PredictionInput" />
            /// </param>
            /// <returns>
            ///     <see cref="PredictionOutput" />
            /// </returns>
            public static PredictionOutput GetLinePrediction(PredictionInput input)
            {
                var mainTargetPrediction = Movement.GetPrediction(input, false, true);
                var posibleTargets       = new List <PossibleTarget>
                {
                    new PossibleTarget {
                        Position = mainTargetPrediction.UnitPosition.ToVector2(), Unit = input.Unit
                    }
                };

                if (mainTargetPrediction.Hitchance >= HitChance.Medium)
                {
                    //Add the posible targets  in range:
                    posibleTargets.AddRange(GetPossibleTargets(input));
                }

                if (posibleTargets.Count > 1)
                {
                    var candidates = new List <Vector2>();
                    foreach (var targetCandidates in
                             posibleTargets.Select(
                                 target =>
                                 GetCandidates(input.From.ToVector2(), target.Position, (input.Radius), input.Range)))
                    {
                        candidates.AddRange(targetCandidates);
                    }

                    var bestCandidateHits      = -1;
                    var bestCandidate          = new Vector2();
                    var bestCandidateHitPoints = new List <Vector2>();
                    var positionsList          = posibleTargets.Select(t => t.Position).ToList();

                    foreach (var candidate in candidates)
                    {
                        if (
                            GetHits(
                                input.From.ToVector2(), candidate, (input.Radius + input.Unit.BoundingRadius / 3 - 10),
                                new List <Vector2> {
                            posibleTargets[0].Position
                        }).Count() == 1)
                        {
                            var hits      = GetHits(input.From.ToVector2(), candidate, input.Radius, positionsList).ToList();
                            var hitsCount = hits.Count;
                            if (hitsCount >= bestCandidateHits)
                            {
                                bestCandidateHits      = hitsCount;
                                bestCandidate          = candidate;
                                bestCandidateHitPoints = hits.ToList();
                            }
                        }
                    }

                    if (bestCandidateHits > 1)
                    {
                        float   maxDistance = -1;
                        Vector2 p1 = new Vector2(), p2 = new Vector2();

                        //Center the position
                        for (var i = 0; i < bestCandidateHitPoints.Count; i++)
                        {
                            for (var j = 0; j < bestCandidateHitPoints.Count; j++)
                            {
                                var startP = input.From.ToVector2();
                                var endP   = bestCandidate;
                                var proj1  = positionsList[i].ProjectOn(startP, endP);
                                var proj2  = positionsList[j].ProjectOn(startP, endP);
                                var dist   = Vector2.DistanceSquared(bestCandidateHitPoints[i], proj1.LinePoint) +
                                             Vector2.DistanceSquared(bestCandidateHitPoints[j], proj2.LinePoint);
                                if (dist >= maxDistance &&
                                    (proj1.LinePoint - positionsList[i]).AngleBetween(
                                        proj2.LinePoint - positionsList[j]) > 90)
                                {
                                    maxDistance = dist;
                                    p1          = positionsList[i];
                                    p2          = positionsList[j];
                                }
                            }
                        }

                        return(new PredictionOutput
                        {
                            Hitchance = mainTargetPrediction.Hitchance,
                            AoeHitCount = bestCandidateHits,
                            UnitPosition = mainTargetPrediction.UnitPosition,
                            CastPosition = ((p1 + p2) * 0.5f).ToVector3(),
                            Input = input
                        });
                    }
                }

                return(mainTargetPrediction);
            }
            /// <summary>
            ///     Returns an Area-of-Effect cone prediction from a prediction input source.
            /// </summary>
            /// <param name="input">
            ///     <see cref="PredictionInput" />
            /// </param>
            /// <returns>
            ///     <see cref="PredictionOutput" />
            /// </returns>
            public static PredictionOutput GetConePrediction(PredictionInput input)
            {
                var mainTargetPrediction = Movement.GetPrediction(input, false, true);
                var posibleTargets       = new List <PossibleTarget>
                {
                    new PossibleTarget {
                        Position = mainTargetPrediction.UnitPosition.ToVector2(), Unit = input.Unit
                    }
                };

                if (mainTargetPrediction.Hitchance >= HitChance.Medium)
                {
                    //Add the posible targets  in range:
                    posibleTargets.AddRange(GetPossibleTargets(input));
                }

                if (posibleTargets.Count > 1)
                {
                    var candidates = new List <Vector2>();

                    foreach (var target in posibleTargets)
                    {
                        target.Position = target.Position - input.From.ToVector2();
                    }

                    for (var i = 0; i < posibleTargets.Count; i++)
                    {
                        for (var j = 0; j < posibleTargets.Count; j++)
                        {
                            if (i != j)
                            {
                                var p = (posibleTargets[i].Position + posibleTargets[j].Position) * 0.5f;
                                if (!candidates.Contains(p))
                                {
                                    candidates.Add(p);
                                }
                            }
                        }
                    }

                    var bestCandidateHits = -1;
                    var bestCandidate     = new Vector2();
                    var positionsList     = posibleTargets.Select(t => t.Position).ToList();

                    foreach (var candidate in candidates)
                    {
                        var hits = GetHits(candidate, input.Range, input.Radius, positionsList);
                        if (hits > bestCandidateHits)
                        {
                            bestCandidate     = candidate;
                            bestCandidateHits = hits;
                        }
                    }

                    if (bestCandidateHits > 1 && input.From.ToVector2().DistanceSquared(bestCandidate) > 50 * 50)
                    {
                        return(new PredictionOutput
                        {
                            Hitchance = mainTargetPrediction.Hitchance,
                            AoeHitCount = bestCandidateHits,
                            UnitPosition = mainTargetPrediction.UnitPosition,
                            CastPosition = bestCandidate.ToVector3(),
                            Input = input
                        });
                    }
                }
                return(mainTargetPrediction);
            }
Beispiel #3
0
        /// <summary>
        ///     Returns Calculated Prediction based off given data values.
        /// </summary>
        /// <param name="input">
        ///     <see cref="PredictionInput" />
        /// </param>
        /// <param name="ft">Add Delay</param>
        /// <param name="checkCollision">Check Collision</param>
        /// <returns>
        ///     <see cref="PredictionOutput" />
        /// </returns>
        internal static PredictionOutput GetPrediction(PredictionInput input, bool ft, bool checkCollision)
        {
            PredictionOutput result = null;

            if (!input.Unit.IsValidTarget(float.MaxValue, false))
            {
                return(new PredictionOutput());
            }

            if (ft)
            {
                //Increase the delay due to the latency and server tick:
                input.Delay += Game.Ping / 2000f + 0.05f;

                if (input.Aoe)
                {
                    return(Cluster.GetAoEPrediction(input));
                }
            }

            //Target too far away.
            if (System.Math.Abs(input.Range - float.MaxValue) > float.Epsilon &&
                input.Unit.DistanceSquared(input.RangeCheckFrom) > System.Math.Pow(input.Range * 1.5, 2))
            {
                return(new PredictionOutput {
                    Input = input
                });
            }

            //Unit is dashing.
            if (input.Unit.IsDashing())
            {
                result = GetDashingPrediction(input);
            }
            else
            {
                //Unit is immobile.
                var remainingImmobileT = UnitIsImmobileUntil(input.Unit);
                if (remainingImmobileT >= 0d)
                {
                    result = GetImmobilePrediction(input, remainingImmobileT);
                }
            }

            //Normal prediction
            if (result == null)
            {
                result = GetStandardPrediction(input);
            }

            //Check if the unit position is in range
            if (System.Math.Abs(input.Range - float.MaxValue) > float.Epsilon)
            {
                if (result.Hitchance == HitChance.High &&
                    input.RangeCheckFrom.DistanceSquared(input.Unit.Position) >
                    System.Math.Pow(input.Range + input.RealRadius * 3 / 4, 2))
                {
                    result.Hitchance = HitChance.Medium;
                }

                if (input.RangeCheckFrom.DistanceSquared(result.UnitPosition) >
                    System.Math.Pow(
                        input.Range + (input.Type == SkillshotType.SkillshotCircle ? input.RealRadius : 0), 2))
                {
                    result.Hitchance = HitChance.OutOfRange;
                }

                if (input.RangeCheckFrom.DistanceSquared(result.CastPosition) > System.Math.Pow(input.Range, 2))
                {
                    if (result.Hitchance != HitChance.OutOfRange)
                    {
                        result.CastPosition = input.RangeCheckFrom +
                                              input.Range *
                                              (result.UnitPosition - input.RangeCheckFrom).Normalized().SetZ();
                    }
                    else
                    {
                        result.Hitchance = HitChance.OutOfRange;
                    }
                }
            }

            //Check for collision
            if (checkCollision && input.Collision)
            {
                var positions = new List <Vector3> {
                    result.UnitPosition, result.CastPosition, input.Unit.Position
                };
                var originalUnit = input.Unit;
                result.CollisionObjects = Collision.GetCollision(positions, input);
                result.CollisionObjects.RemoveAll(x => x.NetworkId == originalUnit.NetworkId);
                result.Hitchance = result.CollisionObjects.Count > 0 ? HitChance.Collision : result.Hitchance;
            }

            return(result);
        }
Beispiel #4
0
 /// <summary>
 ///     Returns Calculated Prediction based off given data values.
 /// </summary>
 /// <param name="input">
 ///     <see cref="PredictionInput" />
 /// </param>
 /// <returns>
 ///     <see cref="PredictionOutput" />
 /// </returns>
 public static PredictionOutput GetPrediction(PredictionInput input)
 {
     return(GetPrediction(input, true, true));
 }
Beispiel #5
0
        /// <summary>
        ///     Get Position on Unit's Path.
        /// </summary>
        /// <param name="input">
        ///     <see cref="PredictionInput" />
        /// </param>
        /// <param name="path">Path in Vector2 List</param>
        /// <param name="speed">Unit Speed</param>
        /// <returns></returns>
        internal static PredictionOutput GetPositionOnPath(PredictionInput input, List <Vector2> path, float speed = -1)
        {
            speed = (System.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.PathLength();

            //Skillshots with only a delay
            if (pLength >= input.Delay * speed - input.RealRadius &&
                System.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.Distance(b);

                    if (d >= tDistance)
                    {
                        var direction = (b - a).Normalized();

                        var cp = a + direction * tDistance;
                        var p  = a +
                                 direction *
                                 ((i == path.Count - 2)
                                    ? System.Math.Min(tDistance + input.RealRadius, d)
                                    : (tDistance + input.RealRadius));

                        return(new PredictionOutput
                        {
                            Input = input,
                            CastPosition = cp.ToVector3(),
                            UnitPosition = p.ToVector3(),
                            Hitchance =
                                Path.PathTracker.GetCurrentPath(input.Unit).Time < 0.1d
                                    ? HitChance.VeryHigh
                                    : HitChance.High
                        });
                    }

                    tDistance -= d;
                }
            }

            //Skillshot with a delay and speed.
            if (pLength >= input.Delay * speed - input.RealRadius &&
                System.Math.Abs(input.Speed - float.MaxValue) > float.Epsilon)
            {
                path = path.CutPath(System.Math.Max(0, input.Delay * speed - input.RealRadius));
                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).Normalized();
                    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)
                    {
                        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)System.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.DistanceSquared(pos) < cp2.DistanceSquared(pos) ? cp1 : cp2;
                            }
                        }

                        return(new PredictionOutput
                        {
                            Input = input,
                            CastPosition = pos.ToVector3(),
                            UnitPosition = p.ToVector3(),
                            Hitchance =
                                Path.PathTracker.GetCurrentPath(input.Unit).Time < 0.1d
                                    ? HitChance.VeryHigh
                                    : HitChance.High
                        });
                    }
                    tT += tB;
                }
            }

            var position = path.Last();

            return(new PredictionOutput
            {
                Input = input,
                CastPosition = position.ToVector3(),
                UnitPosition = position.ToVector3(),
                Hitchance = HitChance.Medium
            });
        }