Auto carry action can be controlled by condition

This commit is contained in:
dnqbob
2022-10-25 20:03:00 +08:00
committed by Gustas
parent 98896f9a75
commit c9dfb215ae
4 changed files with 144 additions and 56 deletions

View File

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