diff --git a/OpenRA.Mods.RA/Air/Fly.cs b/OpenRA.Mods.RA/Air/Fly.cs index 09f2ca2d81..bd900f72b9 100755 --- a/OpenRA.Mods.RA/Air/Fly.cs +++ b/OpenRA.Mods.RA/Air/Fly.cs @@ -16,25 +16,25 @@ namespace OpenRA.Mods.RA.Air { public class Fly : Activity { - public readonly PPos Pos; + readonly WPos pos; - Fly(PPos px) { Pos = px; } + Fly(WPos pos) { this.pos = pos; } - public static Fly ToPx( PPos px ) { return new Fly( px ); } - public static Fly ToCell(CPos pos) { return new Fly(Util.CenterOfCell(pos)); } + public static Fly ToPos(WPos pos) { return new Fly(pos); } + public static Fly ToCell(CPos pos) { return new Fly(pos.CenterPosition); } public override Activity Tick(Actor self) { - var cruiseAltitude = self.Info.Traits.Get().CruiseAltitude; + if (IsCanceled) + return NextActivity; - if (IsCanceled) return NextActivity; - - var d = Pos - self.CenterLocation; - if (d.LengthSquared < 50) /* close enough */ + // Close enough (ported from old code which checked length against sqrt(50) px) + var d = pos - self.CenterPosition; + if (d.HorizontalLengthSquared < 91022) return NextActivity; var aircraft = self.Trait(); - + var cruiseAltitude = self.Info.Traits.Get().CruiseAltitude; var desiredFacing = Util.GetFacing(d, aircraft.Facing); if (aircraft.Altitude == cruiseAltitude) aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.ROT); @@ -46,18 +46,18 @@ namespace OpenRA.Mods.RA.Air return this; } - public override IEnumerable GetTargets( Actor self ) + public override IEnumerable GetTargets(Actor self) { - yield return Target.FromPos(Pos); + yield return Target.FromPos(pos); } } public static class FlyUtil { - public static void Fly(Actor self, int desiredAltitude ) + public static void Fly(Actor self, int desiredAltitude) { var aircraft = self.Trait(); - aircraft.TickMove( PSubPos.PerPx * aircraft.MovementSpeed, aircraft.Facing ); + aircraft.TickMove(PSubPos.PerPx * aircraft.MovementSpeed, aircraft.Facing); aircraft.Altitude += Math.Sign(desiredAltitude - aircraft.Altitude); } } diff --git a/OpenRA.Mods.RA/Air/FlyAttack.cs b/OpenRA.Mods.RA/Air/FlyAttack.cs index 43cc94fa9c..780bc77fb4 100755 --- a/OpenRA.Mods.RA/Air/FlyAttack.cs +++ b/OpenRA.Mods.RA/Air/FlyAttack.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Air if( IsCanceled ) return NextActivity; inner = Util.SequenceActivities( - Fly.ToPx(Target.CenterLocation), + Fly.ToPos(Target.CenterPosition), new FlyTimed(50)); } inner = Util.RunActivity( self, inner ); diff --git a/OpenRA.Mods.RA/Air/Land.cs b/OpenRA.Mods.RA/Air/Land.cs index 42ac37f569..cedd99ebdb 100755 --- a/OpenRA.Mods.RA/Air/Land.cs +++ b/OpenRA.Mods.RA/Air/Land.cs @@ -31,9 +31,9 @@ namespace OpenRA.Mods.RA.Air var aircraft = self.Trait(); var d = Target.CenterPosition - self.CenterPosition; - // close enough (1/16 cell) - // TODO: TickMove may overshoot if the aircraft speed is too high - if (d.HorizontalLengthSquared < 4096) + // The next move would overshoot, so just set the final position + var moveDist = aircraft.MovementSpeed * 7 * 1024 / (Game.CellSize * 32); + if (d.HorizontalLengthSquared < moveDist*moveDist) { aircraft.SetPxPosition(self, PPos.FromWPos(Target.CenterPosition)); return NextActivity; diff --git a/OpenRA.Mods.RA/Air/Plane.cs b/OpenRA.Mods.RA/Air/Plane.cs index 6a877be1ed..0d07f8a657 100755 --- a/OpenRA.Mods.RA/Air/Plane.cs +++ b/OpenRA.Mods.RA/Air/Plane.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Air public class Plane : Aircraft, IResolveOrder, ITick, ISync { - [Sync] public PVecInt RTBPathHash; + [Sync] public WPos RTBPathHash; public Plane( ActorInitializer init, PlaneInfo info ) : base( init, info ) { } diff --git a/OpenRA.Mods.RA/Air/ReturnToBase.cs b/OpenRA.Mods.RA/Air/ReturnToBase.cs index 22a06df9b7..4fddcdf466 100755 --- a/OpenRA.Mods.RA/Air/ReturnToBase.cs +++ b/OpenRA.Mods.RA/Air/ReturnToBase.cs @@ -19,8 +19,7 @@ namespace OpenRA.Mods.RA.Air { bool isCalculated; Actor dest; - - PPos w1, w2, w3; /* tangent points to turn circles */ + WPos w1, w2, w3; public static Actor ChooseAirfield(Actor self, bool unreservedOnly) { @@ -35,11 +34,14 @@ namespace OpenRA.Mods.RA.Air void Calculate(Actor self) { - if (dest == null || Reservable.IsReserved(dest)) dest = ChooseAirfield(self, true); + if (dest == null || Reservable.IsReserved(dest)) + dest = ChooseAirfield(self, true); - if (dest == null) return; + if (dest == null) + return; var plane = self.Trait(); + var planeInfo = self.Info.Traits.Get(); var res = dest.TraitOrDefault(); if (res != null) { @@ -47,41 +49,46 @@ namespace OpenRA.Mods.RA.Air plane.reservation = res.Reserve(dest, self, plane); } - var landPos = dest.CenterLocation; - var aircraft = self.Trait(); + var landPos = dest.CenterPosition; + var speed = plane.MovementSpeed * 1024 / Game.CellSize / 5; - var speed = .2f * aircraft.MovementSpeed; + // Distance required for descent. + // TODO: Generalize this for arbitrary flight pitches + var landDistance = planeInfo.CruiseAltitude * speed; + var altitude = planeInfo.CruiseAltitude * 1024 / Game.CellSize; - /* if the aircraft is on the ground, it will take off to the cruise altitude first before approaching */ - var altitude = aircraft.Altitude; - if (altitude == 0) altitude = self.Info.Traits.Get().CruiseAltitude; + // Land towards the east + var approachStart = landPos + new WVec(-landDistance, 0, altitude); - var approachStart = landPos.ToInt2() - new float2(altitude * speed, 0); - var turnRadius = (128f / self.Info.Traits.Get().ROT) * speed / (float)Math.PI; + // Add 10% to the turning radius to ensure we have enough room + var turnRadius = (int)(141 * speed / planeInfo.ROT / (float)(Math.PI)); - /* work out the center points */ - var fwd = -float2.FromAngle(aircraft.Facing / 128f * (float)Math.PI); - var side = new float2(-fwd.Y, fwd.X); /* rotate */ + // Find the center of the turning circles for clockwise and counterclockwise turns + var angle = WAngle.FromFacing(plane.Facing); + var fwd = -new WVec(angle.Sin(), angle.Cos(), 0); + + // Work out whether we should turn clockwise or counter-clockwise for approach + var side = new WVec(-fwd.Y, fwd.X, fwd.Z); + var approachDelta = self.CenterPosition - approachStart; var sideTowardBase = new[] { side, -side } - .OrderBy(a => float2.Dot(a, self.CenterLocation.ToInt2() - approachStart)) + .OrderBy(a => WVec.Dot(a, approachDelta)) .First(); - var c1 = self.CenterLocation.ToInt2() + turnRadius * sideTowardBase; - var c2 = approachStart + new float2(0, turnRadius * Math.Sign(self.CenterLocation.Y - approachStart.Y)); // above or below start point + // Calculate the tangent line that joins the turning circles at the current and approach positions + var cp = self.CenterPosition + turnRadius * sideTowardBase / 1024; + var posCenter = new WPos(cp.X, cp.Y, altitude); + var approachCenter = approachStart + new WVec(0, turnRadius * Math.Sign(self.CenterPosition.Y - approachStart.Y), 0); + var tangentDirection = approachCenter - posCenter; + var tangentOffset = new WVec(-tangentDirection.Y, tangentDirection.X, 0) * turnRadius / tangentDirection.Length; - /* work out tangent points */ - var d = c2 - c1; - var e = (turnRadius / d.Length) * d; - var f = new float2(-e.Y, e.X); /* rotate */ + // TODO: correctly handle CCW <-> CW turns + if (tangentOffset.X > 0) + tangentOffset = -tangentOffset; - /* TODO: support internal tangents, too! */ - - if (f.X > 0) f = -f; - - w1 = (PPos)(c1 + f).ToInt2(); - w2 = (PPos)(c2 + f).ToInt2(); - w3 = (PPos)(approachStart).ToInt2(); - plane.RTBPathHash = (PVecInt)w1 + (PVecInt)w2 + (PVecInt)w3; + w1 = posCenter + tangentOffset; + w2 = approachCenter + tangentOffset; + w3 = approachStart; + plane.RTBPathHash = w1 + (WVec)w2 + (WVec)w3; isCalculated = true; } @@ -93,12 +100,12 @@ namespace OpenRA.Mods.RA.Air public override Activity Tick(Actor self) { - if (IsCanceled) - return NextActivity; - if (self.IsDead()) + if (IsCanceled || self.IsDead()) return NextActivity; + if (!isCalculated) Calculate(self); + if (dest == null) { var nearestAfld = ChooseAirfield(self, false); @@ -111,9 +118,9 @@ namespace OpenRA.Mods.RA.Air } return Util.SequenceActivities( - Fly.ToPx(w1), - Fly.ToPx(w2), - Fly.ToPx(w3), + Fly.ToPos(w1), + Fly.ToPos(w2), + Fly.ToPos(w3), new Land(Target.FromActor(dest)), NextActivity); }