Example #1
0
        /// <summary>
        /// Calculates the vector used for acceleration using the current velocity.
        /// The current velocity is only used to detarmaine the direction.
        /// </summary>
        /// <param name="velocity">Current velocity of the car</param>
        /// <returns>Acceleration vector</returns>
        private static Vector CalculateAccelerationVector(Vector velocity)
        {
            var clone = new Vector(velocity.X, velocity.Y);

            clone.Normalize();
            return(Vector.Divide(clone, accelerationDivider));
        }
Example #2
0
        /// <summary>
        /// Makes the car approach the target and slow down.
        /// </summary>
        /// <param name="velocity">Current velocity of the car</param>
        /// <param name="yaw">Current relative yaw of the car</param>
        /// <param name="addedRotation">Amount of rotation (in degrees) to add</param>
        /// <param name="distanceToDestination">Distance to the destination</param>
        public void HandleApproachTarget(ref Vector velocity, ref double yaw, ref double addedRotation, ref double distanceToDestination)
        {
            // Get the current speed and rotation of the car.
            var speed    = velocity.Length;
            var rotation = MathUtil.VelocityToRotation(velocity);

            // Check if we're going too fast, have passengers or are near the destination.
            if (speed > maxTurningSpeed / 2d || Car.PassengerCount > 0 || distanceToDestination < 20)
            {
                // Check if we need to drop passengers off.
                if (Car.PassengerCount > 0 && /*speed < 0.05 && */ speed > 0)
                {
                    var distance = Car.DistanceTraveled - Car.PassengersBoardDistance;
                    var price    = GPSSystem.CalculatePrice(distance);
                    App.Console.Print($"[C{Car.Id}] Charged €{price:F2} for {distance:F1} units", Colors.Blue);

                    App.Console.Print($"[C{Car.Id}] Dropping customers", Colors.Blue);
                    var passenger = Car.Passengers[0];
                    TripEnd?.Invoke(this, new TripEventArgs(passenger.Group, passenger.Building.Location, Car.Location, Car));
                    Car.Passengers.Clear();
                    MainScreen.AILoop.Unsubscribe(Update);
                }

                // Slow down the car.
                velocity = speed > 0.05
                    ? Vector.Add(velocity, CalculateDeccelerationVector(velocity))
                    : new Vector();
            }

            // Get the target and current location.
            var destination = Car.Destination.Location;
            var location    = Car.Location;

            // Calculate the angle between the car and the target.
            var sub = new Vector(
                destination.X - location.X,
                destination.Y - location.Y
                );

            sub.Normalize();
            var angle = Vector.AngleBetween(rotation, sub);

            // If we need to go to the right and are able to, do so.
            if (angle > 0)
            {
                if (yaw > -maxInLaneRotation)
                {
                    addedRotation -= rotationSpeed;
                }
            }
            // If we need to go to the left and are able to, do so.
            else
            {
                if (yaw < maxInLaneRotation)
                {
                    addedRotation += rotationSpeed;
                }
            }
        }
        private void MoveBack()
        {
            Vector destinationDirection = startPos - Character.Position;

            destinationDirection.Normalize();
            Point screenPoint = ScreenPosition.GetPointForDirection(destinationDirection);

            PerformClickMove(screenPoint);
        }
        public static void SetUpDirection(Vector direction)
        {
            if (direction.Length < UP_DIRECTION_LENGTH)
            {
                throw new Exception($"direction must be of length {UP_DIRECTION_LENGTH}.");
            }

            direction.Normalize();
            instance.upDirection = direction;
        }
 public override void OnEnter()
 {
     base.OnEnter();
     timerStarted = false;
     SetInitialClickPos();
     clickPos  = initClickPos;
     direction = clickPos - ScreenPosition.Center;
     direction.Normalize();
     TryOpenChest();
 }
Example #6
0
        private Point GetDirectionArrowPoint()
        {
            System.Windows.Vector vector = new System.Windows.Vector(this._line.X2 - this._line.X1,
                                                                     this._line.Y2 - this._line.Y1);
            System.Windows.Vector desiredVector = vector;
            desiredVector.Normalize();
            double desiredLength = 10 /*(vector - desiredVector).Length * .1*/;

            vector -= (desiredVector * desiredLength);

            return(new Point((int)(this._line.X1 + vector.X),
                             (int)(this._line.Y1 + vector.Y)));
        }
