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) diff --git a/mods/cnc/rules/aircraft.yaml b/mods/cnc/rules/aircraft.yaml index e274ea21ec..5f149a947a 100644 --- a/mods/cnc/rules/aircraft.yaml +++ b/mods/cnc/rules/aircraft.yaml @@ -153,6 +153,7 @@ C17: Plane: ROT: 5 Speed: 326 + Repulsable: False Health: HP: 25 Armor: @@ -191,6 +192,7 @@ A10: Plane: ROT: 4 Speed: 373 + Repulsable: False Health: HP: 150 Armor: diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index 16e71adb7d..c32836876e 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -42,6 +42,7 @@ FRIGATE: Speed: 350 RepairBuildings: repaira,repairo,repairh RearmBuildings: starporta,starporto,starporth + Repulsable: False Health: HP: 500 -TargetableAircraft: @@ -106,6 +107,7 @@ ORNI.bomber: Speed: 350 RepairBuildings: repaira,repairo,repairh RearmBuildings: starporta,starporto,starporth + Repulsable: False LimitedAmmo: Ammo: 5 RenderUnit: @@ -135,6 +137,7 @@ CARRYALL.infantry: Speed: 280 RepairBuildings: repaira,repairo,repairh RearmBuildings: starporta,starporto,starporth + Repulsable: False RenderUnit: Image: carryall WithShadow: diff --git a/mods/ra/rules/aircraft.yaml b/mods/ra/rules/aircraft.yaml index 24d0e0ff97..b6a9962d94 100644 --- a/mods/ra/rules/aircraft.yaml +++ b/mods/ra/rules/aircraft.yaml @@ -9,6 +9,7 @@ BADR: Plane: ROT: 5 Speed: 149 + Repulsable: False RenderUnit: WithShadow: IronCurtainable: @@ -46,6 +47,7 @@ BADR.Bomber: Plane: ROT: 5 Speed: 149 + Repulsable: False LimitedAmmo: Ammo: 7 RenderUnit: @@ -99,9 +101,10 @@ MIG: FacingTolerance: 20 Plane: InitialFacing: 192 - ROT: 5 - Speed: 186 + ROT: 4 + Speed: 223 RearmBuildings: afld + RepulsionSpeed: 40 AutoTarget: TargetWhenIdle: false TargetWhenDamaged: false @@ -158,8 +161,9 @@ YAK: Plane: RearmBuildings: afld InitialFacing: 192 - ROT: 5 - Speed: 149 + ROT: 4 + Speed: 178 + RepulsionSpeed: 40 AutoTarget: TargetWhenIdle: false TargetWhenDamaged: false @@ -343,6 +347,7 @@ U2: Plane: ROT: 7 Speed: 373 + Repulsable: False RenderUnit: WithShadow: IronCurtainable: