Auto carry action can be controlled by condition
This commit is contained in:
@@ -25,6 +25,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public class AutoCarryable : Carryable, ICallForTransport
|
||||
{
|
||||
readonly AutoCarryableInfo info;
|
||||
bool autoReserved = false;
|
||||
|
||||
public CPos? Destination { get; private set; }
|
||||
public bool WantsTransport => Destination != null && !IsTraitDisabled;
|
||||
|
||||
public AutoCarryable(AutoCarryableInfo info)
|
||||
: base(info)
|
||||
@@ -44,14 +48,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return;
|
||||
|
||||
Destination = null;
|
||||
autoReserved = false;
|
||||
|
||||
// TODO: We could implement something like a carrier.Trait<Carryall>().CancelTransportNotify(self) and call it here
|
||||
}
|
||||
|
||||
void RequestTransport(Actor self, CPos destination)
|
||||
{
|
||||
var delta = self.World.Map.CenterOfCell(destination) - self.CenterPosition;
|
||||
if (delta.HorizontalLengthSquared < info.MinDistance.LengthSquared)
|
||||
if (!IsValidAutoCarryDistance(self, destination))
|
||||
{
|
||||
Destination = null;
|
||||
return;
|
||||
@@ -63,13 +67,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return;
|
||||
|
||||
// Inform all idle carriers
|
||||
var carriers = self.World.ActorsWithTrait<Carryall>()
|
||||
.Where(c => c.Trait.State == Carryall.CarryallState.Idle && !c.Trait.IsTraitDisabled && !c.Actor.IsDead && c.Actor.Owner == self.Owner && c.Actor.IsInWorld)
|
||||
var carriers = self.World.ActorsWithTrait<AutoCarryall>()
|
||||
.Where(c => c.Trait.State == Carryall.CarryallState.Idle && !c.Trait.IsTraitDisabled && c.Trait.EnableAutoCarry && !c.Actor.IsDead && c.Actor.Owner == self.Owner && c.Actor.IsInWorld)
|
||||
.OrderBy(p => (self.Location - p.Actor.Location).LengthSquared);
|
||||
|
||||
// Enumerate idle carriers to find the first that is able to transport us
|
||||
foreach (var carrier in carriers)
|
||||
if (carrier.Trait.RequestTransportNotify(carrier.Actor, self, destination))
|
||||
if (carrier.Trait.RequestTransportNotify(carrier.Actor, self))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,38 +88,66 @@ namespace OpenRA.Mods.Common.Traits
|
||||
base.Detached(self);
|
||||
}
|
||||
|
||||
public override bool Reserve(Actor self, Actor carrier)
|
||||
public bool AutoReserve(Actor self, Actor carrier)
|
||||
{
|
||||
if (Reserved || !WantsTransport)
|
||||
return false;
|
||||
|
||||
var delta = self.World.Map.CenterOfCell(Destination.Value) - self.CenterPosition;
|
||||
if (delta.HorizontalLengthSquared < info.MinDistance.LengthSquared)
|
||||
if (!IsValidAutoCarryDistance(self, Destination.Value))
|
||||
{
|
||||
// Cancel pickup
|
||||
MovementCancelled();
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.Reserve(self, carrier);
|
||||
if (Reserve(self, carrier))
|
||||
{
|
||||
autoReserved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare for transport pickup
|
||||
public override LockResponse LockForPickup(Actor self, Actor carrier)
|
||||
{
|
||||
if ((state == State.Locked && Carrier != carrier) || !WantsTransport)
|
||||
if (state == State.Locked && Carrier != carrier)
|
||||
return LockResponse.Failed;
|
||||
|
||||
// Last chance to change our mind...
|
||||
var delta = self.World.Map.CenterOfCell(Destination.Value) - self.CenterPosition;
|
||||
if (delta.HorizontalLengthSquared < info.MinDistance.LengthSquared)
|
||||
// When "autoReserved" is true, the carrying operation is given by auto command
|
||||
// we still need to check the validity of "Destination" to ensure an effective trip.
|
||||
if (autoReserved)
|
||||
{
|
||||
// Cancel pickup
|
||||
MovementCancelled();
|
||||
return LockResponse.Failed;
|
||||
if (!WantsTransport)
|
||||
{
|
||||
// Cancel pickup
|
||||
MovementCancelled();
|
||||
return LockResponse.Failed;
|
||||
}
|
||||
|
||||
if (!IsValidAutoCarryDistance(self, Destination.Value))
|
||||
{
|
||||
// Cancel pickup
|
||||
MovementCancelled();
|
||||
return LockResponse.Failed;
|
||||
}
|
||||
|
||||
// Reset "autoReserved" as we finished the check
|
||||
autoReserved = false;
|
||||
}
|
||||
|
||||
return base.LockForPickup(self, carrier);
|
||||
}
|
||||
|
||||
bool IsValidAutoCarryDistance(Actor self, CPos destination)
|
||||
{
|
||||
if (Mobile == null)
|
||||
return false;
|
||||
|
||||
// TODO: change the check here to pathfinding distance in the future
|
||||
return (self.World.Map.CenterOfCell(destination) - self.CenterPosition).HorizontalLengthSquared >= info.MinDistance.LengthSquared
|
||||
|| !Mobile.PathFinder.PathExistsForLocomotor(Mobile.Locomotor, self.Location, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,39 +9,69 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Automatically transports harvesters with the Carryable trait between resource fields and refineries.")]
|
||||
[Desc("Automatically transports harvesters with the AutoCarryable and CarryableHarvester between resource fields and refineries.")]
|
||||
public class AutoCarryallInfo : CarryallInfo
|
||||
{
|
||||
[ConsumedConditionReference]
|
||||
[Desc("Boolean expression defining the condition under which the auto carry behavior is enabled. Enabled by default.")]
|
||||
public readonly BooleanExpression AutoCarryCondition = null;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new AutoCarryall(init.Self, this); }
|
||||
}
|
||||
|
||||
public class AutoCarryall : Carryall, INotifyBecomingIdle
|
||||
public class AutoCarryall : Carryall, INotifyBecomingIdle, IObservesVariables
|
||||
{
|
||||
bool busy;
|
||||
readonly AutoCarryallInfo info;
|
||||
|
||||
public bool EnableAutoCarry { get; private set; }
|
||||
|
||||
public AutoCarryall(Actor self, AutoCarryallInfo info)
|
||||
: base(self, info) { }
|
||||
: base(self, info)
|
||||
{
|
||||
this.info = info;
|
||||
EnableAutoCarry = true;
|
||||
}
|
||||
|
||||
static bool Busy(Actor self) => self.CurrentActivity != null && self.CurrentActivity is not FlyIdle;
|
||||
|
||||
void INotifyBecomingIdle.OnBecomingIdle(Actor self)
|
||||
{
|
||||
busy = false;
|
||||
if (!EnableAutoCarry || IsTraitDisabled)
|
||||
return;
|
||||
|
||||
FindCarryableForTransport(self);
|
||||
}
|
||||
|
||||
// A carryable notifying us that he'd like to be carried
|
||||
public override bool RequestTransportNotify(Actor self, Actor carryable, CPos destination)
|
||||
public override IEnumerable<VariableObserver> GetVariableObservers()
|
||||
{
|
||||
if (busy || IsTraitDisabled)
|
||||
foreach (var observer in base.GetVariableObservers())
|
||||
yield return observer;
|
||||
|
||||
if (info.AutoCarryCondition != null)
|
||||
yield return new VariableObserver(AutoCarryConditionsChanged, info.AutoCarryCondition.Variables);
|
||||
}
|
||||
|
||||
void AutoCarryConditionsChanged(Actor self, IReadOnlyDictionary<string, int> conditions)
|
||||
{
|
||||
EnableAutoCarry = info.AutoCarryCondition.Evaluate(conditions);
|
||||
}
|
||||
|
||||
// A carryable notifying us that he'd like to be carried
|
||||
public bool RequestTransportNotify(Actor self, Actor carryable)
|
||||
{
|
||||
if (Busy(self) || IsTraitDisabled || !EnableAutoCarry)
|
||||
return false;
|
||||
|
||||
if (ReserveCarryable(self, carryable))
|
||||
if (AutoReserveCarryable(self, carryable))
|
||||
{
|
||||
self.QueueActivity(false, new FerryUnit(self, carryable));
|
||||
return true;
|
||||
@@ -50,10 +80,28 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AutoReserveCarryable(Actor self, Actor carryable)
|
||||
{
|
||||
if (State == CarryallState.Reserved)
|
||||
UnreserveCarryable(self);
|
||||
|
||||
if (State != CarryallState.Idle)
|
||||
return false;
|
||||
|
||||
var act = carryable.TraitOrDefault<AutoCarryable>();
|
||||
|
||||
if (act == null || !act.AutoReserve(carryable, self))
|
||||
return false;
|
||||
|
||||
Carryable = carryable;
|
||||
State = CarryallState.Reserved;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsBestAutoCarryallForCargo(Actor self, Actor candidateCargo)
|
||||
{
|
||||
// Find carriers
|
||||
var carriers = self.World.ActorsHavingTrait<AutoCarryall>(c => !c.busy)
|
||||
var carriers = self.World.ActorsHavingTrait<AutoCarryall>(c => !Busy(self) && c.EnableAutoCarry)
|
||||
.Where(a => a.Owner == self.Owner && a.IsInWorld);
|
||||
|
||||
return carriers.ClosestTo(candidateCargo) == self;
|
||||
@@ -65,7 +113,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return;
|
||||
|
||||
// Get all carryables who want transport
|
||||
var carryables = self.World.ActorsWithTrait<Carryable>().Where(p =>
|
||||
var carryables = self.World.ActorsWithTrait<AutoCarryable>().Where(p =>
|
||||
{
|
||||
var actor = p.Actor;
|
||||
if (actor == null)
|
||||
@@ -93,9 +141,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var p in carryables)
|
||||
{
|
||||
// Check if its actually me who's the best candidate
|
||||
if (IsBestAutoCarryallForCargo(self, p.Actor) && ReserveCarryable(self, p.Actor))
|
||||
if (IsBestAutoCarryallForCargo(self, p.Actor) && AutoReserveCarryable(self, p.Actor))
|
||||
{
|
||||
busy = true;
|
||||
self.QueueActivity(false, new FerryUnit(self, p.Actor));
|
||||
break;
|
||||
}
|
||||
@@ -105,32 +152,48 @@ namespace OpenRA.Mods.Common.Traits
|
||||
sealed class FerryUnit : Activity
|
||||
{
|
||||
readonly Actor cargo;
|
||||
readonly Carryable carryable;
|
||||
readonly Carryall carryall;
|
||||
readonly AutoCarryable carryable;
|
||||
readonly AutoCarryall carryall;
|
||||
|
||||
public FerryUnit(Actor self, Actor cargo)
|
||||
{
|
||||
this.cargo = cargo;
|
||||
carryable = cargo.Trait<Carryable>();
|
||||
carryall = self.Trait<Carryall>();
|
||||
carryable = cargo.Trait<AutoCarryable>();
|
||||
carryall = self.Trait<AutoCarryall>();
|
||||
}
|
||||
|
||||
protected override void OnFirstRun(Actor self)
|
||||
{
|
||||
if (!cargo.IsDead && !carryall.IsTraitDisabled)
|
||||
if (!carryall.IsTraitDisabled && carryall.Carryable != null && !carryall.Carryable.IsDead)
|
||||
QueueChild(new PickupUnit(self, cargo, 0, carryall.Info.TargetLineColor));
|
||||
}
|
||||
|
||||
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
{
|
||||
// Draw a line to destination if haven't pick up the cargo
|
||||
if (ChildActivity is PickupUnit)
|
||||
{
|
||||
yield return new TargetLineNode(Target.FromActor(cargo), carryall.Info.TargetLineColor);
|
||||
if (carryable.Destination != null)
|
||||
yield return new TargetLineNode(Target.FromCell(self.World, carryable.Destination.Value), carryall.Info.TargetLineColor);
|
||||
}
|
||||
else
|
||||
foreach (var n in ChildActivity.TargetLineNodes(self))
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
// Cargo may have become invalid or PickupUnit cancelled.
|
||||
if (carryall.IsTraitDisabled || carryall.Carryable == null || carryall.Carryable.IsDead)
|
||||
if (IsCanceling || carryall.IsTraitDisabled || carryall.Carryable == null || carryall.Carryable.IsDead)
|
||||
return true;
|
||||
|
||||
var dropRange = carryall.Info.DropRange;
|
||||
var destination = carryable.Destination;
|
||||
if (destination != null)
|
||||
self.QueueActivity(true, new DeliverUnit(self, Target.FromCell(self.World, destination.Value), dropRange, carryall.Info.TargetLineColor));
|
||||
if (carryable.Destination != null)
|
||||
QueueChild(new DeliverUnit(self, Target.FromCell(self.World, carryable.Destination.Value), dropRange, carryall.Info.TargetLineColor));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,14 +48,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
int carriedToken = Actor.InvalidConditionToken;
|
||||
int lockedToken = Actor.InvalidConditionToken;
|
||||
|
||||
Mobile mobile;
|
||||
IDelayCarryallPickup[] delayPickups;
|
||||
|
||||
public Actor Carrier { get; private set; }
|
||||
public bool Reserved => state != State.Free;
|
||||
public CPos? Destination { get; protected set; }
|
||||
public bool WantsTransport => Destination != null && !IsTraitDisabled;
|
||||
|
||||
protected Mobile Mobile { get; private set; }
|
||||
protected enum State { Free, Reserved, Locked }
|
||||
protected State state = State.Free;
|
||||
protected bool attached;
|
||||
@@ -65,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
protected override void Created(Actor self)
|
||||
{
|
||||
mobile = self.TraitOrDefault<Mobile>();
|
||||
Mobile = self.TraitOrDefault<Mobile>();
|
||||
delayPickups = self.TraitsImplementing<IDelayCarryallPickup>().ToArray();
|
||||
|
||||
base.Created(self);
|
||||
@@ -129,7 +127,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (delayPickups.Any(d => d.IsTraitEnabled() && !d.TryLockForPickup(self, carrier)))
|
||||
return LockResponse.Pending;
|
||||
|
||||
if (mobile != null && !mobile.CanStayInCell(self.Location))
|
||||
if (Mobile != null && !Mobile.CanStayInCell(self.Location))
|
||||
return LockResponse.Pending;
|
||||
|
||||
if (state != State.Locked)
|
||||
@@ -142,7 +140,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
// Make sure we are not moving and at our normal position with respect to the cell grid
|
||||
if (mobile != null && mobile.IsMovingBetweenCells)
|
||||
if (Mobile != null && Mobile.IsMovingBetweenCells)
|
||||
return LockResponse.Pending;
|
||||
|
||||
return LockResponse.Success;
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Carrying
|
||||
}
|
||||
|
||||
readonly AircraftInfo aircraftInfo;
|
||||
protected readonly AircraftInfo AircraftInfo;
|
||||
readonly Aircraft aircraft;
|
||||
readonly BodyOrientation body;
|
||||
readonly IFacing facing;
|
||||
@@ -102,8 +102,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
// The actor we are currently carrying.
|
||||
[Sync]
|
||||
public Actor Carryable { get; private set; }
|
||||
public CarryallState State { get; private set; }
|
||||
public Actor Carryable { get; protected set; }
|
||||
public CarryallState State { get; protected set; }
|
||||
|
||||
WAngle cachedFacing;
|
||||
IActorPreview[] carryablePreview;
|
||||
@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Carryable = null;
|
||||
State = CarryallState.Idle;
|
||||
|
||||
aircraftInfo = self.Info.TraitInfoOrDefault<AircraftInfo>();
|
||||
AircraftInfo = self.Info.TraitInfoOrDefault<AircraftInfo>();
|
||||
aircraft = self.Trait<Aircraft>();
|
||||
body = self.Trait<BodyOrientation>();
|
||||
facing = self.Trait<IFacing>();
|
||||
@@ -183,11 +183,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
UnreserveCarryable(self);
|
||||
}
|
||||
|
||||
public virtual bool RequestTransportNotify(Actor self, Actor carryable, CPos destination)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual WVec OffsetForCarryable(Actor self, Actor carryable)
|
||||
{
|
||||
return Info.LocalOffset - carryable.Info.TraitInfo<CarryableInfo>().LocalOffset;
|
||||
@@ -239,7 +234,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
CarryableOffset = WVec.Zero;
|
||||
}
|
||||
|
||||
public virtual bool ReserveCarryable(Actor self, Actor carryable)
|
||||
public bool ReserveCarryable(Actor self, Actor carryable)
|
||||
{
|
||||
if (State == CarryallState.Reserved)
|
||||
UnreserveCarryable(self);
|
||||
@@ -327,7 +322,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
yield return new CarryallPickupOrderTargeter(Info);
|
||||
yield return new DeployOrderTargeter("Unload", 10,
|
||||
() => CanUnload() ? Info.UnloadCursor : Info.UnloadBlockedCursor);
|
||||
yield return new CarryallDeliverUnitTargeter(aircraftInfo, Info);
|
||||
yield return new CarryallDeliverUnitTargeter(AircraftInfo, Info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +349,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (order.OrderString == "DeliverUnit")
|
||||
{
|
||||
var cell = self.World.Map.Clamp(self.World.Map.CellContaining(order.Target.CenterPosition));
|
||||
if (!aircraftInfo.MoveIntoShroud && !self.Owner.Shroud.IsExplored(cell))
|
||||
if (!AircraftInfo.MoveIntoShroud && !self.Owner.Shroud.IsExplored(cell))
|
||||
return;
|
||||
|
||||
self.QueueActivity(order.Queued, new DeliverUnit(self, order.Target, Info.DropRange, Info.TargetLineColor));
|
||||
|
||||
Reference in New Issue
Block a user