Example #7
0
        public static Vector Translate(Vector v, Vector axis, double length)
        {
            if (axis.Length == 0)
            {
                throw new ArgumentException("Translate axis cannot be of zero length.");
            }

            axis.Normalize();
            Vector translation = axis * length;
            Vector translated  = v + translation;

            return(translated);
        }
Example #8
0
        public PointF LocationIndexSuper(Cyotek.Windows.Forms.ImageBox i)
        {
            System.Windows.Vector v = new System.Windows.Vector(pointOfProfile.Point.X - pointOnProfile.Point.X, pointOfProfile.Point.Y - pointOnProfile.Point.Y);
            v.Normalize();
            double distancePlus = GetDistanceBetween(pointOnProfile.Point, pointOfProfile.Point) + (20);

            v = System.Windows.Vector.Multiply(distancePlus, v);
            PointF p = new PointF((float)(pointOnProfile.Point.X + v.X), (float)(pointOnProfile.Point.Y + v.Y));

            p.X -= 3;
            p.Y -= 3;

            return(p);
        }
Example #9
0
        private Point GetCompactLocation(Rectangle target)
        {
            if (target.GetCenter() == Center)
            {
                return(target.Location);
            }
            var    tempRect         = target;
            var    previousLocation = tempRect.Location;
            double x           = tempRect.Location.X;
            double y           = tempRect.Location.Y;
            var    deltaVector = new System.Windows.Vector(Center.X - x, Center.Y - y);

            deltaVector.Normalize();

            while (!tempRect.IsCrossing(rectangles.Where(r => !r.Equals(target)).ToList()))
            {
                previousLocation = tempRect.Location;
                x += deltaVector.X;
                y += deltaVector.Y;
                tempRect.Location = new Point((int)x, (int)y);
            }

            return(previousLocation);
        }
