Replace IsDisabled checks in production with IsTraitPaused/Disabled checks

Note: We might want to separate IsTraitDisabled checks later (possibly make the latter cancel the currently produced item), but that can be done in a follow-up.
This commit is contained in:
reaperrr
2017-11-19 17:45:44 +01:00
committed by Paul Chote
parent 3aa8b3ae29
commit 383840135f
9 changed files with 93 additions and 34 deletions

View File

@@ -41,6 +41,9 @@ namespace OpenRA.Mods.Cnc.Traits
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits) public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{ {
if (IsTraitDisabled || IsTraitPaused)
return false;
var owner = self.Owner; var owner = self.Owner;
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>(); var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>();

View File

@@ -46,42 +46,50 @@ namespace OpenRA.Mods.Common.Traits
this.info = info; this.info = info;
} }
[Sync] bool isActive = false;
protected override void Tick(Actor self) protected override void Tick(Actor self)
{ {
// PERF: Avoid LINQ. // PERF: Avoid LINQ.
isActive = false; Enabled = false;
var isActive = false;
foreach (var x in self.World.ActorsWithTrait<Production>()) foreach (var x in self.World.ActorsWithTrait<Production>())
{ {
if (x.Actor.Owner == self.Owner && x.Trait.Info.Produces.Contains(Info.Type)) if (x.Trait.IsTraitDisabled)
{ continue;
isActive = true;
break; if (x.Actor.Owner != self.Owner || !x.Trait.Info.Produces.Contains(Info.Type))
} continue;
Enabled |= IsValidFaction;
isActive |= !x.Trait.IsTraitPaused;
} }
base.Tick(self); if (!Enabled)
ClearQueue();
TickInner(self, !isActive);
} }
public override IEnumerable<ActorInfo> AllItems() public override IEnumerable<ActorInfo> AllItems()
{ {
return isActive ? base.AllItems() : NoItems; return Enabled ? base.AllItems() : NoItems;
} }
public override IEnumerable<ActorInfo> BuildableItems() public override IEnumerable<ActorInfo> BuildableItems()
{ {
return isActive ? base.BuildableItems() : NoItems; return Enabled ? base.BuildableItems() : NoItems;
} }
public override TraitPair<Production> MostLikelyProducer() public override TraitPair<Production> MostLikelyProducer()
{ {
return self.World.ActorsWithTrait<Production>() var productionActors = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner .Where(x => x.Actor.Owner == self.Owner
&& x.Trait.Info.Produces.Contains(Info.Type)) && !x.Trait.IsTraitDisabled && x.Trait.Info.Produces.Contains(Info.Type))
.OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .OrderByDescending(x => x.Actor.IsPrimaryBuilding())
.ThenByDescending(x => x.Actor.ActorID) .ThenByDescending(x => x.Actor.ActorID)
.FirstOrDefault(); .ToList();
var unpaused = productionActors.FirstOrDefault(a => !a.Trait.IsTraitPaused);
return unpaused.Trait != null ? unpaused : productionActors.FirstOrDefault();
} }
protected override bool BuildUnit(ActorInfo unit) protected override bool BuildUnit(ActorInfo unit)
@@ -94,6 +102,7 @@ namespace OpenRA.Mods.Common.Traits
var producers = self.World.ActorsWithTrait<Production>() var producers = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner .Where(x => x.Actor.Owner == self.Owner
&& !x.Trait.IsTraitDisabled
&& x.Trait.Info.Produces.Contains(type)) && x.Trait.Info.Produces.Contains(type))
.OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .OrderByDescending(x => x.Actor.IsPrimaryBuilding())
.ThenByDescending(x => x.Actor.ActorID); .ThenByDescending(x => x.Actor.ActorID);
@@ -104,8 +113,11 @@ namespace OpenRA.Mods.Common.Traits
return false; return false;
} }
foreach (var p in producers.Where(p => !p.Actor.IsDisabled())) foreach (var p in producers)
{ {
if (p.Trait.IsTraitPaused)
continue;
var inits = new TypeDictionary var inits = new TypeDictionary
{ {
new OwnerInit(self.Owner), new OwnerInit(self.Owner),
@@ -134,7 +146,7 @@ namespace OpenRA.Mods.Common.Traits
var type = bi.BuildAtProductionType ?? info.Type; var type = bi.BuildAtProductionType ?? info.Type;
var selfsameProductionsCount = self.World.ActorsWithTrait<Production>() var selfsameProductionsCount = self.World.ActorsWithTrait<Production>()
.Count(p => p.Actor.Owner == self.Owner && p.Trait.Info.Produces.Contains(type)); .Count(p => !p.Trait.IsTraitDisabled && !p.Trait.IsTraitPaused && p.Actor.Owner == self.Owner && p.Trait.Info.Produces.Contains(type));
var speedModifier = selfsameProductionsCount.Clamp(1, info.BuildTimeSpeedReduction.Length) - 1; var speedModifier = selfsameProductionsCount.Clamp(1, info.BuildTimeSpeedReduction.Length) - 1;
time = (time * info.BuildTimeSpeedReduction[speedModifier]) / 100; time = (time * info.BuildTimeSpeedReduction[speedModifier]) / 100;

View File

@@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits
public virtual object Create(ActorInitializer init) { return new ProductionQueue(init, init.Self.Owner.PlayerActor, this); } public virtual object Create(ActorInitializer init) { return new ProductionQueue(init, init.Self.Owner.PlayerActor, this); }
} }
public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement, INotifyOwnerChanged, INotifyKilled, INotifySold, ISync, INotifyTransform public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement, INotifyOwnerChanged, INotifyKilled, INotifySold, ISync, INotifyTransform, INotifyCreated
{ {
public readonly ProductionQueueInfo Info; public readonly ProductionQueueInfo Info;
readonly Actor self; readonly Actor self;
@@ -76,6 +76,8 @@ namespace OpenRA.Mods.Common.Traits
readonly IEnumerable<ActorInfo> allProducibles; readonly IEnumerable<ActorInfo> allProducibles;
readonly IEnumerable<ActorInfo> buildableProducibles; readonly IEnumerable<ActorInfo> buildableProducibles;
Production[] productionTraits;
// Will change if the owner changes // Will change if the owner changes
PowerManager playerPower; PowerManager playerPower;
PlayerResources playerResources; PlayerResources playerResources;
@@ -89,9 +91,10 @@ namespace OpenRA.Mods.Common.Traits
[Sync] public int CurrentSlowdown { get { return QueueLength == 0 ? 0 : queue[0].Slowdown; } } [Sync] public int CurrentSlowdown { get { return QueueLength == 0 ? 0 : queue[0].Slowdown; } }
[Sync] public bool CurrentPaused { get { return QueueLength != 0 && queue[0].Paused; } } [Sync] public bool CurrentPaused { get { return QueueLength != 0 && queue[0].Paused; } }
[Sync] public bool CurrentDone { get { return QueueLength != 0 && queue[0].Done; } } [Sync] public bool CurrentDone { get { return QueueLength != 0 && queue[0].Done; } }
[Sync] public bool Enabled { get; private set; } [Sync] public bool Enabled { get; protected set; }
public string Faction { get; private set; } public string Faction { get; private set; }
[Sync] public bool IsValidFaction { get; private set; }
public ProductionQueue(ActorInitializer init, Actor playerActor, ProductionQueueInfo info) public ProductionQueue(ActorInitializer init, Actor playerActor, ProductionQueueInfo info)
{ {
@@ -102,14 +105,20 @@ namespace OpenRA.Mods.Common.Traits
developerMode = playerActor.Trait<DeveloperMode>(); developerMode = playerActor.Trait<DeveloperMode>();
Faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : self.Owner.Faction.InternalName; Faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : self.Owner.Faction.InternalName;
Enabled = !info.Factions.Any() || info.Factions.Contains(Faction); IsValidFaction = !info.Factions.Any() || info.Factions.Contains(Faction);
Enabled = IsValidFaction;
CacheProducibles(playerActor); CacheProducibles(playerActor);
allProducibles = producible.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); allProducibles = producible.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key);
buildableProducibles = producible.Where(a => a.Value.Buildable).Select(a => a.Key); buildableProducibles = producible.Where(a => a.Value.Buildable).Select(a => a.Key);
} }
void ClearQueue() void INotifyCreated.Created(Actor self)
{
productionTraits = self.TraitsImplementing<Production>().ToArray();
}
protected void ClearQueue()
{ {
if (queue.Count == 0) if (queue.Count == 0)
return; return;
@@ -130,7 +139,7 @@ namespace OpenRA.Mods.Common.Traits
if (!Info.Sticky) if (!Info.Sticky)
{ {
Faction = self.Owner.Faction.InternalName; Faction = self.Owner.Faction.InternalName;
Enabled = !Info.Factions.Any() || Info.Factions.Contains(Faction); IsValidFaction = !Info.Factions.Any() || Info.Factions.Contains(Faction);
} }
// Regenerate the producibles and tech tree state // Regenerate the producibles and tech tree state
@@ -236,6 +245,25 @@ namespace OpenRA.Mods.Common.Traits
} }
protected virtual void Tick(Actor self) protected virtual void Tick(Actor self)
{
// PERF: Avoid LINQ when checking whether all production traits are disabled/paused
var anyEnabledProduction = false;
var anyUnpausedProduction = false;
foreach (var p in productionTraits)
{
anyEnabledProduction |= !p.IsTraitDisabled;
anyUnpausedProduction |= !p.IsTraitPaused;
}
if (!anyEnabledProduction)
ClearQueue();
Enabled = IsValidFaction && anyEnabledProduction;
TickInner(self, !anyUnpausedProduction);
}
protected virtual void TickInner(Actor self, bool allProductionPaused)
{ {
while (queue.Count > 0 && BuildableItems().All(b => b.Name != queue[0].Item)) while (queue.Count > 0 && BuildableItems().All(b => b.Name != queue[0].Item))
{ {
@@ -244,7 +272,7 @@ namespace OpenRA.Mods.Common.Traits
FinishProduction(); FinishProduction();
} }
if (queue.Count > 0) if (queue.Count > 0 && !allProductionPaused)
queue[0].Tick(playerResources); queue[0].Tick(playerResources);
} }
@@ -367,16 +395,19 @@ namespace OpenRA.Mods.Common.Traits
// Returns the actor/trait that is most likely (but not necessarily guaranteed) to produce something in this queue // Returns the actor/trait that is most likely (but not necessarily guaranteed) to produce something in this queue
public virtual TraitPair<Production> MostLikelyProducer() public virtual TraitPair<Production> MostLikelyProducer()
{ {
var trait = self.TraitsImplementing<Production>().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type)); var traits = productionTraits.Where(p => !p.IsTraitDisabled && p.Info.Produces.Contains(Info.Type));
return new TraitPair<Production>(self, trait); var unpaused = traits.FirstOrDefault(a => !a.IsTraitPaused);
return new TraitPair<Production>(self, unpaused != null ? unpaused : traits.FirstOrDefault());
} }
// Builds a unit from the actor that holds this queue (1 queue per building) // Builds a unit from the actor that holds this queue (1 queue per building)
// Returns false if the unit can't be built // Returns false if the unit can't be built
protected virtual bool BuildUnit(ActorInfo unit) protected virtual bool BuildUnit(ActorInfo unit)
{ {
// Cannot produce if I'm dead var mostLikelyProducerTrait = MostLikelyProducer().Trait;
if (!self.IsInWorld || self.IsDead)
// Cannot produce if I'm dead or trait is disabled
if (!self.IsInWorld || self.IsDead || mostLikelyProducerTrait == null)
{ {
CancelProduction(unit.Name, 1); CancelProduction(unit.Name, 1);
return false; return false;
@@ -388,8 +419,7 @@ namespace OpenRA.Mods.Common.Traits
new FactionInit(BuildableInfo.GetInitialFaction(unit, Faction)) new FactionInit(BuildableInfo.GetInitialFaction(unit, Faction))
}; };
var sp = self.TraitsImplementing<Production>().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type)); if (!mostLikelyProducerTrait.IsTraitPaused && mostLikelyProducerTrait.Produce(self, unit, developerMode.AllTech ? null : Info.Type, inits))
if (sp != null && !self.IsDisabled() && sp.Produce(self, unit, developerMode.AllTech ? null : Info.Type, inits))
{ {
FinishProduction(); FinishProduction();
return true; return true;

View File

@@ -19,27 +19,26 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("This unit has access to build queues.")] [Desc("This unit has access to build queues.")]
public class ProductionInfo : ITraitInfo public class ProductionInfo : PausableConditionalTraitInfo
{ {
[FieldLoader.Require] [FieldLoader.Require]
[Desc("e.g. Infantry, Vehicles, Aircraft, Buildings")] [Desc("e.g. Infantry, Vehicles, Aircraft, Buildings")]
public readonly string[] Produces = { }; public readonly string[] Produces = { };
public virtual object Create(ActorInitializer init) { return new Production(init, this); } public override object Create(ActorInitializer init) { return new Production(init, this); }
} }
public class Production : INotifyCreated public class Production : PausableConditionalTrait<ProductionInfo>, INotifyCreated
{ {
readonly Lazy<RallyPoint> rp; readonly Lazy<RallyPoint> rp;
public readonly ProductionInfo Info;
public string Faction { get; private set; } public string Faction { get; private set; }
Building building; Building building;
public Production(ActorInitializer init, ProductionInfo info) public Production(ActorInitializer init, ProductionInfo info)
: base(info)
{ {
Info = info;
rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault<RallyPoint>()); rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault<RallyPoint>());
Faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName; Faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
} }
@@ -132,7 +131,7 @@ namespace OpenRA.Mods.Common.Traits
public virtual bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits) public virtual bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{ {
if (Reservable.IsReserved(self) || (building != null && building.Locked)) if (IsTraitDisabled || IsTraitPaused || Reservable.IsReserved(self) || (building != null && building.Locked))
return false; return false;
// Pick a spawn/exit point pair // Pick a spawn/exit point pair

View File

@@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Traits
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits) public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{ {
if (IsTraitDisabled || IsTraitPaused)
return false;
var aircraftInfo = producee.TraitInfoOrDefault<AircraftInfo>(); var aircraftInfo = producee.TraitInfoOrDefault<AircraftInfo>();
var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>(); var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>();

View File

@@ -45,6 +45,9 @@ namespace OpenRA.Mods.Common.Traits
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits) public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{ {
if (IsTraitDisabled || IsTraitPaused)
return false;
var owner = self.Owner; var owner = self.Owner;
var exit = SelectExit(self, producee, productionType); var exit = SelectExit(self, producee, productionType);

View File

@@ -100,6 +100,7 @@ GAPILE:
ExitsDebugOverlay: ExitsDebugOverlay:
Production: Production:
Produces: Infantry Produces: Infantry
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
ProductionBar: ProductionBar:
@@ -159,6 +160,7 @@ GAWEAP:
ExitsDebugOverlay: ExitsDebugOverlay:
Production: Production:
Produces: Vehicle Produces: Vehicle
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
ProductionBar: ProductionBar:
@@ -213,6 +215,7 @@ GAHPAD:
IsPlayerPalette: false IsPlayerPalette: false
Production: Production:
Produces: Air Produces: Air
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
Reservable: Reservable:
@@ -442,6 +445,7 @@ GAPLUG:
ChargeTime: 720 ChargeTime: 720
Production: Production:
Produces: HunterSeeker Produces: HunterSeeker
PauseOnCondition: empdisable
Exit@1: Exit@1:
ExitsDebugOverlay: ExitsDebugOverlay:
SupportPowerChargeBar: SupportPowerChargeBar:

View File

@@ -114,6 +114,7 @@ NAHAND:
IsPlayerPalette: false IsPlayerPalette: false
Production: Production:
Produces: Infantry Produces: Infantry
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
ProductionBar: ProductionBar:
@@ -171,6 +172,7 @@ NAWEAP:
ExitsDebugOverlay: ExitsDebugOverlay:
Production: Production:
Produces: Vehicle Produces: Vehicle
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
ProductionBar: ProductionBar:
@@ -221,6 +223,7 @@ NAHPAD:
IsPlayerPalette: false IsPlayerPalette: false
Production: Production:
Produces: Air Produces: Air
PauseOnCondition: empdisable
PrimaryBuilding: PrimaryBuilding:
PrimaryCondition: primary PrimaryCondition: primary
Reservable: Reservable:
@@ -378,5 +381,6 @@ NATMPL:
ChargeTime: 720 ChargeTime: 720
Production: Production:
Produces: HunterSeeker Produces: HunterSeeker
PauseOnCondition: empdisable
Exit@1: Exit@1:
ExitsDebugOverlay: ExitsDebugOverlay:

View File

@@ -19,6 +19,7 @@ GACNST:
MaxHeightDelta: 3 MaxHeightDelta: 3
Production: Production:
Produces: Building,Defense Produces: Building,Defense
PauseOnCondition: empdisable
Valued: Valued:
Cost: 2500 Cost: 2500
Tooltip: Tooltip: