Improve Carryall behaviour and integration with Aircraft.

This commit is contained in:
tovl
2019-06-30 14:01:55 +00:00
committed by reaperrr
parent adecd4ca87
commit 8e5875453a
9 changed files with 96 additions and 155 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<Carryall>();
body = self.Trait<BodyOrientation>();
}
Target FindDropLocation(Target requested, WDist maxSearchDistance)
protected override void OnFirstRun(Actor self)
{
var positionable = carryall.Carryable.Trait<IPositionable>();
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;
}
class ReleaseUnit : Activity

View File

@@ -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,64 +86,24 @@ 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;
}

View File

@@ -187,10 +187,12 @@ namespace OpenRA.Mods.Common.Traits
Repairable repairable;
Rearmable rearmable;
IAircraftCenterPositionOffset[] positionOffsets;
ConditionManager conditionManager;
IDisposable reservation;
IEnumerable<int> speedModifiers;
INotifyMoving[] notifyMoving;
IOverrideAircraftLanding overrideAircraftLanding;
[Sync]
public int Facing { get; set; }
@@ -206,13 +208,13 @@ namespace OpenRA.Mods.Common.Traits
IEnumerable<CPos> landingCells = Enumerable.Empty<CPos>();
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<CenterPositionInit, WPos>());
Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 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<VariableObserver> GetVariableObservers()
@@ -281,6 +293,8 @@ namespace OpenRA.Mods.Common.Traits
speedModifiers = self.TraitsImplementing<ISpeedModifier>().ToArray().Select(sm => sm.GetSpeedModifier());
cachedPosition = self.CenterPosition;
notifyMoving = self.TraitsImplementing<INotifyMoving>().ToArray();
positionOffsets = self.TraitsImplementing<IAircraftCenterPositionOffset>().ToArray();
overrideAircraftLanding = self.TraitOrDefault<IOverrideAircraftLanding>();
}
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)

View File

@@ -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;
}
}

View File

@@ -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<Carryall>().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());

View File

@@ -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<string> landableTerrainTypes;
/// <summary>Offset between the carryall's and the carried actor's CenterPositions</summary>
public WVec CarryableOffset { get; private set; }
@@ -154,6 +155,17 @@ namespace OpenRA.Mods.Common.Traits
return Info.LocalOffset - carryable.Info.TraitInfo<CarryableInfo>().LocalOffset;
}
WVec IAircraftCenterPositionOffset.PositionOffset
{
get
{
var localOffset = CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation));
return body.LocalToWorld(localOffset);
}
}
HashSet<string> 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<Mobile>().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<IPositionable>().CanEnterCell(targetCell, self);
var targetCell = self.World.Map.CellContaining(aircraft.GetPosition());
return Carryable != null && aircraft.CanLand(targetCell, blockedByMobile: false);
}
IEnumerable<IOrderTargeter> 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")

View File

@@ -448,6 +448,16 @@ namespace OpenRA.Mods.Common.Traits
Activity WrapMove(Activity moveInner);
}
public interface IAircraftCenterPositionOffset
{
WVec PositionOffset { get; }
}
public interface IOverrideAircraftLanding
{
HashSet<string> LandableTerrainTypes { get; }
}
public interface IRadarSignature
{
void PopulateRadarSignatureCells(Actor self, List<Pair<CPos, Color>> destinationBuffer);