Example #10
0
        public static bool IsInsideAngle(Point vectorOriginPoint, System.Windows.Vector v1, System.Windows.Vector v2, Point p)
        {
            var center = vectorOriginPoint;

            //make angle with 2 vectors
            var angle = System.Windows.Vector.AngleBetween(v1, v2);
            var halfAngle = angle / 2;

            v1.Normalize();
            v2.Normalize();

            //vector that bisects angle between v1 and v2
            var bisector = v1 + v2;
            bisector.Normalize();


            var vectorToPoint = new System.Windows.Vector(p.X - center.X, p.Y - center.Y);
            vectorToPoint.Normalize();

            var AngleBetweenBisectorAndVectorToPoint = Math.Abs(System.Windows.Vector.AngleBetween(bisector, vectorToPoint));

            //if angle between bisector and vector to point p is less than half of angle v1v2 then point lies inside the angle
            return AngleBetweenBisectorAndVectorToPoint <= halfAngle;
        }
        private static void MouseMovement_DeadZoning()
        {
            // Each game may use "dead zones" of different size and shape to eliminate near-but-not-zero readings
            // that controllers give at rest. (See https://itstillworks.com/12629709/what-is-a-dead-zone-in-an-fps.)
            // Since mice do not suffer from this phenomenon at rest, users expect reaction out of the minimum
            // possible mouse movements. To translate this to joystick positions effectively, we want that single
            // pixel of mouse movement to position the joystick just past the joystick dead zone. (For effective
            // mouse control of any given game, we will have to discover and track dead zone details as part of the
            // control profile settings, or provide an auto-discovery mechanism that watches for screen change in
            // order to automatically determine the dead zone details. Difficult, but doable. TODO: ATTEMPT THIS!)
            // Possibly other details would need to be stored per game, as advanced techniques to combat other
            // game-specific joystick assists (weird accelerations and such) get figured out. Either way, usually
            // the user should get best results out of tuning their in-game sensitivities way up, to allow for the
            // mouse-to-joystick translation to yield a high, accurate range, including fast mouse flicks to attain
            // fast turning.
            // Dead zones are usually square or circular, meaning the game will ignore small stick positions when:
            // * The absolute X and Y values of the stick are less than some value (for a "square" dead zone).
            // * The (X,Y) vector length is less than some value (for a "circular" dead zone).
            // For simplicity, we'll assume circular dead zones, because the math to scale into the range of legal
            // non-ignored values for a square dead zone is more complicated than may appear (so the first attempt
            // was glitchy), and vector math results work quite well regardless of the dead zone shape.

            // Grab and reset mouse position and time passed, for calculating mouse velocity for this polling.
            var mouseX = Cursor.Position.X;
            var mouseY = Cursor.Position.Y;

            Cursor.Position = centered;
            var timeSinceLastPoll = stopwatch.Elapsed.TotalMilliseconds;

            stopwatch.Restart();

            // Mouse velocity here is average pixels per milliseconds passed since the last polling. This helps
            // get correct-feeling, smooth movements, even if the thread pool is not being particularly nice to
            // our polling thread ATM. For example, if the thread pool gives us a 22ms cycle from last poll with
            // 11 pixels of movement on one pass, then gave us a 16ms cycle with 8 pixels of movement on another,
            // we'd want the same final stick position for both since the user did not vary their mouse velocity.
            // Also invert these values now if the user has configured input inversion.
            var    changeX           = Program.ActiveConfig.Mouse_Invert_X ? centered.X - mouseX : mouseX - centered.X;
            var    changeY           = Program.ActiveConfig.Mouse_Invert_Y ? mouseY - centered.Y : centered.Y - mouseY;
            double sensitivityScaleX = Program.ActiveConfig.Mouse_Sensitivity_X / 1000.0;
            double sensitivityScaleY = Program.ActiveConfig.Mouse_Sensitivity_Y / 1000.0;
            double velocityX         = changeX * sensitivityScaleX / timeSinceLastPoll;
            double velocityY         = changeY * sensitivityScaleY / timeSinceLastPoll;

            short joyX = 0, joyY = 0;

            var deadZoneSize = Program.ActiveConfig.DeadZoneSize;

            if (deadZoneCalibrator != null)
            {
                // Pretend the mouse is always moving one pixel in each axis, until user intervention.
                deadZoneSize = deadZoneCalibrator.AdvanceDeadZoneSize();
                joyX         = Convert.ToInt16(deadZoneSize);
                joyY         = 0;
            }
            else if (velocityX != 0 || velocityY != 0)
            {
                // Find the percentage of the max mouse vector was travelled, in order to scale the final
                // vector by the full dead zone plus the same percent of the non-dead-zone area.
                // This will allow tiny movements to be just outside the dead-zone but in the correct
                // vector, while large movements scale out to the outer edge of possible stick positions,
                // but still at the correct proportions.

                var mouseVectorLengthToReachMaxStickPosition = 5d;
                var mouseVector           = new System.Windows.Vector(velocityX, velocityY);
                var percentMouseMagnitude = mouseVector.Length / mouseVectorLengthToReachMaxStickPosition;
                percentMouseMagnitude = Math.Min(percentMouseMagnitude, 1.0);

                // Normalize the mouse vector in preparation for changing to the target stick scale.
                mouseVector.Normalize();

                // The mouseVector is now a raw direction that we want to multiply up into the stick
                // range past the (assumed-to-be-circular) dead zone.
                var remainingStickMagnitude = short.MaxValue - deadZoneSize;
                var targetMagnitude         = deadZoneSize + remainingStickMagnitude * percentMouseMagnitude;
                mouseVector *= targetMagnitude;

                joyX = Convert.ToInt16(mouseVector.X);
                joyY = Convert.ToInt16(mouseVector.Y);
            }

            // Send Axis
            SetAxis(joyX, joyY);
        }
Example #12
0
 public static VECTOR Normalize(VECTOR v) { v.Normalize(); return v; }
Example #13
0
 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static VECTOR Normalize(VECTOR v) { v.Normalize(); return v; }
Example #14
0
 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static VECTOR Normalize(VECTOR v)
 {
     v.Normalize(); return(v);
 }
Example #15
0
 public static VECTOR Normalize(VECTOR v)
 {
     return(VECTOR.Normalize(v));
 }
Example #16
0
 public static VECTOR Normalize(VECTOR v)
 {
     v.Normalize(); return(v);
 }
