diff --git a/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs b/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs index 47b1c9dcf7..0960678b35 100644 --- a/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs +++ b/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs @@ -54,7 +54,9 @@ namespace OpenRA.Mods.Common.Activities if (info.MaximumSpinSpeed < 0 || Math.Abs(spin) < info.MaximumSpinSpeed) spin += acceleration; // TODO: Possibly unhardcode this - aircraft.Facing = (aircraft.Facing + spin) % 256; + // Allow for negative spin values and convert from facing to angle units + // TODO: Remember to convert this when removing WAngle.FromFacing + aircraft.Facing = new WAngle(aircraft.Facing.Angle + 4 * spin); } var move = info.Moves ? aircraft.FlyStep(aircraft.Facing) : WVec.Zero; diff --git a/OpenRA.Mods.Common/Activities/Air/Fly.cs b/OpenRA.Mods.Common/Activities/Air/Fly.cs index 247b87f2b2..5462deff99 100644 --- a/OpenRA.Mods.Common/Activities/Air/Fly.cs +++ b/OpenRA.Mods.Common/Activities/Air/Fly.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Activities this.minRange = minRange; } - public static void FlyTick(Actor self, Aircraft aircraft, int desiredFacing, WDist desiredAltitude, WVec moveOverride, int turnSpeedOverride = -1) + public static void FlyTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, WVec moveOverride, int turnSpeedOverride = -1) { var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition); var move = aircraft.Info.CanSlide ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing); @@ -83,13 +83,13 @@ namespace OpenRA.Mods.Common.Activities aircraft.SetPosition(self, aircraft.CenterPosition + move); } - public static void FlyTick(Actor self, Aircraft aircraft, int desiredFacing, WDist desiredAltitude, int turnSpeedOverride = -1) + public static void FlyTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, int turnSpeedOverride = -1) { FlyTick(self, aircraft, desiredFacing, desiredAltitude, WVec.Zero, turnSpeedOverride); } // Should only be used for vertical-only movement, usually VTOL take-off or land. Terrain-induced altitude changes should always be handled by FlyTick. - public static bool VerticalTakeOffOrLandTick(Actor self, Aircraft aircraft, int desiredFacing, WDist desiredAltitude, int turnSpeedOverride = -1) + public static bool VerticalTakeOffOrLandTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, int turnSpeedOverride = -1) { var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition); var move = WVec.Zero; @@ -173,7 +173,7 @@ namespace OpenRA.Mods.Common.Activities return true; var isSlider = aircraft.Info.CanSlide; - var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing; + var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw : aircraft.Facing; var move = isSlider ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing); // Inside the minimum range, so reverse if we CanSlide, otherwise face away from the target. @@ -183,8 +183,7 @@ namespace OpenRA.Mods.Common.Activities FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, -move); else { - desiredFacing = Util.NormalizeFacing(desiredFacing + 128); - FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, move); + FlyTick(self, aircraft, desiredFacing + new WAngle(512), aircraft.Info.CruiseAltitude, move); } return false; @@ -230,10 +229,9 @@ namespace OpenRA.Mods.Common.Activities // The current facing is a tangent of the minimal turn circle. // Make a perpendicular vector, and use it to locate the turn's center. - var turnCenterFacing = aircraft.Facing; - turnCenterFacing += Util.GetNearestFacing(aircraft.Facing, desiredFacing) > 0 ? 64 : -64; + var turnCenterFacing = aircraft.Facing + new WAngle(Util.GetTurnDirection(aircraft.Facing, desiredFacing) * 256); - var turnCenterDir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(turnCenterFacing)); + var turnCenterDir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(turnCenterFacing)); turnCenterDir *= turnRadius; turnCenterDir /= 1024; @@ -267,8 +265,8 @@ namespace OpenRA.Mods.Common.Activities { // turnSpeed -> divide into 256 to get the number of ticks per complete rotation // speed -> multiply to get distance travelled per rotation (circumference) - // 45 -> divide by 2*pi to get the turn radius: 45==256/(2*pi), with some extra leeway - return turnSpeed > 0 ? 45 * speed / turnSpeed : 0; + // 180 -> divide by 2*pi to get the turn radius: 180==1024/(2*pi), with some extra leeway + return turnSpeed > 0 ? 180 * speed / turnSpeed : 0; } } } diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index 67ab3cf86b..652931cd95 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -153,7 +153,7 @@ namespace OpenRA.Mods.Common.Activities } var delta = attackAircraft.GetTargetPosition(pos, target) - pos; - var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing; + var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw : aircraft.Facing; QueueChild(new TakeOff(self)); diff --git a/OpenRA.Mods.Common/Activities/Air/FlyIdle.cs b/OpenRA.Mods.Common/Activities/Air/FlyIdle.cs index 1f7820cf4a..f8341dc405 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyIdle.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyIdle.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Activities public FlyIdle(Actor self, int ticks = -1, bool tickIdle = true) { aircraft = self.Trait(); - turnSpeed = aircraft.Info.IdleTurnSpeed > -1 ? aircraft.Info.IdleTurnSpeed : aircraft.TurnSpeed; + turnSpeed = aircraft.IdleTurnSpeed > -1 ? aircraft.IdleTurnSpeed : aircraft.TurnSpeed; remainingTicks = ticks; if (tickIdle) @@ -50,12 +50,11 @@ namespace OpenRA.Mods.Common.Activities if (!aircraft.Info.CanHover) { - // We can't possibly turn this fast - var desiredFacing = aircraft.Facing + 64; - // This override is necessary, otherwise aircraft with CanSlide would circle sideways var move = aircraft.FlyStep(aircraft.Facing); + // We can't possibly turn this fast + var desiredFacing = aircraft.Facing + new WAngle(256); Fly.FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, move, turnSpeed); } diff --git a/OpenRA.Mods.Common/Activities/Air/Land.cs b/OpenRA.Mods.Common/Activities/Air/Land.cs index d73ceee40e..8c0ac04d83 100644 --- a/OpenRA.Mods.Common/Activities/Air/Land.cs +++ b/OpenRA.Mods.Common/Activities/Air/Land.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Activities { readonly Aircraft aircraft; readonly WVec offset; - readonly int desiredFacing; + readonly WAngle? desiredFacing; readonly bool assignTargetOnFirstRun; readonly CPos[] clearCells; readonly WDist landRange; @@ -34,22 +34,22 @@ namespace OpenRA.Mods.Common.Activities bool landingInitiated; bool finishedApproach; - public Land(Actor self, int facing = -1, Color? targetLineColor = null) + public Land(Actor self, WAngle? facing = null, Color? targetLineColor = null) : this(self, Target.Invalid, new WDist(-1), WVec.Zero, facing, null) { assignTargetOnFirstRun = true; } - public Land(Actor self, Target target, int facing = -1, Color? targetLineColor = null) + public Land(Actor self, Target target, WAngle? facing = null, Color? targetLineColor = null) : this(self, target, new WDist(-1), WVec.Zero, facing, targetLineColor: targetLineColor) { } - public Land(Actor self, Target target, WDist landRange, int facing = -1, Color? targetLineColor = null) + public Land(Actor self, Target target, WDist landRange, WAngle? facing = null, Color? targetLineColor = null) : this(self, target, landRange, WVec.Zero, facing, targetLineColor: targetLineColor) { } - public Land(Actor self, Target target, WVec offset, int facing = -1, Color? targetLineColor = null) + public Land(Actor self, Target target, WVec offset, WAngle? facing = null, Color? targetLineColor = null) : this(self, target, WDist.Zero, offset, facing, targetLineColor: targetLineColor) { } - public Land(Actor self, Target target, WDist landRange, WVec offset, int facing = -1, CPos[] clearCells = null, Color? targetLineColor = null) + public Land(Actor self, Target target, WDist landRange, WVec offset, WAngle? facing = null, CPos[] clearCells = null, Color? targetLineColor = null) { aircraft = self.Trait(); this.target = target; @@ -60,8 +60,8 @@ namespace OpenRA.Mods.Common.Activities // NOTE: desiredFacing = -1 means we should not prefer any particular facing and instead just // use whatever facing gives us the most direct path to the landing site. - if (facing == -1 && aircraft.Info.TurnToLand) - desiredFacing = aircraft.Info.InitialFacing; + if (!facing.HasValue && aircraft.Info.TurnToLand) + desiredFacing = WAngle.FromFacing(aircraft.Info.InitialFacing); else desiredFacing = facing; } @@ -141,9 +141,10 @@ namespace OpenRA.Mods.Common.Activities QueueChild(new Fly(self, Target.FromPos(targetPosition))); return false; } - else if (desiredFacing != -1 && desiredFacing != aircraft.Facing) + + if (desiredFacing.HasValue && desiredFacing.Value != aircraft.Facing) { - QueueChild(new Turn(self, desiredFacing)); + QueueChild(new Turn(self, desiredFacing.Value.Facing)); return false; } } @@ -159,17 +160,17 @@ namespace OpenRA.Mods.Common.Activities // Approach landing from the opposite direction of the desired facing // TODO: Calculate sensible trajectory without preferred facing. var rotation = WRot.Zero; - if (desiredFacing != -1) - rotation = WRot.FromFacing(desiredFacing); + if (desiredFacing.HasValue) + rotation = WRot.FromYaw(desiredFacing.Value); var approachStart = targetPosition + new WVec(0, landDistance, altitude).Rotate(rotation); // Add 10% to the turning radius to ensure we have enough room var speed = aircraft.MovementSpeed * 32 / 35; - var turnRadius = Fly.CalculateTurnRadius(speed, aircraft.Info.TurnSpeed); + var turnRadius = Fly.CalculateTurnRadius(speed, aircraft.TurnSpeed); // Find the center of the turning circles for clockwise and counterclockwise turns - var angle = WAngle.FromFacing(aircraft.Facing); + var angle = aircraft.Facing; var fwd = -new WVec(angle.Sin(), angle.Cos(), 0); // Work out whether we should turn clockwise or counter-clockwise for approach @@ -196,7 +197,7 @@ namespace OpenRA.Mods.Common.Activities var w2 = approachCenter + tangentOffset; var w3 = approachStart; - turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.Info.TurnSpeed); + turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.TurnSpeed); // Move along approach trajectory. QueueChild(new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3))); @@ -252,7 +253,7 @@ namespace OpenRA.Mods.Common.Activities } var landingAlt = self.World.Map.DistanceAboveTerrain(targetPosition) + aircraft.LandAltitude; - Fly.FlyTick(self, aircraft, d.Yaw.Facing, landingAlt); + Fly.FlyTick(self, aircraft, d.Yaw, landingAlt); return false; } diff --git a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs index 91a9357e74..d253bb4743 100644 --- a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Activities readonly Rearmable rearmable; readonly bool alwaysLand; Actor dest; - int facing = -1; + WAngle? facing; public ReturnToBase(Actor self, Actor dest = null, bool alwaysLand = false) { @@ -114,7 +114,7 @@ namespace OpenRA.Mods.Common.Activities var exit = dest.FirstExitOrDefault(); var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero; if (aircraft.Info.TurnToDock || !aircraft.Info.VTOL) - facing = aircraft.Info.InitialFacing; + facing = WAngle.FromFacing(aircraft.Info.InitialFacing); aircraft.MakeReservation(dest); QueueChild(new Land(self, Target.FromActor(dest), offset, facing, Color.Green)); diff --git a/OpenRA.Mods.Common/Activities/PickupUnit.cs b/OpenRA.Mods.Common/Activities/PickupUnit.cs index 433574af83..826bd96e36 100644 --- a/OpenRA.Mods.Common/Activities/PickupUnit.cs +++ b/OpenRA.Mods.Common/Activities/PickupUnit.cs @@ -93,7 +93,7 @@ namespace OpenRA.Mods.Common.Activities ChildActivity.Cancel(self); var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); - QueueChild(new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset), carryableFacing.Facing)); + QueueChild(new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset), WAngle.FromFacing(carryableFacing.Facing))); // Pause briefly before attachment for visual effect if (delay > 0) diff --git a/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs index e81ac4ea29..315acb88b8 100644 --- a/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs @@ -156,7 +156,7 @@ namespace OpenRA.Mods.Common.Scripting // Scripted cargo aircraft must turn to default position before unloading. // TODO: pass facing through UnloadCargo instead. if (aircraft != null) - transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, entryPath.Last()), WDist.FromCells(dropRange), aircraft.Info.InitialFacing)); + transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, entryPath.Last()), WDist.FromCells(dropRange), WAngle.FromFacing(aircraft.Info.InitialFacing))); if (cargo != null) transport.QueueActivity(new UnloadCargo(transport, WDist.FromCells(dropRange))); diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 529b784df4..498b57f399 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -209,13 +209,21 @@ namespace OpenRA.Mods.Common.Traits IOverrideAircraftLanding overrideAircraftLanding; [Sync] - public int Facing { get; set; } + public WAngle Facing; + + int IFacing.Facing + { + get { return Facing.Facing; } + set { Facing = WAngle.FromFacing(value); } + } [Sync] public WPos CenterPosition { get; private set; } public CPos TopLeft { get { return self.World.Map.CellContaining(CenterPosition); } } - public int TurnSpeed { get { return !IsTraitDisabled && !IsTraitPaused ? Info.TurnSpeed : 0; } } + public int TurnSpeed { get { return !IsTraitDisabled && !IsTraitPaused ? 4 * Info.TurnSpeed : 0; } } + public int IdleTurnSpeed { get { return Info.IdleTurnSpeed != -1 ? 4 * Info.IdleTurnSpeed : -1; } } + public Actor ReservedActor { get; private set; } public bool MayYieldReservation { get; private set; } public bool ForceLanding { get; private set; } @@ -240,7 +248,7 @@ namespace OpenRA.Mods.Common.Traits MovementType movementTypes; WPos cachedPosition; - int cachedFacing; + WAngle cachedFacing; public Aircraft(ActorInitializer init, AircraftInfo info) : base(info) @@ -255,7 +263,7 @@ namespace OpenRA.Mods.Common.Traits if (centerPositionInit != null) SetPosition(self, centerPositionInit.Value); - Facing = init.GetValue(info, Info.InitialFacing); + Facing = WAngle.FromFacing(init.GetValue(info, Info.InitialFacing)); creationActivityDelay = init.GetValue(info, 0); } @@ -393,7 +401,7 @@ namespace OpenRA.Mods.Common.Traits // HACK: Prevent updating visibility twice per tick. We really shouldn't be // moving twice in a tick in the first place. notify = false; - SetPosition(self, CenterPosition + FlyStep(speed, repulsionForce.Yaw.Facing)); + SetPosition(self, CenterPosition + FlyStep(speed, repulsionForce.Yaw)); notify = true; } @@ -558,14 +566,14 @@ namespace OpenRA.Mods.Common.Traits return new[] { Pair.New(TopLeft, SubCell.FullCell) }; } - public WVec FlyStep(int facing) + public WVec FlyStep(WAngle facing) { return FlyStep(MovementSpeed, facing); } - public WVec FlyStep(int speed, int facing) + public WVec FlyStep(int speed, WAngle facing) { - var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)); + var dir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(facing)); return speed * dir / 1024; } @@ -663,7 +671,7 @@ namespace OpenRA.Mods.Common.Traits public void ModifyDeathActorInit(Actor self, TypeDictionary init) { - init.Add(new FacingInit(Facing)); + init.Add(new FacingInit(Facing.Facing)); } void INotifyBecomingIdle.OnBecomingIdle(Actor self) @@ -1176,7 +1184,7 @@ namespace OpenRA.Mods.Common.Traits void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary inits) { if (!inits.Contains() && !inits.Contains()) - inits.Add(new DynamicFacingInit(() => Facing)); + inits.Add(new DynamicFacingInit(() => Facing.Facing)); } Activity ICreationActivity.GetCreationActivity() diff --git a/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs b/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs index 8d80b3f2f0..e03f3a6a74 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits }); var exitCell = self.Location + exit.ExitCell; - actor.QueueActivity(new Land(actor, Target.FromActor(self), WDist.Zero, WVec.Zero, info.Facing, clearCells: new CPos[1] { exitCell })); + actor.QueueActivity(new Land(actor, Target.FromActor(self), WDist.Zero, WVec.Zero, WAngle.FromFacing(info.Facing), clearCells: new CPos[1] { exitCell })); actor.QueueActivity(new CallFunc(() => { if (!self.IsInWorld || self.IsDead) diff --git a/OpenRA.Mods.Common/Util.cs b/OpenRA.Mods.Common/Util.cs index 3050fd9a84..7c32813927 100644 --- a/OpenRA.Mods.Common/Util.cs +++ b/OpenRA.Mods.Common/Util.cs @@ -46,6 +46,30 @@ namespace OpenRA.Mods.Common return facing + turn; } + /// + /// Adds step angle units to facing in the direction that takes it closer to desiredFacing. + /// If facing is already within step of desiredFacing then desiredFacing is returned. + /// Step is given as an integer to allow negative values (step away from the desired facing) + /// + public static WAngle TickFacing(WAngle facing, WAngle desiredFacing, int step) + { + var leftTurn = (facing - desiredFacing).Angle; + var rightTurn = (desiredFacing - facing).Angle; + if (leftTurn < step || rightTurn < step) + return desiredFacing; + + return rightTurn < leftTurn ? new WAngle(facing.Angle + step) : new WAngle(facing.Angle - step); + } + + /// + /// Determines whether desiredFacing is clockwise (-1) or anticlockwise (+1) of facing. + /// If desiredFacing is equal to facing or directly behind facing we treat it as being anticlockwise + /// + public static int GetTurnDirection(WAngle facing, WAngle desiredFacing) + { + return (facing - desiredFacing).Angle < 512 ? -1 : 1; + } + /// /// Calculate the frame index (between 0..numFrames) that /// should be used for the given facing value.