/// <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)); }
/// <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(); }
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))); }
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); }
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); }
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); }
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); }
public static VECTOR Normalize(VECTOR v) { v.Normalize(); return v; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static VECTOR Normalize(VECTOR v) { v.Normalize(); return v; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static VECTOR Normalize(VECTOR v) { v.Normalize(); return(v); }
public static VECTOR Normalize(VECTOR v) { return(VECTOR.Normalize(v)); }
public static VECTOR Normalize(VECTOR v) { v.Normalize(); return(v); }
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); }
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; }
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)); } } } }
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; }
/// <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; }