diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/AttackOrderPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/AttackOrderPower.cs index 10ed146951..2373665b29 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/AttackOrderPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/AttackOrderPower.cs @@ -88,7 +88,7 @@ namespace OpenRA.Mods.Cnc.Traits var pos = world.Map.CenterOfCell(cell); var range = attack.GetMaximumRange().LengthSquared; - return instance.Instances.Where(i => !i.Self.IsDisabled()).MinByOrDefault(a => (a.Self.CenterPosition - pos).HorizontalLengthSquared).Self; + return instance.Instances.Where(i => !i.IsTraitPaused).MinByOrDefault(a => (a.Self.CenterPosition - pos).HorizontalLengthSquared).Self; } bool IsValidTarget(World world, CPos cell) @@ -96,7 +96,7 @@ namespace OpenRA.Mods.Cnc.Traits var pos = world.Map.CenterOfCell(cell); var range = attack.GetMaximumRange().LengthSquared; - return world.Map.Contains(cell) && instance.Instances.Any(a => !a.Self.IsDisabled() && (a.Self.CenterPosition - pos).HorizontalLengthSquared < range); + return world.Map.Contains(cell) && instance.Instances.Any(a => !a.IsTraitPaused && (a.Self.CenterPosition - pos).HorizontalLengthSquared < range); } IEnumerable IOrderGenerator.Order(World world, CPos cell, int2 worldPixel, MouseInput mi) @@ -122,7 +122,7 @@ namespace OpenRA.Mods.Cnc.Traits IEnumerable IOrderGenerator.RenderAboveShroud(WorldRenderer wr, World world) { - foreach (var a in instance.Instances.Where(i => !i.Self.IsDisabled())) + foreach (var a in instance.Instances.Where(i => !i.IsTraitPaused)) { yield return new RangeCircleRenderable( a.Self.CenterPosition, diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/GpsPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/GpsPower.cs index b0570a57af..f3b0d0a975 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/GpsPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/GpsPower.cs @@ -101,18 +101,18 @@ namespace OpenRA.Mods.Cnc.Traits } bool NoActiveRadar { get { return !self.World.ActorsHavingTrait(r => !r.IsTraitDisabled).Any(a => a.Owner == self.Owner); } } - bool wasDisabled; + bool wasPaused; void ITick.Tick(Actor self) { - if (!wasDisabled && (self.IsDisabled() || (info.RequiresActiveRadar && NoActiveRadar))) + if (!wasPaused && (IsTraitPaused || (info.RequiresActiveRadar && NoActiveRadar))) { - wasDisabled = true; + wasPaused = true; RemoveGps(self); } - else if (wasDisabled && !self.IsDisabled() && !(info.RequiresActiveRadar && NoActiveRadar)) + else if (wasPaused && !IsTraitPaused && !(info.RequiresActiveRadar && NoActiveRadar)) { - wasDisabled = false; + wasPaused = false; owner.GpsAdd(self); } } diff --git a/OpenRA.Mods.Common/Commands/DevCommands.cs b/OpenRA.Mods.Common/Commands/DevCommands.cs index d4191309c7..b8d3e212a4 100644 --- a/OpenRA.Mods.Common/Commands/DevCommands.cs +++ b/OpenRA.Mods.Common/Commands/DevCommands.cs @@ -53,6 +53,7 @@ namespace OpenRA.Mods.Common.Commands register("all", "toggles all cheats and gives you some cash for your trouble."); register("crash", "crashes the game."); register("levelup", "adds a specified number of levels to the selected actors."); + register("poweroutage", "causes owners of selected actors to have a 5 second power outage."); } public void InvokeCommand(string name, string arg) @@ -123,6 +124,11 @@ namespace OpenRA.Mods.Common.Commands } break; + + case "poweroutage": + foreach (var player in world.Selection.Actors.Select(a => a.Owner.PlayerActor).Distinct()) + world.IssueOrder(new Order("PowerOutage", player, false) { ExtraData = 250 }); + break; } } diff --git a/OpenRA.Mods.Common/Traits/Power/AffectedByPowerOutage.cs b/OpenRA.Mods.Common/Traits/Power/AffectedByPowerOutage.cs index c89fc0018f..019bbb4adb 100644 --- a/OpenRA.Mods.Common/Traits/Power/AffectedByPowerOutage.cs +++ b/OpenRA.Mods.Common/Traits/Power/AffectedByPowerOutage.cs @@ -15,23 +15,39 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Disables the actor when a power outage is triggered (see `InfiltrateForPowerOutage` for more information).")] - public class AffectedByPowerOutageInfo : ITraitInfo + public class AffectedByPowerOutageInfo : ConditionalTraitInfo { - public object Create(ActorInitializer init) { return new AffectedByPowerOutage(init.Self); } + [GrantedConditionReference] + [Desc("The condition to grant while there is a power outage.")] + public readonly string Condition = null; + + public override object Create(ActorInitializer init) { return new AffectedByPowerOutage(init.Self, this); } } - public class AffectedByPowerOutage : INotifyOwnerChanged, ISelectionBar, IPowerModifier, IDisable + public class AffectedByPowerOutage : ConditionalTrait, INotifyOwnerChanged, ISelectionBar, INotifyCreated, INotifyAddedToWorld { PowerManager playerPower; + ConditionManager conditionManager; + int token = ConditionManager.InvalidConditionToken; - public AffectedByPowerOutage(Actor self) + public AffectedByPowerOutage(Actor self, AffectedByPowerOutageInfo info) + : base(info) { playerPower = self.Owner.PlayerActor.Trait(); } + void INotifyAddedToWorld.AddedToWorld(Actor self) { UpdateStatus(self); } + protected override void TraitEnabled(Actor self) { UpdateStatus(self); } + protected override void TraitDisabled(Actor self) { Revoke(self); } + + void INotifyCreated.Created(Actor self) + { + conditionManager = self.TraitOrDefault(); + } + float ISelectionBar.GetValue() { - if (playerPower.PowerOutageRemainingTicks <= 0) + if (IsTraitDisabled || playerPower.PowerOutageRemainingTicks <= 0) return 0; return (float)playerPower.PowerOutageRemainingTicks / playerPower.PowerOutageTotalTicks; @@ -44,19 +60,30 @@ namespace OpenRA.Mods.Common.Traits bool ISelectionBar.DisplayWhenEmpty { get { return false; } } - int IPowerModifier.GetPowerModifier() + public void UpdateStatus(Actor self) { - return playerPower.PowerOutageRemainingTicks > 0 ? 0 : 100; + if (!IsTraitDisabled && playerPower.PowerOutageRemainingTicks > 0) + Grant(self); + else + Revoke(self); } - public bool Disabled + void Grant(Actor self) { - get { return playerPower.PowerOutageRemainingTicks > 0; } + if (token == ConditionManager.InvalidConditionToken) + token = conditionManager.GrantCondition(self, Info.Condition); + } + + void Revoke(Actor self) + { + if (token != ConditionManager.InvalidConditionToken) + token = conditionManager.RevokeCondition(self, token); } void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { playerPower = newOwner.PlayerActor.Trait(); + UpdateStatus(self); } } } diff --git a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs index 0467ac26e4..30dfd9eca4 100644 --- a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs +++ b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs @@ -9,6 +9,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new PowerManager(init.Self, this); } } - public class PowerManager : ITick, ISync + public class PowerManager : ITick, ISync, IResolveOrder { readonly Actor self; readonly PowerManagerInfo info; @@ -140,11 +141,17 @@ namespace OpenRA.Mods.Common.Traits void UpdatePowerOutageActors() { - var actors = self.World.ActorsHavingTrait() - .Where(a => !a.IsDead && a.IsInWorld && a.Owner == self.Owner); + var traitPairs = self.World.ActorsWithTrait() + .Where(p => !p.Actor.IsDead && p.Actor.IsInWorld && p.Actor.Owner == self.Owner); - foreach (var a in actors) - UpdateActor(a); + foreach (var p in traitPairs) + p.Trait.UpdateStatus(p.Actor); + } + + void IResolveOrder.ResolveOrder(Actor self, Order order) + { + if (devMode.Enabled && order.OrderString == "PowerOutage") + TriggerPowerOutage((int)order.ExtraData); } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs index 9bc5b784d8..c96553387c 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs @@ -21,9 +21,6 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly int Interval = 750; - [Desc("Pause when the actor is disabled. Deprecated. Use conditions instead.")] - public readonly bool PauseOnLowPower = false; - public override object Create(ActorInitializer init) { return new WithIdleAnimation(init.Self, this); } } @@ -48,8 +45,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (--ticks <= 0) { - if (!(Info.PauseOnLowPower && self.IsDisabled())) - wsb.PlayCustomAnimation(self, Info.Sequences.Random(Game.CosmeticRandom), () => wsb.CancelCustomAnimation(self)); + wsb.PlayCustomAnimation(self, Info.Sequences.Random(Game.CosmeticRandom), () => wsb.CancelCustomAnimation(self)); ticks = Info.Interval; } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs index 169baf6d30..89eb8d15ed 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs @@ -18,7 +18,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [Desc("Renders a decorative animation on units and buildings.")] - public class WithIdleOverlayInfo : ConditionalTraitInfo, IRenderActorPreviewSpritesInfo, Requires, Requires + public class WithIdleOverlayInfo : PausableConditionalTraitInfo, IRenderActorPreviewSpritesInfo, Requires, Requires { [Desc("Animation to play when the actor is created.")] [SequenceReference] public readonly string StartSequence = null; @@ -35,8 +35,6 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Custom palette is a player palette BaseName")] public readonly bool IsPlayerPalette = false; - public readonly bool PauseOnLowPower = false; - public override object Create(ActorInitializer init) { return new WithIdleOverlay(init.Self, this); } public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) @@ -72,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class WithIdleOverlay : ConditionalTrait, INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyTransform + public class WithIdleOverlay : PausableConditionalTrait, INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyTransform { readonly Animation overlay; bool buildComplete; @@ -84,8 +82,7 @@ namespace OpenRA.Mods.Common.Traits.Render var body = self.Trait(); buildComplete = !self.Info.HasTraitInfo(); // always render instantly for units - overlay = new Animation(self.World, rs.GetImage(self), - () => (info.PauseOnLowPower && self.IsDisabled()) || !buildComplete); + overlay = new Animation(self.World, rs.GetImage(self), () => IsTraitPaused || !buildComplete); if (info.StartSequence != null) overlay.PlayThen(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.StartSequence), () => overlay.PlayRepeating(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.Sequence))); diff --git a/OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs index 724beb38a0..e3b4a79cf4 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs @@ -14,32 +14,29 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [Desc("Replaces the building animation when it rearms a unit.")] - public class WithRearmAnimationInfo : ITraitInfo, Requires + public class WithRearmAnimationInfo : ConditionalTraitInfo, Requires { [Desc("Sequence name to use")] [SequenceReference] public readonly string Sequence = "active"; - public readonly bool PauseOnLowPower = false; - - public object Create(ActorInitializer init) { return new WithRearmAnimation(init.Self, this); } + public override object Create(ActorInitializer init) { return new WithRearmAnimation(init.Self, this); } } - public class WithRearmAnimation : INotifyRearm, INotifyBuildComplete, INotifySold + public class WithRearmAnimation : ConditionalTrait, INotifyRearm, INotifyBuildComplete, INotifySold { - readonly WithRearmAnimationInfo info; readonly WithSpriteBody spriteBody; bool buildComplete; public WithRearmAnimation(Actor self, WithRearmAnimationInfo info) + : base(info) { - this.info = info; spriteBody = self.TraitOrDefault(); } void INotifyRearm.Rearming(Actor self, Actor target) { - if (buildComplete && spriteBody != null && !(info.PauseOnLowPower && self.IsDisabled())) - spriteBody.PlayCustomAnimation(self, info.Sequence, () => spriteBody.CancelCustomAnimation(self)); + if (buildComplete && spriteBody != null && !IsTraitDisabled) + spriteBody.PlayCustomAnimation(self, Info.Sequence, () => spriteBody.CancelCustomAnimation(self)); } void INotifyBuildComplete.BuildingComplete(Actor self) diff --git a/OpenRA.Mods.Common/Traits/Render/WithRepairAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithRepairAnimation.cs index 5805878016..f9de5e9e49 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithRepairAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithRepairAnimation.cs @@ -14,32 +14,29 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [Desc("Replaces the building animation when it repairs a unit.")] - public class WithRepairAnimationInfo : ITraitInfo, Requires + public class WithRepairAnimationInfo : ConditionalTraitInfo, Requires { [Desc("Sequence name to use")] [SequenceReference] public readonly string Sequence = "active"; - public readonly bool PauseOnLowPower = false; - - public object Create(ActorInitializer init) { return new WithRepairAnimation(init.Self, this); } + public override object Create(ActorInitializer init) { return new WithRepairAnimation(init.Self, this); } } - public class WithRepairAnimation : INotifyRepair, INotifyBuildComplete, INotifySold + public class WithRepairAnimation : ConditionalTrait, INotifyRepair, INotifyBuildComplete, INotifySold { - readonly WithRepairAnimationInfo info; readonly WithSpriteBody spriteBody; bool buildComplete; public WithRepairAnimation(Actor self, WithRepairAnimationInfo info) + : base(info) { - this.info = info; spriteBody = self.TraitOrDefault(); } void INotifyRepair.Repairing(Actor self, Actor target) { - if (buildComplete && spriteBody != null && !(info.PauseOnLowPower && self.IsDisabled())) - spriteBody.PlayCustomAnimation(self, info.Sequence, () => spriteBody.CancelCustomAnimation(self)); + if (buildComplete && spriteBody != null && !IsTraitDisabled) + spriteBody.PlayCustomAnimation(self, Info.Sequence, () => spriteBody.CancelCustomAnimation(self)); } void INotifyBuildComplete.BuildingComplete(Actor self) diff --git a/OpenRA.Mods.Common/Traits/Render/WithRepairOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithRepairOverlay.cs index a5dcb11902..a09d6061de 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithRepairOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithRepairOverlay.cs @@ -16,7 +16,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [Desc("Displays an overlay when the building is being repaired by the player.")] - public class WithRepairOverlayInfo : ITraitInfo, Requires, Requires + public class WithRepairOverlayInfo : PausableConditionalTraitInfo, Requires, Requires { [Desc("Sequence name to use")] [SequenceReference] public readonly string Sequence = "active"; @@ -30,30 +30,28 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Custom palette is a player palette BaseName")] public readonly bool IsPlayerPalette = false; - public readonly bool PauseOnLowPower = false; - - public object Create(ActorInitializer init) { return new WithRepairOverlay(init.Self, this); } + public override object Create(ActorInitializer init) { return new WithRepairOverlay(init.Self, this); } } - public class WithRepairOverlay : INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyRepair + public class WithRepairOverlay : PausableConditionalTrait, INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyRepair { readonly Animation overlay; bool buildComplete; bool visible; public WithRepairOverlay(Actor self, WithRepairOverlayInfo info) + : base(info) { var rs = self.Trait(); var body = self.Trait(); buildComplete = !self.Info.HasTraitInfo(); // always render instantly for units - overlay = new Animation(self.World, rs.GetImage(self), - () => info.PauseOnLowPower && self.IsDisabled()); + overlay = new Animation(self.World, rs.GetImage(self), () => IsTraitPaused); overlay.PlayThen(info.Sequence, () => visible = false); var anim = new AnimationWithOffset(overlay, () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), - () => !visible || !buildComplete, + () => IsTraitDisabled || !visible || !buildComplete, p => RenderUtils.ZOffsetFromCenter(self, p, 1)); rs.Add(anim, info.Palette, info.IsPlayerPalette); diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs index 15fbce2a67..052a1fb267 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs @@ -13,7 +13,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - public abstract class SupportPowerInfo : ConditionalTraitInfo + public abstract class SupportPowerInfo : PausableConditionalTraitInfo { [Desc("Measured in seconds.")] public readonly int ChargeTime = 0; @@ -76,7 +76,7 @@ namespace OpenRA.Mods.Common.Traits public SupportPowerInfo() { OrderName = GetType().Name + "Order"; } } - public class SupportPower : ConditionalTrait + public class SupportPower : PausableConditionalTrait { public readonly Actor Self; readonly SupportPowerInfo info; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs index 2e9567ee21..3b7e0dfe2b 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs @@ -179,11 +179,6 @@ namespace OpenRA.Mods.Common.Traits prereqsAvailable = available; } - static bool InstanceDisabled(SupportPower sp) - { - return sp.Self.IsDisabled(); - } - bool notifiedCharging; bool notifiedReady; public void Tick() @@ -192,7 +187,7 @@ namespace OpenRA.Mods.Common.Traits if (!instancesEnabled) RemainingTime = TotalTime; - Active = !Disabled && Instances.Any(i => !i.Self.IsDisabled()); + Active = !Disabled && Instances.Any(i => !i.IsTraitPaused); if (!Active) return; @@ -224,7 +219,7 @@ namespace OpenRA.Mods.Common.Traits if (!Ready) return; - var power = Instances.FirstOrDefault(); + var power = Instances.FirstOrDefault(i => !i.IsTraitPaused); if (power == null) return; @@ -236,7 +231,7 @@ namespace OpenRA.Mods.Common.Traits if (!Ready) return; - var power = Instances.Where(i => !InstanceDisabled(i)) + var power = Instances.Where(i => !i.IsTraitPaused) .MinByOrDefault(a => { if (a.Self.OccupiesSpace == null) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index e350aabe96..dcf4d5b603 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -601,6 +601,40 @@ namespace OpenRA.Mods.Common.UtilityCommands if (node.Key == "SoundFile" && parent.Key.StartsWith("AmbientSound", StringComparison.Ordinal)) RenameNodeKey(node, "SoundFiles"); + // PauseOnLowPower property has been replaced with PauseOnCondition/RequiresCondition + if (engineVersion < 20170501) + { + if (node.Key.StartsWith("WithRearmAnimation", StringComparison.Ordinal) || node.Key.StartsWith("WithRepairAnimation", StringComparison.Ordinal) + || node.Key.StartsWith("WithIdleAnimation", StringComparison.Ordinal)) + { + var pauseOnLowPowerNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "PauseOnLowPower"); + if (pauseOnLowPowerNode != null) + { + node.Value.Nodes.Remove(pauseOnLowPowerNode); + Console.WriteLine("PauseOnLowPower has been removed from {0}; use RequiresCondition instead.".F(node.Key)); + } + } + else if (node.Key.StartsWith("WithIdleOverlay", StringComparison.Ordinal) || node.Key.StartsWith("WithRepairOverlay", StringComparison.Ordinal)) + { + var pauseOnLowPowerNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "PauseOnLowPower"); + if (pauseOnLowPowerNode != null) + { + node.Value.Nodes.Remove(pauseOnLowPowerNode); + Console.WriteLine("PauseOnLowPower has been removed from {0}; use PauseOnCondition or RequiresCondition instead.".F(node.Key)); + } + } + else if (node.Key.StartsWith("AffectedByPowerOutage", StringComparison.Ordinal)) + { + Console.WriteLine("Actor {0} has AffectedByPowerOutage; use the Condition property to apply its effects.".F(node.Key)); + } + else if (node.Key.StartsWith("IonCannonPower", StringComparison.Ordinal) || node.Key.StartsWith("ProduceActorPower", StringComparison.Ordinal) + || node.Key.StartsWith("NukePower", StringComparison.Ordinal) || node.Key.StartsWith("AttackOrderPower", StringComparison.Ordinal) + || node.Key.StartsWith("GpsPower", StringComparison.Ordinal)) + { + Console.WriteLine("{0} requires PauseOnCondition for pausing.".F(node.Key)); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index fd536ac1bf..6a2db6ccb4 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -477,6 +477,7 @@ HQ: DetectCloaked: Range: 5c0 AirstrikePower: + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons Icon: airstrike ChargeTime: 240 @@ -573,6 +574,7 @@ EYE: DetectCloaked: Range: 5c0 IonCannonPower: + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons Icon: ioncannon Cursor: ioncannon @@ -625,6 +627,7 @@ TMPL: DetectCloaked: Range: 5c0 NukePower: + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons Icon: abomb Cursor: nuke diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 501a10b91c..ccd7cd1e40 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -533,7 +533,7 @@ outpost: mercenary: outpost.ordos WithIdleOverlay@DISH: Sequence: idle-dish - PauseOnLowPower: true + PauseOnCondition: disabled RequiresCondition: !severe-damaged GrantConditionOnDamageState@STOPDISH: Condition: severe-damaged @@ -960,6 +960,7 @@ palace: NukePower: Cursor: nuke Icon: deathhand + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons, ~palace.nuke ChargeTime: 300 Description: Death Hand @@ -979,6 +980,7 @@ palace: Description: Recruit Fremen LongDesc: Elite infantry unit armed with assault rifles and rockets\n Strong vs Infantry, Vehicles\n Weak vs Artillery\n Special Ability: Invisibility Icon: fremen + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons, ~palace.fremen Actors: fremen, fremen Type: Palace @@ -990,6 +992,7 @@ palace: Description: Recruit Saboteur LongDesc: Sneaky infantry, armed with explosives\n Strong vs Buildings\n Weak vs Everything\n Special Ability: destroy buildings Icon: saboteur + PauseOnCondition: disabled Prerequisites: ~techlevel.superweapons, ~palace.saboteur Actors: saboteur Type: Palace diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index bede68cdd8..da413cbb0c 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -933,3 +933,12 @@ Palette: disabled GrantConditionOnDisabled@IDISABLE: Condition: disabled + +^DisabledByPowerOutage: + AffectedByPowerOutage: + Condition: power-outage + InfiltrateForPowerOutage: + DisableOnCondition@POWER_OUTAGE: + RequiresCondition: power-outage + Power: + RequiresCondition: !power-outage diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 4908d2cb41..cbe2ae5d67 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -21,6 +21,7 @@ MSLO: RevealsShroud: Range: 5c0 NukePower: + PauseOnCondition: disabled Cursor: nuke Icon: abomb ChargeTime: 540 @@ -318,6 +319,7 @@ IRON: Bib: HasMinibib: Yes GrantExternalConditionPower@IRONCURTAIN: + PauseOnCondition: disabled Icon: invuln ChargeTime: 120 Description: Invulnerability @@ -375,6 +377,7 @@ PDOX: Prerequisite: pdox.germany ChronoshiftPower@chronoshift: OrderName: Chronoshift + PauseOnCondition: disabled Prerequisites: !pdox.germany Icon: chrono ChargeTime: 120 @@ -389,6 +392,7 @@ PDOX: DisplayRadarPing: True ChronoshiftPower@advancedchronoshift: OrderName: AdvancedChronoshift + PauseOnCondition: disabled Prerequisites: pdox.germany Icon: chrono ChargeTime: 120 @@ -811,6 +815,7 @@ ATEK: Range: 6c0 Bib: GpsPower: + PauseOnCondition: disabled Icon: gps OneShot: yes ChargeTime: 480 @@ -1304,6 +1309,7 @@ AFLD: POWR: Inherits: ^Building Inherits@IDISABLE: ^DisabledOverlay + Inherits@POWER_OUTAGE: ^DisabledByPowerOutage Buildable: Queue: Building BuildPaletteOrder: 10 @@ -1327,8 +1333,6 @@ POWR: Bib: Power: Amount: 100 - InfiltrateForPowerOutage: - AffectedByPowerOutage: Targetable: TargetTypes: Ground, Structure, C4, DetonateAttack, SpyInfiltrate ScalePowerWithHealth: @@ -1339,6 +1343,7 @@ POWR: APWR: Inherits: ^Building Inherits@IDISABLE: ^DisabledOverlay + Inherits@POWER_OUTAGE: ^DisabledByPowerOutage Buildable: Queue: Building BuildPaletteOrder: 110 @@ -1366,8 +1371,6 @@ APWR: Bib: Power: Amount: 200 - InfiltrateForPowerOutage: - AffectedByPowerOutage: Targetable: TargetTypes: Ground, Structure, C4, DetonateAttack, SpyInfiltrate ScalePowerWithHealth: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index e5db3a514e..041dd68759 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -972,3 +972,12 @@ Palette: disabled GrantConditionOnDisabled@IDISABLE: Condition: disabled + +^DisabledByPowerOutage: + AffectedByPowerOutage: + Condition: power-outage + InfiltrateForPowerOutage: + DisableOnCondition@POWER_OUTAGE: + RequiresCondition: power-outage + Power: + RequiresCondition: !power-outage diff --git a/mods/ts/rules/gdi-structures.yaml b/mods/ts/rules/gdi-structures.yaml index 082b8fc440..a9680e79cb 100644 --- a/mods/ts/rules/gdi-structures.yaml +++ b/mods/ts/rules/gdi-structures.yaml @@ -1,6 +1,7 @@ GAPOWR: Inherits: ^Building Inherits@IDISABLE: ^DisabledOverlay + Inherits@POWER_OUTAGE: ^DisabledByPowerOutage Buildable: Queue: Building BuildPaletteOrder: 10 @@ -23,15 +24,15 @@ GAPOWR: Range: 4c0 MaxHeightDelta: 3 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights WithIdleOverlay@PLUG: + PauseOnCondition: disabled Sequence: idle-plug Selectable: Bounds: 90, 48, 0, -6 Power: Amount: 100 - InfiltrateForPowerOutage: - AffectedByPowerOutage: PowerTooltip: Targetable: TargetTypes: Ground, C4, DetonateAttack, SpyInfiltrate @@ -41,10 +42,11 @@ GAPOWR: Conditions: powrup: powrup.a Power@pluga: - RequiresCondition: powrup.a + RequiresCondition: powrup.a && !power-outage Amount: 50 WithIdleOverlay@pluga: RequiresCondition: powrup.a + PauseOnCondition: disabled Sequence: idle-powrupa Pluggable@plugb: Offset: 1,1 @@ -52,9 +54,10 @@ GAPOWR: powrup: powrup.b WithIdleOverlay@plugb: RequiresCondition: powrup.b + PauseOnCondition: disabled Sequence: idle-powrupb Power@plugb: - RequiresCondition: powrup.b + RequiresCondition: powrup.b && !power-outage Amount: 50 ProvidesPrerequisite@buildingname: SelectionDecorations: @@ -271,8 +274,10 @@ GADEPT: WithIdleOverlay@CIRCUITS: Sequence: circuits WithRepairOverlay@CRANE: + PauseOnCondition: empdisable Sequence: crane WithRepairOverlay@PLATFORM: + RequiresCondition: !empdisable Sequence: platform WithDeathAnimation@BIB: DeathSequence: dead-ground @@ -290,6 +295,7 @@ GADEPT: GARADR: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Buildable: Queue: Building BuildPaletteOrder: 80 @@ -326,7 +332,7 @@ GARADR: MaxHeightDelta: 3 WithIdleOverlay@DISH: Sequence: idle-dish - PauseOnLowPower: yes + PauseOnCondition: disabled Targetable: TargetTypes: Ground, C4, SpyInfiltrate Power: @@ -334,11 +340,10 @@ GARADR: ProvidesPrerequisite@buildingname: SelectionDecorations: VisualBounds: 96, 118, 0, -38 - GrantConditionOnDisabled@IDISABLE: - Condition: disabled GATECH: Inherits: ^Building + Inherits@IDISABLE: ^DisabledOverlay Buildable: Queue: Building BuildPaletteOrder: 150 @@ -363,9 +368,11 @@ GATECH: Range: 4c0 MaxHeightDelta: 3 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: -150 + RequiresPower: ProvidesPrerequisite@buildingname: SelectionDecorations: VisualBounds: 110, 60, 3, -4 @@ -393,10 +400,13 @@ GAPLUG: PowerupSpeech: EnablePower PowerdownSpeech: DisablePower WithIdleOverlay@DISH: + PauseOnCondition: disabled Sequence: idle-dish WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights WithIdleOverlay@STRIP: + RequiresCondition: !disabled Sequence: idle-strip Health: HP: 1000 @@ -407,6 +417,7 @@ GAPLUG: MaxHeightDelta: 3 IonCannonPower: Cursor: ioncannon + PauseOnCondition: disabled || empdisable RequiresCondition: plug.ioncannona || plug.ioncannonb Icon: ioncannon Effect: explosion @@ -421,6 +432,7 @@ GAPLUG: DisplayRadarPing: True CameraActor: camera ProduceActorPower: + PauseOnCondition: disabled || empdisable RequiresCondition: plug.hunterseekera || plug.hunterseekerb Description: Hunter Seeker LongDesc: Releases a drone that will acquire and destroy an enemy target. @@ -451,9 +463,11 @@ GAPLUG: plug.hunterseeker: !plug.hunterseekerb && !plug.ioncannona && !plug.hunterseekera WithIdleOverlay@ioncannona: RequiresCondition: plug.ioncannona + PauseOnCondition: disabled Sequence: idle-ioncannona WithIdleOverlay@hunterseekera: RequiresCondition: plug.hunterseekera + PauseOnCondition: disabled Sequence: idle-hunterseekera Pluggable@plugb: Offset: 1,2 @@ -465,9 +479,11 @@ GAPLUG: plug.hunterseeker: !plug.hunterseekera && !plug.ioncannonb && !plug.hunterseekerb WithIdleOverlay@ioncannonb: RequiresCondition: plug.ioncannonb + PauseOnCondition: disabled Sequence: idle-ioncannonb WithIdleOverlay@hunterseekerb: RequiresCondition: plug.hunterseekerb + PauseOnCondition: disabled Sequence: idle-hunterseekerb ProvidesPrerequisite@buildingname: SelectionDecorations: diff --git a/mods/ts/rules/gdi-support.yaml b/mods/ts/rules/gdi-support.yaml index ba48c4a6ec..40b7f38308 100644 --- a/mods/ts/rules/gdi-support.yaml +++ b/mods/ts/rules/gdi-support.yaml @@ -116,6 +116,7 @@ GACTWR: WithMuzzleOverlay: RequiresCondition: tower.vulcan WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights LineBuildNode: Types: turret diff --git a/mods/ts/rules/nod-structures.yaml b/mods/ts/rules/nod-structures.yaml index afd1cb6ab6..6b484f44f3 100644 --- a/mods/ts/rules/nod-structures.yaml +++ b/mods/ts/rules/nod-structures.yaml @@ -1,6 +1,7 @@ NAPOWR: Inherits: ^Building Inherits@IDISABLE: ^DisabledOverlay + Inherits@POWER_OUTAGE: ^DisabledByPowerOutage Buildable: Queue: Building BuildPaletteOrder: 20 @@ -25,11 +26,10 @@ NAPOWR: Range: 4c0 MaxHeightDelta: 3 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: 100 - InfiltrateForPowerOutage: - AffectedByPowerOutage: Targetable: TargetTypes: Ground, C4, DetonateAttack, SpyInfiltrate ScalePowerWithHealth: @@ -40,6 +40,7 @@ NAPOWR: NAAPWR: Inherits: ^Building Inherits@IDISABLE: ^DisabledOverlay + Inherits@POWER_OUTAGE: ^DisabledByPowerOutage Buildable: Queue: Building BuildPaletteOrder: 120 @@ -64,11 +65,10 @@ NAAPWR: Range: 4c0 MaxHeightDelta: 3 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: 200 - InfiltrateForPowerOutage: - AffectedByPowerOutage: Targetable: TargetTypes: Ground, C4, DetonateAttack, SpyInfiltrate ScalePowerWithHealth: @@ -249,6 +249,7 @@ NAHPAD: NARADR: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Buildable: Queue: Building BuildPaletteOrder: 90 @@ -285,7 +286,7 @@ NARADR: MaxHeightDelta: 3 WithIdleOverlay@DISH: Sequence: idle-dish - PauseOnLowPower: true + PauseOnCondition: disabled Targetable: TargetTypes: Ground, C4, SpyInfiltrate Power: @@ -293,11 +294,10 @@ NARADR: ProvidesPrerequisite@buildingname: SelectionDecorations: VisualBounds: 96, 72, 0, -12 - GrantConditionOnDisabled@IDISABLE: - Condition: disabled NATECH: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Buildable: Queue: Building BuildPaletteOrder: 160 @@ -322,15 +322,18 @@ NATECH: Range: 4c0 MaxHeightDelta: 3 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: -150 + RequiresPower: ProvidesPrerequisite@buildingname: SelectionDecorations: VisualBounds: 86, 58, 0, -4 NATMPL: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Buildable: Queue: Building BuildPaletteOrder: 180 @@ -356,9 +359,12 @@ NATMPL: MaxHeightDelta: 3 Power: Amount: -200 + RequiresPower: WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights ProduceActorPower: + PauseOnCondition: empdisable Description: Hunter Seeker LongDesc: Releases a drone that will acquire and destroy an enemy target. Prerequisites: ~techlevel.superweapons diff --git a/mods/ts/rules/nod-support.yaml b/mods/ts/rules/nod-support.yaml index 6227aef6b8..9db185e407 100644 --- a/mods/ts/rules/nod-support.yaml +++ b/mods/ts/rules/nod-support.yaml @@ -43,6 +43,7 @@ NAGATE_B: NAPOST: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Buildable: Queue: Defense BuildPaletteOrder: 150 @@ -82,8 +83,6 @@ NAPOST: PowerupSpeech: EnablePower PowerdownSpeech: DisablePower RequiresPower: - GrantConditionOnDisabled: - Condition: disabled LineBuildSegmentExternalCondition: RequiresCondition: !disabled && !make-animation-playing Condition: active-posts @@ -224,6 +223,7 @@ NAOBEL: Palette: player IsPlayerPalette: true WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: -150 @@ -269,6 +269,7 @@ NASAM: NASTLH: Inherits: ^Building + Inherits@IDISABLED: ^DisabledOverlay Valued: Cost: 2500 Tooltip: @@ -290,7 +291,7 @@ NASTLH: MaxHeightDelta: 3 WithIdleOverlay@pulse: Sequence: pulse - PauseOnLowPower: true + RequiresCondition: !disabled WithRangeCircle: Range: 12c0 Type: cloakgenerator @@ -301,8 +302,6 @@ NASTLH: PowerupSpeech: EnablePower PowerdownSpeech: DisablePower IndicatorPalette: mouse - GrantConditionOnDisabled: - Condition: disabled ProximityExternalCondition: RequiresCondition: !disabled Condition: cloakgenerator @@ -342,6 +341,7 @@ NAMISL: RevealsShroud: Range: 4c0 WithIdleOverlay@LIGHTS: + RequiresCondition: !disabled Sequence: idle-lights Power: Amount: -50 @@ -353,6 +353,7 @@ NAMISL: ProvidesPrerequisite@buildingname: SupportPowerChargeBar: NukePower: + PauseOnCondition: disabled Cursor: nuke Icon: clustermissile ChargeTime: 540 diff --git a/mods/ts/rules/shared-support.yaml b/mods/ts/rules/shared-support.yaml index 5b6eb687ea..2d7ea414dc 100644 --- a/mods/ts/rules/shared-support.yaml +++ b/mods/ts/rules/shared-support.yaml @@ -45,6 +45,7 @@ NAPULS: ProvidesPrerequisite@gdi: ResetOnOwnerChange: true AttackOrderPower: + PauseOnCondition: empdisable || disabled Cursor: emp Icon: emp ChargeTime: 135