Example #17
0
        private PointF pointSurfaceCoor(double dist, bool down)
        {
            int    i             = 0;
            double droveDistance = 0;
            double upperDistance = 0;
            double lowerDistance = 0;
            PointF pointTmp      = new PointF();
            int    flag          = 0;

            for (i = 0; i < MainWindow.nacaProfile.Count - 1; i++)
            {
                if (flag == 1 && MainWindow.nacaProfile[i].X == 0 && MainWindow.nacaProfile[i].Y == 0)
                {
                    flag = 2;
                }
                if (flag == 0)
                {
                    upperDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);
                    if (MainWindow.nacaProfile[i + 1].X == 0 ||
                        (MainWindow.nacaProfile[i].X <= MainWindow.A.X && MainWindow.nacaProfile[i + 1].X >= MainWindow.A.X))
                    {
                        flag = 1;
                    }
                }
                else if (flag == 2)
                {
                    lowerDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);
                    if (MainWindow.nacaProfile[i + 1].X == 0 ||
                        (MainWindow.nacaProfile[i].X <= MainWindow.B.X && MainWindow.nacaProfile[i + 1].X >= MainWindow.B.X))
                    {
                        break;
                    }
                }
            }

            for (i = 0; i < MainWindow.nacaProfile.Count - 1; i++)
            {
                if (MainWindow.nacaProfile[i].Y >= 0 && MainWindow.nacaProfile[i + 1].Y > 0 && down)
                {
                    droveDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);

                    if (droveDistance >= dist * lowerDistance)
                    {
                        System.Windows.Vector v = new System.Windows.Vector(MainWindow.nacaProfile[i].X - MainWindow.nacaProfile[i + 1].X,
                                                                            MainWindow.nacaProfile[i].Y - MainWindow.nacaProfile[i + 1].Y);
                        v.Normalize();
                        v = System.Windows.Vector.Multiply(droveDistance - dist * lowerDistance, v);

                        pointTmp = new PointF((float)(MainWindow.nacaProfile[i + 1].X + v.X), (float)(MainWindow.nacaProfile[i + 1].Y + v.Y));
                        break;
                    }
                }
                else if (MainWindow.nacaProfile[i].Y <= 0 && MainWindow.nacaProfile[i + 1].Y < 0 && !down)
                {
                    droveDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);

                    if (droveDistance >= dist * upperDistance)
                    {
                        System.Windows.Vector v = new System.Windows.Vector(MainWindow.nacaProfile[i].X - MainWindow.nacaProfile[i + 1].X,
                                                                            MainWindow.nacaProfile[i].Y - MainWindow.nacaProfile[i + 1].Y);
                        v.Normalize();
                        v = System.Windows.Vector.Multiply(droveDistance - dist * upperDistance, v);

                        pointTmp = new PointF((float)(MainWindow.nacaProfile[i + 1].X + v.X), (float)(MainWindow.nacaProfile[i + 1].Y + v.Y));
                        break;
                    }
                }
            }
            PointF leading = new PointF();
            PointF falling = new PointF();

            if (MainWindow.level != 3)
            {
                leading = MainWindow.calibrateProfilePoint1.Point;
                falling = MainWindow.calibrateProfilePoint2.Point;
            }
            else if (MainWindow.level == 3)
            {
                leading = window.images.getActual().point1.Point;
                falling = window.images.getActual().point2.Point;
            }

            double d = MainWindow.GetDistanceBetween(leading, falling);

            if (MainWindow.PercentRealLenght == 100)
            {
                MainWindow.PercentRealLenght = 99.999f;
            }
            d = (d / MainWindow.PercentRealLenght) * 100;

            pointTmp.X *= (float)d;
            pointTmp.Y *= (float)d;

            return(pointTmp);
        }
Example #18
0
        public PointF LocationIndexSuper(Cyotek.Windows.Forms.ImageBox i)
        {
            System.Windows.Vector v = new System.Windows.Vector(pointOfProfile.Point.X - pointOnProfile.Point.X, pointOfProfile.Point.Y - pointOnProfile.Point.Y);
            v.Normalize();
            double distancePlus = GetDistanceBetween(pointOnProfile.Point, pointOfProfile.Point) + (20);
            v = System.Windows.Vector.Multiply(distancePlus, v);
            PointF p = new PointF((float)(pointOnProfile.Point.X + v.X), (float)(pointOnProfile.Point.Y + v.Y));
            p.X -= 3;
            p.Y -= 3;

            return p;
        }
