diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 20bde1192b..933a24b273 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -402,7 +402,6 @@ - @@ -836,6 +835,7 @@ + diff --git a/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs b/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs index 6b8f79c4a6..1808a103e4 100644 --- a/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs @@ -33,6 +33,11 @@ namespace OpenRA.Mods.Common.Orders return OrderInner(world, mi); } + protected virtual bool IsValidTrait(T t) + { + return Exts.IsTraitEnabled(t); + } + protected IEnumerable OrderInner(World world, MouseInput mi) { if (mi.Button == MouseButton.Left) @@ -40,7 +45,7 @@ namespace OpenRA.Mods.Common.Orders var underCursor = world.ScreenMap.ActorsAtMouse(mi) .Select(a => a.Actor) .FirstOrDefault(a => a.Owner == world.LocalPlayer && a.TraitsImplementing() - .Any(Exts.IsTraitEnabled)); + .Any(IsValidTrait)); if (underCursor == null) yield break; @@ -66,10 +71,15 @@ namespace OpenRA.Mods.Common.Orders public abstract string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi); } - public class PowerDownOrderGenerator : GlobalButtonOrderGenerator + public class PowerDownOrderGenerator : GlobalButtonOrderGenerator { public PowerDownOrderGenerator() : base("PowerDown") { } + protected override bool IsValidTrait(ToggleConditionOnOrder t) + { + return !t.IsTraitDisabled && !t.IsTraitPaused; + } + public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { mi.Button = MouseButton.Left; diff --git a/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs b/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs new file mode 100644 index 0000000000..0805014196 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs @@ -0,0 +1,112 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Mods.Common.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Toggles a condition on and off when a specified order type is received.")] + public class ToggleConditionOnOrderInfo : PausableConditionalTraitInfo + { + [FieldLoader.Require] + [GrantedConditionReference] + [Desc("Condition to grant.")] + public readonly string Condition = null; + + [FieldLoader.Require] + [Desc("Order name that toggles the condition.")] + public readonly string OrderName = null; + + public readonly string EnabledSound = null; + public readonly string EnabledSpeech = null; + + public readonly string DisabledSound = null; + public readonly string DisabledSpeech = null; + + public override object Create(ActorInitializer init) { return new ToggleConditionOnOrder(init.Self, this); } + } + + public class ToggleConditionOnOrder : PausableConditionalTrait, IResolveOrder + { + ConditionManager conditionManager; + int conditionToken = ConditionManager.InvalidConditionToken; + + // If the trait is paused this may be true with no condition granted + [Sync] bool enabled = false; + + public ToggleConditionOnOrder(Actor self, ToggleConditionOnOrderInfo info) + : base(info) { } + + protected override void Created(Actor self) + { + base.Created(self); + + conditionManager = self.TraitOrDefault(); + } + + void SetCondition(Actor self, bool granted) + { + if (conditionManager == null) + return; + + if (granted && conditionToken == ConditionManager.InvalidConditionToken) + { + conditionToken = conditionManager.GrantCondition(self, Info.Condition); + + if (Info.EnabledSound != null) + Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", Info.EnabledSound, self.Owner.Faction.InternalName); + + if (Info.EnabledSpeech != null) + Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.EnabledSpeech, self.Owner.Faction.InternalName); + } + else if (!granted && conditionToken != ConditionManager.InvalidConditionToken) + { + conditionToken = conditionManager.RevokeCondition(self, conditionToken); + + if (Info.DisabledSound != null) + Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", Info.DisabledSound, self.Owner.Faction.InternalName); + + if (Info.DisabledSpeech != null) + Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.DisabledSpeech, self.Owner.Faction.InternalName); + } + } + + void IResolveOrder.ResolveOrder(Actor self, Order order) + { + if (!IsTraitDisabled && !IsTraitPaused && order.OrderString == Info.OrderName) + { + enabled = !enabled; + SetCondition(self, enabled); + } + } + + protected override void TraitDisabled(Actor self) + { + // Disabling the trait resets the condition + enabled = false; + SetCondition(self, false); + } + + protected override void TraitPaused(Actor self) + { + // Pausing the trait removes the condition + // but does not reset the enabled value + SetCondition(self, false); + } + + protected override void TraitResumed(Actor self) + { + // Unpausing the trait restores the previous state + SetCondition(self, enabled); + } + } +} diff --git a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs deleted file mode 100644 index 1b52a7de88..0000000000 --- a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs +++ /dev/null @@ -1,125 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using OpenRA.Mods.Common.Effects; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - [Desc("The player can disable the power individually on this actor.")] - public class CanPowerDownInfo : ConditionalTraitInfo, Requires - { - [FieldLoader.Require] - [GrantedConditionReference] - [Desc("Condition to grant.")] - public readonly string PowerdownCondition = null; - - [Desc("Restore power when this trait is disabled.")] - public readonly bool CancelWhenDisabled = false; - - public readonly string PowerupSound = null; - public readonly string PowerdownSound = null; - - public readonly string PowerupSpeech = null; - public readonly string PowerdownSpeech = null; - - public override object Create(ActorInitializer init) { return new CanPowerDown(init.Self, this); } - } - - public class CanPowerDown : ConditionalTrait, IPowerModifier, IResolveOrder, INotifyOwnerChanged - { - [Sync] bool isPoweredDown = false; - PowerManager power; - - ConditionManager conditionManager; - int conditionToken = ConditionManager.InvalidConditionToken; - - public CanPowerDown(Actor self, CanPowerDownInfo info) - : base(info) - { - power = self.Owner.PlayerActor.Trait(); - } - - protected override void Created(Actor self) - { - base.Created(self); - - conditionManager = self.TraitOrDefault(); - } - - protected override void TraitEnabled(Actor self) - { - Update(self); - power.UpdateActor(self); - } - - void Update(Actor self) - { - if (conditionManager == null) - return; - - if (isPoweredDown && conditionToken == ConditionManager.InvalidConditionToken) - conditionToken = conditionManager.GrantCondition(self, Info.PowerdownCondition); - else if (!isPoweredDown && conditionToken != ConditionManager.InvalidConditionToken) - conditionToken = conditionManager.RevokeCondition(self, conditionToken); - } - - void IResolveOrder.ResolveOrder(Actor self, Order order) - { - if (!IsTraitDisabled && order.OrderString == "PowerDown") - { - isPoweredDown = !isPoweredDown; - - if (Info.PowerupSound != null && isPoweredDown) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", Info.PowerupSound, self.Owner.Faction.InternalName); - - if (Info.PowerdownSound != null && !isPoweredDown) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", Info.PowerdownSound, self.Owner.Faction.InternalName); - - if (Info.PowerupSpeech != null && isPoweredDown) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.PowerupSpeech, self.Owner.Faction.InternalName); - - if (Info.PowerdownSpeech != null && !isPoweredDown) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.PowerdownSpeech, self.Owner.Faction.InternalName); - - Update(self); - power.UpdateActor(self); - } - } - - int IPowerModifier.GetPowerModifier() - { - return !IsTraitDisabled && isPoweredDown ? 0 : 100; - } - - void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) - { - power = newOwner.PlayerActor.Trait(); - } - - protected override void TraitDisabled(Actor self) - { - if (!isPoweredDown || !Info.CancelWhenDisabled) - return; - - isPoweredDown = false; - - if (Info.PowerupSound != null) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sound", Info.PowerupSound, self.Owner.Faction.InternalName); - - if (Info.PowerupSpeech != null) - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.PowerupSpeech, self.Owner.Faction.InternalName); - - Update(self); - power.UpdateActor(self); - } - } -} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index a921f98ba5..7e7996a30c 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -25,6 +25,9 @@ namespace OpenRA.Mods.Common.UtilityCommands static void RenameNodeKey(MiniYamlNode node, string key) { + if (node == null) + return; + var parts = node.Key.Split('@'); node.Key = key; if (parts.Length > 1) @@ -1569,6 +1572,40 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // CanPowerDown was replaced with a more general trait for toggling a condition + if (engineVersion < 20171225) + { + var cpd = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("CanPowerDown", StringComparison.Ordinal)); + if (cpd != null) + { + RenameNodeKey(cpd, "ToggleConditionOnOrder"); + + RenameNodeKey(cpd.Value.Nodes.FirstOrDefault(n => n.Key == "PowerupSound"), "DisabledSound"); + RenameNodeKey(cpd.Value.Nodes.FirstOrDefault(n => n.Key == "PowerupSpeech"), "DisabledSpeech"); + RenameNodeKey(cpd.Value.Nodes.FirstOrDefault(n => n.Key == "PowerdownSound"), "EnabledSound"); + RenameNodeKey(cpd.Value.Nodes.FirstOrDefault(n => n.Key == "PowerdownSpeech"), "EnabledSpeech"); + cpd.Value.Nodes.Add(new MiniYamlNode("OrderName", "PowerDown")); + + var condition = cpd.Value.Nodes.FirstOrDefault(n => n.Key == "PowerdownCondition"); + if (condition != null) + RenameNodeKey(condition, "Condition"); + else + cpd.Value.Nodes.Add(new MiniYamlNode("Condition", "powerdown")); + + if (cpd.Value.Nodes.RemoveAll(n => n.Key == "CancelWhenDisabled") > 0) + { + Console.WriteLine("CancelWhenDisabled was removed when CanPowerDown was replaced by ToggleConditionOnOrder"); + Console.WriteLine("Use PauseOnCondition instead of RequiresCondition to replicate the behavior of 'false'."); + } + + node.Value.Nodes.Add(new MiniYamlNode("PowerMultiplier@POWERDOWN", new MiniYaml("", new List() + { + new MiniYamlNode("RequiresCondition", condition.Value.Value), + new MiniYamlNode("Modifier", "0") + }))); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index be2599458b..dfc0a26e14 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -474,13 +474,17 @@ GrantCondition@IDISABLE: RequiresCondition: lowpower || powerdown Condition: disabled - CanPowerDown: - PowerupSound: EnablePower - PowerdownSound: DisablePower - PowerdownCondition: powerdown + ToggleConditionOnOrder: + DisabledSound: EnablePower + EnabledSound: DisablePower + Condition: powerdown + OrderName: PowerDown WithDecoration@POWERDOWN: Image: poweroff Sequence: offline Palette: chrome RequiresCondition: powerdown ReferencePoint: Center + PowerMultiplier@POWERDOWN: + RequiresCondition: powerdown + Modifier: 0 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 9e8139b6c9..98bc2ac0fa 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -1100,16 +1100,20 @@ GrantCondition@IDISABLE: RequiresCondition: lowpower || powerdown Condition: disabled - CanPowerDown: - PowerupSound: EnablePower - PowerdownSound: DisablePower - PowerdownCondition: powerdown + ToggleConditionOnOrder: + DisabledSound: EnablePower + EnabledSound: DisablePower + Condition: powerdown + OrderName: PowerDown WithDecoration@POWERDOWN: Image: poweroff Sequence: offline Palette: chrome RequiresCondition: powerdown ReferencePoint: Center + PowerMultiplier@POWERDOWN: + RequiresCondition: powerdown + Modifier: 0 ^DisabledByPowerOutage: WithColoredOverlay@IDISABLE: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index db8ca06929..7c23f27114 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -1226,16 +1226,20 @@ GrantCondition@IDISABLE: RequiresCondition: powerdown Condition: disabled - CanPowerDown: - PowerupSpeech: EnablePower - PowerdownSpeech: DisablePower - PowerdownCondition: powerdown + ToggleConditionOnOrder: + DisabledSpeech: EnablePower + EnabledSpeech: DisablePower + Condition: powerdown + OrderName: PowerDown WithDecoration@POWERDOWN: Image: poweroff Sequence: offline Palette: mouse RequiresCondition: powerdown ReferencePoint: Center + PowerMultiplier@POWERDOWN: + RequiresCondition: powerdown + Modifier: 0 ^DisableOnLowPowerOrPowerDown: Inherits@LOWPOWER: ^DisableOnLowPower