diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index 61ecefd009..6b5872ce09 100644 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -22,6 +22,11 @@ namespace OpenRA.Mods.RA.Air public class AircraftInfo : ITraitInfo, IFacingInfo, IOccupySpaceInfo, UsesInit, UsesInit { public readonly WRange CruiseAltitude = new WRange(1280); + public readonly WRange IdealSeparation = new WRange(1706); + [Desc("Whether the aircraft can be repulsed.")] + public readonly bool Repulsable = true; + [Desc("The speed at which the aircraft is repulsed from other aircraft. Specify -1 for normal movement speed.")] + public readonly int RepulsionSpeed = -1; [ActorReference] public readonly string[] RepairBuildings = { "fix" }; @@ -38,7 +43,7 @@ namespace OpenRA.Mods.RA.Air public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice, INotifyAddedToWorld, INotifyRemovedFromWorld { - static readonly Pair[] NoCells = new Pair[] { }; + static readonly Pair[] NoCells = { }; readonly AircraftInfo info; readonly Actor self; @@ -60,13 +65,61 @@ namespace OpenRA.Mods.RA.Air if (init.Contains()) SetPosition(self, init.Get()); - this.Facing = init.Contains() ? init.Get() : info.InitialFacing; + Facing = init.Contains() ? init.Get() : info.InitialFacing; + } + + public void Repulse() + { + var repulsionForce = GetRepulsionForce(); + + var repulsionFacing = Util.GetFacing(repulsionForce, -1); + if (repulsionFacing == -1) + return; + + var speed = info.RepulsionSpeed != -1 ? info.RepulsionSpeed : MovementSpeed; + SetPosition(self, CenterPosition + FlyStep(speed, repulsionFacing)); + } + + public virtual WVec GetRepulsionForce() + { + if (!info.Repulsable) + return WVec.Zero; + + // Repulsion only applies when we're flying! + var altitude = CenterPosition.Z; + if (altitude != info.CruiseAltitude.Range) + return WVec.Zero; + + return self.World.FindActorsInCircle(self.CenterPosition, info.IdealSeparation) + .Where(a => !a.IsDead() && a.HasTrait()) + .Select(GetRepulsionForce) + .Aggregate(WVec.Zero, (a, b) => a + b); + } + + public WVec GetRepulsionForce(Actor other) + { + if (self == other || other.CenterPosition.Z < self.CenterPosition.Z) + return WVec.Zero; + + var d = self.CenterPosition - other.CenterPosition; + var distSq = d.HorizontalLengthSquared; + if (distSq > info.IdealSeparation.Range * info.IdealSeparation.Range) + return WVec.Zero; + + if (distSq < 1) + { + var yaw = self.World.SharedRandom.Next(0, 1023); + var rot = new WRot(WAngle.Zero, WAngle.Zero, new WAngle(yaw)); + return new WVec(1024, 0, 0).Rotate(rot); + } + + return (d * 1024 * 8) / (int)distSq; } public Actor GetActorBelow() { if (self.CenterPosition.Z != 0) - return null; // not on the ground. + return null; // not on the ground. return self.World.ActorMap.GetUnitsAt(self.Location) .FirstOrDefault(a => a.HasTrait()); @@ -158,7 +211,11 @@ namespace OpenRA.Mods.RA.Air public WVec FlyStep(int facing) { - var speed = MovementSpeed; + return FlyStep(MovementSpeed, facing); + } + + public WVec FlyStep(int speed, int facing) + { var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)); return speed * dir / 1024; } diff --git a/OpenRA.Mods.RA/Air/Helicopter.cs b/OpenRA.Mods.RA/Air/Helicopter.cs index 2d416bef2a..8ee4f10f93 100755 --- a/OpenRA.Mods.RA/Air/Helicopter.cs +++ b/OpenRA.Mods.RA/Air/Helicopter.cs @@ -18,8 +18,6 @@ namespace OpenRA.Mods.RA.Air { class HelicopterInfo : AircraftInfo, IMoveInfo { - public readonly WRange IdealSeparation = new WRange(1706); - [Desc("Allow the helicopter land after it has no more commands.")] public readonly bool LandWhenIdle = true; @@ -135,41 +133,7 @@ namespace OpenRA.Mods.RA.Air self.QueueActivity(new TakeOff()); } - // Repulsion only applies when we're flying! - var altitude = CenterPosition.Z; - if (altitude != Info.CruiseAltitude.Range) - return; - - var otherHelis = self.World.FindActorsInCircle(self.CenterPosition, Info.IdealSeparation) - .Where(a => a.HasTrait()); - - var f = otherHelis - .Select(h => GetRepulseForce(self, h)) - .Aggregate(WVec.Zero, (a, b) => a + b); - - var repulsionFacing = Util.GetFacing(f, -1); - if (repulsionFacing != -1) - SetPosition(self, CenterPosition + FlyStep(repulsionFacing)); - } - - public WVec GetRepulseForce(Actor self, Actor other) - { - if (self == other || other.CenterPosition.Z < self.CenterPosition.Z) - return WVec.Zero; - - var d = self.CenterPosition - other.CenterPosition; - var distSq = d.HorizontalLengthSquared; - if (distSq > Info.IdealSeparation.Range * Info.IdealSeparation.Range) - return WVec.Zero; - - if (distSq < 1) - { - var yaw = self.World.SharedRandom.Next(0, 1023); - var rot = new WRot(WAngle.Zero, WAngle.Zero, new WAngle(yaw)); - return new WVec(1024, 0, 0).Rotate(rot); - } - - return (d * 1024 * 8) / (int)distSq; + Repulse(); } public Activity MoveTo(CPos cell, int nearEnough) { return new HeliFly(self, Target.FromCell(self.World, cell)); } diff --git a/OpenRA.Mods.RA/Air/Plane.cs b/OpenRA.Mods.RA/Air/Plane.cs index 09f26c30a9..52a607ec1c 100755 --- a/OpenRA.Mods.RA/Air/Plane.cs +++ b/OpenRA.Mods.RA/Air/Plane.cs @@ -50,6 +50,21 @@ namespace OpenRA.Mods.RA.Air self.QueueActivity(new TakeOff()); } + + Repulse(); + } + + public override WVec GetRepulsionForce() + { + var repulsionForce = base.GetRepulsionForce(); + if (repulsionForce == WVec.Zero) + return WVec.Zero; + + var currentDir = FlyStep(Facing); + + var dot = WVec.Dot(currentDir, repulsionForce) / (currentDir.HorizontalLength * repulsionForce.HorizontalLength); + // avoid stalling the plane + return dot >= 0 ? repulsionForce : WVec.Zero; } public void ResolveOrder(Actor self, Order order)