Example #19
0
        private void ComputePointsForRealLength()
        {
            double xPoint = realLength / idealLength;

            int i = 0;
            for (i = 0; i < nacaProfile.Count-1; i++)
            {
                if (nacaProfile[i + 1].X == 0)
                    continue;

                if (nacaProfile[i].Y > 0)
                {
                    if ((nacaProfile[i].X > xPoint && nacaProfile[i + 1].X < xPoint) || (nacaProfile[i].X < xPoint && nacaProfile[i + 1].X > xPoint)) // testing if [i]---p---[i+1] or [i+1]---p---[i]
                    {
                        double fDistance; // distance on x-axis between point of real length and point on right/left side
                        double sDistance; // distance on x-axis between point of real length and point on right/left side
                        double ratio;
                        double distance; // distance between those two points

                        fDistance = Math.Abs(xPoint - nacaProfile[i].X);
                        sDistance = Math.Abs(nacaProfile[i + 1].X - xPoint);

                        ratio = fDistance / sDistance;
                        System.Windows.Vector v1 = new System.Windows.Vector(nacaProfile[i].X - nacaProfile[i + 1].X, nacaProfile[i].Y - nacaProfile[i + 1].Y);
                        distance = GetDistanceBetween(nacaProfile[i], nacaProfile[i + 1]);
                        double lenghtNewVector = (distance * sDistance) / (fDistance + sDistance);
                        v1.Normalize();
                        v1 = System.Windows.Vector.Multiply(lenghtNewVector, v1);

                        A = new PointF((float)(nacaProfile[i + 1].X + v1.X), (float)(nacaProfile[i + 1].Y + v1.Y));
                    }
                    else if (nacaProfile[i].X == xPoint)
                    {
                        A = new PointF((float)(nacaProfile[i].X), (float)(nacaProfile[i].Y));
                    }
                    else if (nacaProfile[i + 1].X == xPoint)
                    {
                        A = new PointF((float)(nacaProfile[i + 1].X), (float)(nacaProfile[i + 1].Y));
                    }
                }
                else if (nacaProfile[i].Y < 0)
                {
                    if ((nacaProfile[i].X > xPoint && nacaProfile[i + 1].X < xPoint) || (nacaProfile[i].X < xPoint && nacaProfile[i + 1].X > xPoint)) // testing if [i]---p---[i+1] or [i+1]---p---[i]
                    {
                        double fDistance; // distance on x-axis between point of real length and point on right/left side
                        double sDistance; // distance on x-axis between point of real length and point on right/left side
                        double ratio;
                        double distance; // distance between those two points

                        fDistance = Math.Abs(xPoint - nacaProfile[i].X);
                        sDistance = Math.Abs(nacaProfile[i + 1].X - xPoint);

                        ratio = fDistance / sDistance;
                        System.Windows.Vector v1 = new System.Windows.Vector(nacaProfile[i].X - nacaProfile[i + 1].X, nacaProfile[i].Y - nacaProfile[i + 1].Y);
                        distance = GetDistanceBetween(nacaProfile[i], nacaProfile[i + 1]);
                        double lenghtNewVector = (distance * sDistance) / (fDistance + sDistance);
                        v1.Normalize();
                        v1 = System.Windows.Vector.Multiply(lenghtNewVector, v1);

                        B = new PointF((float)(nacaProfile[i + 1].X + v1.X), (float)(nacaProfile[i + 1].Y + v1.Y));
                    }
                    else if (nacaProfile[i].X == xPoint)
                    {
                        B = new PointF((float)(nacaProfile[i].X), (float)(nacaProfile[i].Y));
                    }
                    else if (nacaProfile[i + 1].X == xPoint)
                    {
                        B = new PointF((float)(nacaProfile[i + 1].X), (float)(nacaProfile[i + 1].Y));
                    }
                }
            }
        }
Example #20
0
        private PointF pointSurfaceCoor(double dist, bool down)
        {
            int i = 0;
            double droveDistance = 0;
            double upperDistance = 0;
            double lowerDistance = 0;
            PointF pointTmp = new PointF();
            int flag = 0;

            for (i = 0; i < MainWindow.nacaProfile.Count - 1; i++)
            {
                if (flag == 1 && MainWindow.nacaProfile[i].X == 0 && MainWindow.nacaProfile[i].Y == 0)
                    flag = 2;
                if (flag == 0)
                {
                    upperDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);
                    if (MainWindow.nacaProfile[i + 1].X == 0 ||
                       (MainWindow.nacaProfile[i].X <= MainWindow.A.X && MainWindow.nacaProfile[i + 1].X >= MainWindow.A.X))
                    {
                        flag = 1;
                    }
                }
                else if (flag == 2)
                {
                    lowerDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);
                    if (MainWindow.nacaProfile[i + 1].X == 0 ||
                       (MainWindow.nacaProfile[i].X <= MainWindow.B.X && MainWindow.nacaProfile[i + 1].X >= MainWindow.B.X))
                    {
                        break;
                    }
                }
            }

            for (i = 0; i < MainWindow.nacaProfile.Count - 1; i++)
            {
                if (MainWindow.nacaProfile[i].Y >= 0 && MainWindow.nacaProfile[i + 1].Y > 0 && down)
                {
                    droveDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);

                    if (droveDistance >= dist * lowerDistance)
                    {
                        System.Windows.Vector v = new System.Windows.Vector(MainWindow.nacaProfile[i].X - MainWindow.nacaProfile[i + 1].X,
                            MainWindow.nacaProfile[i].Y - MainWindow.nacaProfile[i + 1].Y);
                        v.Normalize();
                        v = System.Windows.Vector.Multiply(droveDistance - dist * lowerDistance, v);

                        pointTmp = new PointF((float)(MainWindow.nacaProfile[i + 1].X + v.X), (float)(MainWindow.nacaProfile[i + 1].Y + v.Y));
                        break;
                    }
                }
                else if (MainWindow.nacaProfile[i].Y <= 0 && MainWindow.nacaProfile[i + 1].Y < 0 && !down)
                {
                    droveDistance += MainWindow.GetDistanceBetween(MainWindow.nacaProfile[i], MainWindow.nacaProfile[i + 1]);

                    if (droveDistance >= dist * upperDistance)
                    {
                        System.Windows.Vector v = new System.Windows.Vector(MainWindow.nacaProfile[i].X - MainWindow.nacaProfile[i + 1].X,
                            MainWindow.nacaProfile[i].Y - MainWindow.nacaProfile[i + 1].Y);
                        v.Normalize();
                        v = System.Windows.Vector.Multiply(droveDistance - dist * upperDistance, v);

                        pointTmp = new PointF((float)(MainWindow.nacaProfile[i + 1].X + v.X), (float)(MainWindow.nacaProfile[i + 1].Y + v.Y));
                        break;
                    }
                }
            }
            PointF leading = new PointF();
            PointF falling = new PointF();

            if (MainWindow.level != 3)
            {
                leading = MainWindow.calibrateProfilePoint1.Point;
                falling = MainWindow.calibrateProfilePoint2.Point;
            }
            else if (MainWindow.level == 3)
            {
                leading = window.images.getActual().point1.Point;
                falling = window.images.getActual().point2.Point;
            }

            double d = MainWindow.GetDistanceBetween(leading, falling);
            if (MainWindow.PercentRealLenght == 100)
                MainWindow.PercentRealLenght = 99.999f;
            d = (d / MainWindow.PercentRealLenght) * 100;

            pointTmp.X *= (float)d;
            pointTmp.Y *= (float)d;

            return pointTmp;
        }
