diff --git a/OpenRA.Mods.Common/Activities/Air/Fly.cs b/OpenRA.Mods.Common/Activities/Air/Fly.cs index 91bf7ae50f..f2eb231bb7 100644 --- a/OpenRA.Mods.Common/Activities/Air/Fly.cs +++ b/OpenRA.Mods.Common/Activities/Air/Fly.cs @@ -157,12 +157,13 @@ namespace OpenRA.Mods.Common.Activities return NextActivity; var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target; - var delta = checkTarget.CenterPosition - self.CenterPosition; + var pos = aircraft.GetPosition(); + var delta = checkTarget.CenterPosition - pos; var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing; // Inside the target annulus, so we're done - var insideMaxRange = maxRange.Length > 0 && checkTarget.IsInRange(aircraft.CenterPosition, maxRange); - var insideMinRange = minRange.Length > 0 && checkTarget.IsInRange(aircraft.CenterPosition, minRange); + var insideMaxRange = maxRange.Length > 0 && checkTarget.IsInRange(pos, maxRange); + var insideMinRange = minRange.Length > 0 && checkTarget.IsInRange(pos, minRange); if (insideMaxRange && !insideMinRange) return NextActivity; diff --git a/OpenRA.Mods.Common/Activities/Air/Land.cs b/OpenRA.Mods.Common/Activities/Air/Land.cs index 8ee26a2841..faaf244b82 100644 --- a/OpenRA.Mods.Common/Activities/Air/Land.cs +++ b/OpenRA.Mods.Common/Activities/Air/Land.cs @@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Activities return NextActivity; } - var pos = aircraft.CenterPosition; + var pos = aircraft.GetPosition(); // Reevaluate target position in case the target has moved. targetPosition = target.CenterPosition + offset; diff --git a/OpenRA.Mods.Common/Activities/DeliverUnit.cs b/OpenRA.Mods.Common/Activities/DeliverUnit.cs index 98c1cfa918..bcfa8a310c 100644 --- a/OpenRA.Mods.Common/Activities/DeliverUnit.cs +++ b/OpenRA.Mods.Common/Activities/DeliverUnit.cs @@ -17,48 +17,37 @@ namespace OpenRA.Mods.Common.Activities { public class DeliverUnit : Activity { - readonly Actor self; readonly Carryall carryall; readonly BodyOrientation body; + readonly bool assignTargetOnFirstRun; + readonly WDist deliverRange; + Target destination; - public DeliverUnit(Actor self, CPos destination) - : this(self, Target.FromCell(self.World, destination)) { } - - public DeliverUnit(Actor self) - : this(self, Target.Invalid) { } - - DeliverUnit(Actor self, Target destination) + public DeliverUnit(Actor self, WDist deliverRange) + : this(self, Target.Invalid, deliverRange) + { + assignTargetOnFirstRun = true; + } + + public DeliverUnit(Actor self, Target destination, WDist deliverRange) { - this.self = self; this.destination = destination; + this.deliverRange = deliverRange; carryall = self.Trait(); body = self.Trait(); } - Target FindDropLocation(Target requested, WDist maxSearchDistance) + protected override void OnFirstRun(Actor self) { - var positionable = carryall.Carryable.Trait(); - var centerPosition = requested.CenterPosition; - var targetCell = self.World.Map.CellContaining(centerPosition); + if (assignTargetOnFirstRun) + destination = Target.FromCell(self.World, self.Location); - // The easy case - if (positionable.CanEnterCell(targetCell, self)) - return requested; - - var cellRange = (maxSearchDistance.Length + 1023) / 1024; - foreach (var c in self.World.Map.FindTilesInCircle(targetCell, cellRange)) - { - if (!positionable.CanEnterCell(c, self)) - continue; - - var delta = self.World.Map.CenterOfCell(c) - centerPosition; - if (delta.LengthSquared < maxSearchDistance.LengthSquared) - return Target.FromCell(self.World, c); - } - - return Target.Invalid; + QueueChild(self, new Land(self, destination, deliverRange), true); + QueueChild(self, new Wait(carryall.Info.UnloadingDelay, false), true); + QueueChild(self, new ReleaseUnit(self)); + QueueChild(self, new TakeOff(self)); } public override Activity Tick(Actor self) @@ -70,55 +59,7 @@ namespace OpenRA.Mods.Common.Activities return this; } - if (IsCanceling || carryall.State != Carryall.CarryallState.Carrying || carryall.Carryable.IsDead) - return NextActivity; - - // Drop the actor at the current position - if (destination.Type == TargetType.Invalid) - destination = Target.FromCell(self.World, self.Location); - - var target = FindDropLocation(destination, carryall.Info.DropRange); - - // Can't land, so wait at the target until something changes - if (target.Type == TargetType.Invalid) - { - QueueChild(self, new Fly(self, destination), true); - QueueChild(self, new Wait(25)); - return this; - } - - // Move to drop-off location - var localOffset = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)); - var carryablePosition = self.CenterPosition + body.LocalToWorld(localOffset); - if ((carryablePosition - target.CenterPosition).HorizontalLengthSquared != 0) - { - // For non-zero offsets the drop position depends on the carryall facing - // We therefore need to predict/correct for the facing *at the drop point* - if (carryall.CarryableOffset.HorizontalLengthSquared != 0) - { - var dropFacing = (target.CenterPosition - self.CenterPosition).Yaw.Facing; - localOffset = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, WRot.FromFacing(dropFacing))); - QueueChild(self, new Fly(self, Target.FromPos(target.CenterPosition - body.LocalToWorld(localOffset))), true); - QueueChild(self, new Turn(self, dropFacing)); - return this; - } - - QueueChild(self, new Fly(self, target), true); - return this; - } - - // Make sure that the carried actor is on the ground before releasing it - if (self.World.Map.DistanceAboveTerrain(carryablePosition) != WDist.Zero) - QueueChild(self, new Land(self), true); - - // Pause briefly before releasing for visual effect - if (carryall.Info.UnloadingDelay > 0) - QueueChild(self, new Wait(carryall.Info.UnloadingDelay, false), true); - - // Release carried actor - QueueChild(self, new ReleaseUnit(self)); - QueueChild(self, new Fly(self, Target.FromPos(self.CenterPosition))); - return this; + return NextActivity; } class ReleaseUnit : Activity diff --git a/OpenRA.Mods.Common/Activities/PickupUnit.cs b/OpenRA.Mods.Common/Activities/PickupUnit.cs index f21d537849..749fbb0ca2 100644 --- a/OpenRA.Mods.Common/Activities/PickupUnit.cs +++ b/OpenRA.Mods.Common/Activities/PickupUnit.cs @@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.Activities readonly int delay; - enum PickupState { Intercept, LockCarryable, MoveToCarryable, Turn, Land, Wait, Pickup } + enum PickupState { Intercept, LockCarryable, Pickup } PickupState state; @@ -86,63 +86,23 @@ namespace OpenRA.Mods.Common.Activities if (!carryable.LockForPickup(cargo, self)) Cancel(self); - state = PickupState.MoveToCarryable; - return this; - - case PickupState.MoveToCarryable: - { - // Line up with the attachment point - var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); - var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset); - if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0) - { - QueueChild(self, new Fly(self, Target.FromPos(targetPosition)), true); - return this; - } - - state = PickupState.Turn; - return this; - } - - case PickupState.Turn: - if (carryallFacing.Facing != carryableFacing.Facing) - { - QueueChild(self, new Turn(self, carryableFacing.Facing), true); - return this; - } - - state = PickupState.Land; - return this; - - case PickupState.Land: - { - var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); - var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset); - if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0 || carryallFacing.Facing != carryableFacing.Facing) - { - state = PickupState.MoveToCarryable; - return this; - } - - if (targetPosition.Z != self.CenterPosition.Z) - { - QueueChild(self, new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset))); - return this; - } - - state = delay > 0 ? PickupState.Wait : PickupState.Pickup; - return this; - } - - case PickupState.Wait: - QueueChild(self, new Wait(delay, false), true); state = PickupState.Pickup; return this; case PickupState.Pickup: + { + // Land at the target location + var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); + QueueChild(self, new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset), carryableFacing.Facing)); + + // Pause briefly before attachment for visual effect + if (delay > 0) + QueueChild(self, new Wait(delay, false), true); + // Remove our carryable from world - Attach(self); + QueueChild(self, new CallFunc(() => Attach(self))); return this; + } } return NextActivity; diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 4c4341250d..7d332f1ffc 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -187,10 +187,12 @@ namespace OpenRA.Mods.Common.Traits Repairable repairable; Rearmable rearmable; + IAircraftCenterPositionOffset[] positionOffsets; ConditionManager conditionManager; IDisposable reservation; IEnumerable speedModifiers; INotifyMoving[] notifyMoving; + IOverrideAircraftLanding overrideAircraftLanding; [Sync] public int Facing { get; set; } @@ -206,13 +208,13 @@ namespace OpenRA.Mods.Common.Traits IEnumerable landingCells = Enumerable.Empty(); bool requireForceMove; - public WDist LandAltitude { get; private set; } - public static WPos GroundPosition(Actor self) { return self.CenterPosition - new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(self.CenterPosition)); } + public bool AtLandAltitude { get { return self.World.Map.DistanceAboveTerrain(GetPosition()) == LandAltitude; } } + bool airborne; bool cruising; bool firstTick = true; @@ -236,17 +238,27 @@ namespace OpenRA.Mods.Common.Traits SetPosition(self, init.Get()); Facing = init.Contains() ? init.Get() : Info.InitialFacing; - LandAltitude = info.LandAltitude; } - public void AddLandingOffset(int offset) + public WDist LandAltitude { - LandAltitude += new WDist(offset); + get + { + var alt = Info.LandAltitude; + foreach (var offset in positionOffsets) + alt -= new WDist(offset.PositionOffset.Z); + + return alt; + } } - public void SubtractLandingOffset(int offset) + public WPos GetPosition() { - LandAltitude -= new WDist(offset); + var pos = self.CenterPosition; + foreach (var offset in positionOffsets) + pos += offset.PositionOffset; + + return pos; } public virtual IEnumerable GetVariableObservers() @@ -281,6 +293,8 @@ namespace OpenRA.Mods.Common.Traits speedModifiers = self.TraitsImplementing().ToArray().Select(sm => sm.GetSpeedModifier()); cachedPosition = self.CenterPosition; notifyMoving = self.TraitsImplementing().ToArray(); + positionOffsets = self.TraitsImplementing().ToArray(); + overrideAircraftLanding = self.TraitOrDefault(); } void INotifyAddedToWorld.AddedToWorld(Actor self) @@ -589,8 +603,8 @@ namespace OpenRA.Mods.Common.Traits if (dockingActor != null) return true; - var type = self.World.Map.GetTerrainInfo(cell).Type; - return Info.LandableTerrainTypes.Contains(type); + var landableTerrain = overrideAircraftLanding != null ? overrideAircraftLanding.LandableTerrainTypes : Info.LandableTerrainTypes; + return landableTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type); } bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor, bool blockedByMobile = true) diff --git a/OpenRA.Mods.Common/Traits/AutoCarryall.cs b/OpenRA.Mods.Common/Traits/AutoCarryall.cs index ea619c164d..d7c930ca81 100644 --- a/OpenRA.Mods.Common/Traits/AutoCarryall.cs +++ b/OpenRA.Mods.Common/Traits/AutoCarryall.cs @@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits if (ReserveCarryable(self, carryable)) { self.QueueActivity(false, new PickupUnit(self, carryable, 0)); - self.QueueActivity(true, new DeliverUnit(self, destination)); + self.QueueActivity(true, new DeliverUnit(self, Target.FromCell(self.World, destination), Info.DropRange)); return true; } @@ -97,7 +97,7 @@ namespace OpenRA.Mods.Common.Traits { busy = true; self.QueueActivity(false, new PickupUnit(self, p.Actor, 0)); - self.QueueActivity(true, new DeliverUnit(self, p.Trait.Destination.Value)); + self.QueueActivity(true, new DeliverUnit(self, Target.FromCell(self.World, p.Trait.Destination.Value), Info.DropRange)); break; } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/FreeActorWithDelivery.cs b/OpenRA.Mods.Common/Traits/Buildings/FreeActorWithDelivery.cs index 0ad01d9c2f..e5bb183a1c 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/FreeActorWithDelivery.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/FreeActorWithDelivery.cs @@ -30,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Offset relative to the top-left cell of the building.")] public readonly CVec DeliveryOffset = CVec.Zero; + [Desc("Range to search for an alternative delivery location if the DeliveryOffset cell is blocked.")] + public readonly WDist DeliveryRange = WDist.Zero; + public override object Create(ActorInitializer init) { return new FreeActorWithDelivery(init, this); } } @@ -58,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits carryable.Reserve(cargo, carrier); carrier.Trait().AttachCarryable(carrier, cargo); - carrier.QueueActivity(new DeliverUnit(carrier, location)); + carrier.QueueActivity(new DeliverUnit(carrier, Target.FromCell(self.World, location), Info.DeliveryRange)); carrier.QueueActivity(new Fly(carrier, Target.FromCell(self.World, self.World.Map.ChooseRandomEdgeCell(self.World.SharedRandom)))); carrier.QueueActivity(new RemoveSelf()); diff --git a/OpenRA.Mods.Common/Traits/Carryall.cs b/OpenRA.Mods.Common/Traits/Carryall.cs index 45d82648df..d328723e71 100644 --- a/OpenRA.Mods.Common/Traits/Carryall.cs +++ b/OpenRA.Mods.Common/Traits/Carryall.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits } public class Carryall : INotifyKilled, ISync, ITick, IRender, INotifyActorDisposing, IIssueOrder, IResolveOrder, - IOrderVoice, IIssueDeployOrder + IOrderVoice, IIssueDeployOrder, IAircraftCenterPositionOffset, IOverrideAircraftLanding { public enum CarryallState { @@ -80,7 +80,8 @@ namespace OpenRA.Mods.Common.Traits public CarryallState State { get; private set; } int cachedFacing; - IActorPreview[] carryablePreview = null; + IActorPreview[] carryablePreview; + HashSet landableTerrainTypes; /// Offset between the carryall's and the carried actor's CenterPositions public WVec CarryableOffset { get; private set; } @@ -154,6 +155,17 @@ namespace OpenRA.Mods.Common.Traits return Info.LocalOffset - carryable.Info.TraitInfo().LocalOffset; } + WVec IAircraftCenterPositionOffset.PositionOffset + { + get + { + var localOffset = CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)); + return body.LocalToWorld(localOffset); + } + } + + HashSet IOverrideAircraftLanding.LandableTerrainTypes { get { return landableTerrainTypes ?? aircraft.Info.LandableTerrainTypes; } } + public virtual bool AttachCarryable(Actor self, Actor carryable) { if (State == CarryallState.Carrying) @@ -164,7 +176,8 @@ namespace OpenRA.Mods.Common.Traits self.World.ScreenMap.AddOrUpdate(self); CarryableOffset = OffsetForCarryable(self, carryable); - aircraft.AddLandingOffset(-CarryableOffset.Z); + landableTerrainTypes = Carryable.Trait().Info.LocomotorInfo.TerrainSpeeds.Keys.ToHashSet(); + return true; } @@ -174,7 +187,7 @@ namespace OpenRA.Mods.Common.Traits self.World.ScreenMap.AddOrUpdate(self); carryablePreview = null; - aircraft.SubtractLandingOffset(-CarryableOffset.Z); + landableTerrainTypes = null; CarryableOffset = WVec.Zero; } @@ -245,9 +258,8 @@ namespace OpenRA.Mods.Common.Traits // Check if we can drop the unit at our current location. public bool CanUnload() { - var localOffset = CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)); - var targetCell = self.World.Map.CellContaining(self.CenterPosition + body.LocalToWorld(localOffset)); - return Carryable != null && Carryable.Trait().CanEnterCell(targetCell, self); + var targetCell = self.World.Map.CellContaining(aircraft.GetPosition()); + return Carryable != null && aircraft.CanLand(targetCell, blockedByMobile: false); } IEnumerable IIssueOrder.Orders @@ -286,14 +298,14 @@ namespace OpenRA.Mods.Common.Traits var targetLocation = move.NearestMoveableCell(cell); self.SetTargetLine(Target.FromCell(self.World, targetLocation), Color.Yellow); - self.QueueActivity(order.Queued, new DeliverUnit(self, targetLocation)); + self.QueueActivity(order.Queued, new DeliverUnit(self, order.Target, Info.DropRange)); } else if (order.OrderString == "Unload") { if (!order.Queued && !CanUnload()) return; - self.QueueActivity(order.Queued, new DeliverUnit(self)); + self.QueueActivity(order.Queued, new DeliverUnit(self, Info.DropRange)); } if (order.OrderString == "PickupUnit") diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 7b9f789bf1..fb4586a77e 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -448,6 +448,16 @@ namespace OpenRA.Mods.Common.Traits Activity WrapMove(Activity moveInner); } + public interface IAircraftCenterPositionOffset + { + WVec PositionOffset { get; } + } + + public interface IOverrideAircraftLanding + { + HashSet LandableTerrainTypes { get; } + } + public interface IRadarSignature { void PopulateRadarSignatureCells(Actor self, List> destinationBuffer);