Example #21
0
        /// <summary>
        /// Runs the main car logic. Needs to be subscribed to the MainLoop.
        /// </summary>
        public void Update()
        {
            // If we're not initialized, initialize!
            if (!initialized)
            {
                Init();
            }

            // Update the passenger count.
            Car.PassengerCount = Car.Passengers.Count;

            // Update the current road with the road at our location.
            Car.CurrentRoad         = GPSSystem.GetRoad(Car.Location);
            Car.CurrentIntersection = GPSSystem.FindIntersection(Car.Location);

            if (Car.CurrentRoad == null && Car.CurrentIntersection == null)
            {
                if (!resetTarget)
                {
                    resetTarget = true;
                    var closestRoad = GPSSystem.NearestRoad(Car.Location);
                    if (closestRoad != null && Car != null)
                    {
                        Car.CurrentTarget =
                            MathUtil.Distance(closestRoad.End, Car.Location) >
                            MathUtil.Distance(closestRoad.Start, Car.Location)
                                ? closestRoad.Start
                                : closestRoad.End;
                        App.Console.Print($"[C{Car.Id}] Lost road, trying to get back on, targeting {Car.CurrentTarget}", Colors.Blue);
                    }
                    ;
                }
            }
            else if (resetTarget)
            {
                App.Console.Print($"[C{Car.Id}] Road found again", Colors.Blue);
                resetTarget = false;
            }

            // Calculate the distance to the local target (usually the next intersection).
            var distanceToTarget = MathUtil.Distance(Car.Location, Car.CurrentTarget);
            // Calculate the distance to the destination.
            var distanceToDestination = MathUtil.Distance(Car.Location, Car.Destination.Location);
            // Calculate the relative yaw (in degrees).
            var yaw = MathUtil.VectorToAngle(Car.Rotation, Car.Direction);
            // Get the current velocity of the car.
            var velocity = Car.Velocity;
            // Create a variable to store the added rotation in this update call.
            var addedRotation      = 0.0;
            var closeToDestination = CloseToDestination();

            // Check if we're close to our target but not the destination.
            if (distanceToTarget < 20 && !closeToDestination && !resetTarget)
            {
                // Check if we've not obtained a new target yet.
                if (!obtainedNewTarget)
                {
                    // Find the nearest intersection.
                    var intersection = GPSSystem.FindIntersection(GetClosestRoadPoint(Car.Location));
                    if (intersection == null)
                    {
                        return;
                    }

                    App.Console.Print($"[C{Car.Id}] Requesting new target from intersection {intersection.Location}...", Colors.Blue);

                    // Request the next target from the GPSSystem.
                    var target = GPSSystem.GetDirection(Car, intersection);

                    // Update our target.
                    newTarget         = target.Location;
                    obtainedNewTarget = true;

                    var distance = Math.Round(MathUtil.Distance(newTarget, Car.Location));
                    App.Console.Print($"[C{Car.Id}] Obtained a new target {newTarget} ({distance}m away)", Colors.Blue);
                }
            }
            else
            {
                obtainedNewTarget = false;
            }

            // Check if we are turning.
            if (turning > 0)
            {
                turning--;

                // Check if we locked on the new target yet.
                if (newTarget.X > -1)
                {
                    // Lock on the new target and reset newTarget.
                    Car.CurrentTarget = newTarget;
                    newTarget         = new Vector(-1, -1);
                    App.Console.Print($"[C{Car.Id}] Locked on to target", Colors.Blue);
                }

                // Call the handle function to turn.
                HandleTurn(ref velocity, ref yaw, ref addedRotation);
            }
            // Check if we're still turning around.
            else if (flipping)
            {
                // Stop turning around.
                flipping = false;
                App.Console.Print($"[C{Car.Id}] Turn-around done", Colors.Blue);
            }

            // Check if we're on an intersection.
            if (Car.CurrentIntersection != null)
            {
                // Reset our turn timer.
                turning = 20;
            }
            else
            {
                // Check if we're not close to the destination.
                if (!closeToDestination)
                {
                    // If we're still approaching, stop approaching.
                    if (approach)
                    {
                        approach = false;
                        App.Console.Print($"[C{Car.Id}] Approach done", Colors.Blue);
                    }

                    // Call the handle functions to stay in the lane and accelerate/deccelerate.
                    HandleStayInLane(ref velocity, ref yaw, ref addedRotation);
                    HandleAccelerate(ref velocity, ref distanceToTarget);
                }
                else
                {
                    // If we're not approaching, start approaching.
                    if (!approach)
                    {
                        approach = true;
                        App.Console.Print($"[C{Car.Id}] Initiating approach", Colors.Blue);
                    }

                    // Call the handle function to approach the target.
                    HandleApproachTarget(ref velocity, ref yaw, ref addedRotation, ref distanceToDestination);
                }
            }

            // Update the car's velocity with the result of the handle functions:
            // Temporarily store the current speed.
            var speed = velocity.Length;

            // Rotate the velocity based on the addedRotation this tick.
            velocity = MathUtil.RotateVector(velocity, -addedRotation);
            // Normalize the velocity and multiply with the speed to make sure it stays the same.
            velocity.Normalize();
            velocity = Vector.Multiply(velocity, speed);
            // Actually update the car's velocity.
            Car.Velocity = velocity;

            // Calculate the rotation by normalizing the velocity.
            var rotation = new Vector(velocity.X, velocity.Y);

            rotation.Normalize();

            // If the rotation vector is invalid or the car is not moving, set the rotation to the absolute direction.
            if (rotation.Length < 0.9 || double.IsNaN(rotation.Length) || velocity.Length < 0.1)
            {
                rotation = Car.Direction.GetVector();
            }

            // Actually update the rotation and update the absolute direction.
            Car.Rotation  = rotation;
            Car.Direction = DirectionCarMethods.Parse(rotation);

            // Update the car's location by adding the velocity to it.
            Car.Location          = Vector.Add(Car.Location, Car.Velocity);
            Car.DistanceTraveled += Car.Velocity.Length;
        }