From 47357addeb1f303d58333f568920808299d2e59d Mon Sep 17 00:00:00 2001 From: atlimit8 Date: Thu, 30 Oct 2014 03:09:45 -0500 Subject: [PATCH 1/2] Add Upgrade support to Power/CanPowerDown --- .../Traits/Power/CanPowerDown.cs | 13 +-- .../Traits/Power/Player/PowerManager.cs | 79 ++++++++----------- OpenRA.Mods.Common/Traits/Power/Power.cs | 19 +++-- 3 files changed, 53 insertions(+), 58 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs index 5843af5ddc..2df3a15c27 100644 --- a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs +++ b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs @@ -14,17 +14,18 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Power { [Desc("The player can disable the power individually on this actor.")] - public class CanPowerDownInfo : ITraitInfo, Requires + public class CanPowerDownInfo : UpgradableTraitInfo, ITraitInfo, Requires { - public object Create(ActorInitializer init) { return new CanPowerDown(init.self); } + public object Create(ActorInitializer init) { return new CanPowerDown(init.self, this); } } - public class CanPowerDown : IPowerModifier, IResolveOrder, IDisable, ISync + public class CanPowerDown : UpgradableTrait, IPowerModifier, IResolveOrder, IDisable { [Sync] bool disabled = false; readonly Power power; - public CanPowerDown(Actor self) + public CanPowerDown(Actor self, CanPowerDownInfo info) + : base(info) { power = self.Trait(); } @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Power public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "PowerDown") + if (!IsTraitDisabled && order.OrderString == "PowerDown") { disabled = !disabled; Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", disabled ? "EnablePower" : "DisablePower", self.Owner.Country.Race); @@ -46,7 +47,7 @@ namespace OpenRA.Mods.Common.Power public int GetPowerModifier() { - return disabled ? 0 : 100; + return !IsTraitDisabled && disabled ? 0 : 100; } } } diff --git a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs index f5a8783f8e..a93ae75e77 100644 --- a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs +++ b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs @@ -45,59 +45,38 @@ namespace OpenRA.Mods.Common.Power this.self = self; this.info = info; - self.World.ActorAdded += UpdateActor; - self.World.ActorRemoved += RemoveActor; - devMode = self.Trait(); wasHackEnabled = devMode.UnlimitedPower; } public void UpdateActor(Actor a) { - UpdateActors(new[] { a }); - } - - public void UpdateActors(IEnumerable actors) - { - foreach (var a in actors) - { - if (a.Owner != self.Owner) - return; - - var power = a.TraitOrDefault(); - if (power == null) - return; - - powerDrain[a] = power.GetCurrentPower(); - } - UpdateTotals(); - } - - void RemoveActor(Actor a) - { - if (a.Owner != self.Owner || !a.HasTrait()) + int old; + powerDrain.TryGetValue(a, out old); // old is 0 if a is not in powerDrain + var amount = a.TraitsImplementing().Where(t => !t.IsTraitDisabled).Aggregate(0, (v, p) => v + p.GetEnabledPower()); + powerDrain[a] = amount; + if (amount == old || devMode.UnlimitedPower) return; - - powerDrain.Remove(a); - UpdateTotals(); + if (old > 0) + totalProvided -= old; + else if (old < 0) + totalDrained += old; + if (amount > 0) + totalProvided += amount; + else if (amount < 0) + totalDrained -= amount; } - public void UpdateTotals() + public void RemoveActor(Actor a) { - totalProvided = 0; - totalDrained = 0; - - foreach (var kv in powerDrain) - { - var p = kv.Value; - if (p > 0) - totalProvided += p; - else - totalDrained -= p; - } - - if (devMode.UnlimitedPower) - totalProvided = 1000000; + int amount; + if (!powerDrain.TryGetValue(a, out amount)) + return; + if (amount > 0) + totalProvided -= amount; + else if (amount < 0) + totalDrained += amount; + powerDrain.Remove(a); } int nextPowerAdviceTime = 0; @@ -108,7 +87,16 @@ namespace OpenRA.Mods.Common.Power { if (wasHackEnabled != devMode.UnlimitedPower) { - UpdateTotals(); + totalProvided = 0; + totalDrained = 0; + + if (!devMode.UnlimitedPower) + foreach (var kv in powerDrain) + if (kv.Value > 0) + totalProvided += kv.Value; + else if (kv.Value < 0) + totalDrained -= kv.Value; + wasHackEnabled = devMode.UnlimitedPower; } @@ -150,7 +138,8 @@ namespace OpenRA.Mods.Common.Power .Select(tp => tp.Actor) .Where(a => !a.IsDead && a.IsInWorld && a.Owner == self.Owner); - UpdateActors(actors); + foreach (var a in actors) + UpdateActor(a); } } } diff --git a/OpenRA.Mods.Common/Traits/Power/Power.cs b/OpenRA.Mods.Common/Traits/Power/Power.cs index 2cc802dbc0..cbac173b1c 100644 --- a/OpenRA.Mods.Common/Traits/Power/Power.cs +++ b/OpenRA.Mods.Common/Traits/Power/Power.cs @@ -14,7 +14,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Power { - public class PowerInfo : ITraitInfo + public class PowerInfo : UpgradableTraitInfo, ITraitInfo { [Desc("If negative, it will drain power. If positive, it will provide power.")] public readonly int Amount = 0; @@ -22,28 +22,33 @@ namespace OpenRA.Mods.Common.Power public object Create(ActorInitializer init) { return new Power(init.self, this); } } - public class Power : INotifyOwnerChanged + public class Power : UpgradableTrait, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyOwnerChanged { - readonly PowerInfo info; readonly Lazy powerModifiers; public PowerManager PlayerPower { get; private set; } - public int GetCurrentPower() + public int GetEnabledPower() { - return Util.ApplyPercentageModifiers(info.Amount, powerModifiers.Value.Select(m => m.GetPowerModifier())); + return Util.ApplyPercentageModifiers(Info.Amount, powerModifiers.Value.Select(m => m.GetPowerModifier())); } public Power(Actor self, PowerInfo info) + : base(info) { - this.info = info; PlayerPower = self.Owner.PlayerActor.Trait(); powerModifiers = Exts.Lazy(() => self.TraitsImplementing().ToArray()); } - + + protected override void UpgradeEnabled(Actor self) { PlayerPower.UpdateActor(self); } + protected override void UpgradeDisabled(Actor self) { PlayerPower.UpdateActor(self); } + public void AddedToWorld(Actor self) { PlayerPower.UpdateActor(self); } + public void RemovedFromWorld(Actor self) { PlayerPower.RemoveActor(self); } public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { + PlayerPower.RemoveActor(self); PlayerPower = newOwner.PlayerActor.Trait(); + PlayerPower.UpdateActor(self); } } } From 92779cc496c2c07d695bfcc0d6b15c12e4094a48 Mon Sep 17 00:00:00 2001 From: atlimit8 Date: Sat, 29 Nov 2014 15:56:25 -0600 Subject: [PATCH 2/2] Support multiple Power traits --- .../Effects/PowerdownIndicator.cs | 4 ++- .../Scripting/Properties/PowerProperties.cs | 8 +++--- .../Traits/Power/CanPowerDown.cs | 25 ++++++++++++++++--- .../Traits/Power/ScalePowerWithHealth.cs | 11 ++++---- OpenRA.Mods.D2k/Widgets/BuildPaletteWidget.cs | 9 ++++--- OpenRA.Mods.RA/AI/BaseBuilder.cs | 12 ++++----- .../Widgets/Logic/ProductionTooltipLogic.cs | 3 +-- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/OpenRA.Mods.Common/Effects/PowerdownIndicator.cs b/OpenRA.Mods.Common/Effects/PowerdownIndicator.cs index fa7e3ed86b..96da3461ab 100644 --- a/OpenRA.Mods.Common/Effects/PowerdownIndicator.cs +++ b/OpenRA.Mods.Common/Effects/PowerdownIndicator.cs @@ -19,10 +19,12 @@ namespace OpenRA.Mods.Common.Effects { readonly Actor a; readonly Animation anim; + readonly CanPowerDown canPowerDown; public PowerdownIndicator(Actor a) { this.a = a; + canPowerDown = a.Trait(); anim = new Animation(a.World, "poweroff"); anim.PlayRepeating("offline"); @@ -30,7 +32,7 @@ namespace OpenRA.Mods.Common.Effects public void Tick(World world) { - if (!a.IsInWorld || a.IsDead || !a.Trait().Disabled) + if (!a.IsInWorld || a.IsDead || !canPowerDown.Disabled) world.AddFrameEndTask(w => w.Remove(this)); anim.Tick(); diff --git a/OpenRA.Mods.Common/Scripting/Properties/PowerProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/PowerProperties.cs index e1eff38431..fe85ed8233 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/PowerProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/PowerProperties.cs @@ -9,6 +9,8 @@ #endregion using System; +using System.Collections.Generic; +using System.Linq; using Eluant; using OpenRA.Mods.Common.Power; using OpenRA.Scripting; @@ -56,18 +58,18 @@ namespace OpenRA.Mods.Common.Scripting [ScriptPropertyGroup("Power")] public class ActorPowerProperties : ScriptActorProperties, Requires { - readonly PowerInfo pi; + readonly IEnumerable power; public ActorPowerProperties(ScriptContext context, Actor self) : base(context, self) { - pi = self.Info.Traits.GetOrDefault(); + power = self.TraitsImplementing(); } [Desc("Returns the power drained/provided by this actor.")] public int Power { - get { return pi.Amount; } + get { return power.Sum(p => p.GetEnabledPower()); } } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs index 2df3a15c27..5225755d55 100644 --- a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs +++ b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs @@ -16,18 +16,21 @@ namespace OpenRA.Mods.Common.Power [Desc("The player can disable the power individually on this actor.")] public class CanPowerDownInfo : UpgradableTraitInfo, ITraitInfo, Requires { + [Desc("Restore power when this trait is disabled.")] + public readonly bool CancelWhenDisabled = false; + public object Create(ActorInitializer init) { return new CanPowerDown(init.self, this); } } - public class CanPowerDown : UpgradableTrait, IPowerModifier, IResolveOrder, IDisable + public class CanPowerDown : UpgradableTrait, IPowerModifier, IResolveOrder, IDisable, INotifyOwnerChanged { [Sync] bool disabled = false; - readonly Power power; + PowerManager power; public CanPowerDown(Actor self, CanPowerDownInfo info) : base(info) { - power = self.Trait(); + power = self.Owner.PlayerActor.Trait(); } public bool Disabled { get { return disabled; } } @@ -38,7 +41,7 @@ namespace OpenRA.Mods.Common.Power { disabled = !disabled; Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", disabled ? "EnablePower" : "DisablePower", self.Owner.Country.Race); - power.PlayerPower.UpdateActor(self); + power.UpdateActor(self); if (disabled) self.World.AddFrameEndTask(w => w.Add(new PowerdownIndicator(self))); @@ -49,5 +52,19 @@ namespace OpenRA.Mods.Common.Power { return !IsTraitDisabled && disabled ? 0 : 100; } + + public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) + { + power = newOwner.PlayerActor.Trait(); + } + + protected override void UpgradeDisabled(Actor self) + { + if (!disabled || !Info.CancelWhenDisabled) + return; + disabled = false; + Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "EnablePower", self.Owner.Country.Race); + power.UpdateActor(self); + } } } diff --git a/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs b/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs index f44d477c6e..6e42759bc9 100644 --- a/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs +++ b/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs @@ -18,14 +18,14 @@ namespace OpenRA.Mods.Common.Power public object Create(ActorInitializer init) { return new ScalePowerWithHealth(init.self); } } - public class ScalePowerWithHealth : IPowerModifier, INotifyDamage + public class ScalePowerWithHealth : IPowerModifier, INotifyDamage, INotifyOwnerChanged { - readonly Power power; readonly Health health; + PowerManager power; public ScalePowerWithHealth(Actor self) { - power = self.Trait(); + power = self.Owner.PlayerActor.Trait(); health = self.Trait(); } @@ -34,9 +34,10 @@ namespace OpenRA.Mods.Common.Power return 100 * health.HP / health.MaxHP; } - public void Damaged(Actor self, AttackInfo e) + public void Damaged(Actor self, AttackInfo e) { power.UpdateActor(self); } + public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { - power.PlayerPower.UpdateActor(self); + power = newOwner.PlayerActor.Trait(); } } } diff --git a/OpenRA.Mods.D2k/Widgets/BuildPaletteWidget.cs b/OpenRA.Mods.D2k/Widgets/BuildPaletteWidget.cs index 2474d8a054..6172d50700 100644 --- a/OpenRA.Mods.D2k/Widgets/BuildPaletteWidget.cs +++ b/OpenRA.Mods.D2k/Widgets/BuildPaletteWidget.cs @@ -508,10 +508,11 @@ namespace OpenRA.Mods.D2k.Widgets * ((lowpower) ? CurrentQueue.Info.LowPowerSlowdown : 1); DrawRightAligned(WidgetUtils.FormatTime(time), pos + new int2(-5, 35), lowpower ? Color.Red : Color.White); - var pi = info.Traits.GetOrDefault(); - if (pi != null) - DrawRightAligned("{1}{0}".F(pi.Amount, pi.Amount > 0 ? "+" : ""), pos + new int2(-5, 20), - ((power.PowerProvided - power.PowerDrained) >= -pi.Amount || pi.Amount > 0) ? Color.White : Color.Red); + var pis = info.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1); + var amount = pis.Sum(i => i.Amount); + if (pis != null) + DrawRightAligned("{1}{0}".F(amount, amount > 0 ? "+" : ""), pos + new int2(-5, 20), + ((power.PowerProvided - power.PowerDrained) >= -amount || amount > 0) ? Color.White : Color.Red); p += new int2(5, 35); if (!canBuildThis) diff --git a/OpenRA.Mods.RA/AI/BaseBuilder.cs b/OpenRA.Mods.RA/AI/BaseBuilder.cs index 42f84c957c..bfca998600 100644 --- a/OpenRA.Mods.RA/AI/BaseBuilder.cs +++ b/OpenRA.Mods.RA/AI/BaseBuilder.cs @@ -140,8 +140,8 @@ namespace OpenRA.Mods.RA.AI // First priority is to get out of a low power situation if (playerPower.ExcessPower < 0) { - var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.Get().Amount); - if (power != null && power.Traits.Get().Amount > 0) + var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); + if (power != null && power.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) { // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage if (playerPower.PowerOutageRemainingTicks <= 0) @@ -204,12 +204,12 @@ namespace OpenRA.Mods.RA.AI // Will this put us into low power? var actor = world.Map.Rules.Actors[frac.Key]; - var pi = actor.Traits.GetOrDefault(); - if (playerPower.ExcessPower < 0 || (pi != null && playerPower.ExcessPower < pi.Amount)) + var pis = actor.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1); + if (playerPower.ExcessPower < 0 || playerPower.ExcessPower < pis.Sum(pi => pi.Amount)) { // Try building a power plant instead - var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.Get().Amount); - if (power != null && power.Traits.Get().Amount > 0) + var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount)); + if (power != null && power.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) { // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage if (playerPower.PowerOutageRemainingTicks > 0) diff --git a/OpenRA.Mods.RA/Widgets/Logic/ProductionTooltipLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ProductionTooltipLogic.cs index 07b5db9eec..98dc5da00b 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ProductionTooltipLogic.cs @@ -58,7 +58,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic var tooltip = actor.Traits.Get(); var buildable = actor.Traits.Get(); var cost = actor.Traits.Get().Cost; - var pi = actor.Traits.GetOrDefault(); nameLabel.GetText = () => tooltip.Name; @@ -74,7 +73,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : ""; requiresLabel.GetText = () => requiresString; - var power = pi != null ? pi.Amount : 0; + var power = actor.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(i => i.Amount); var powerString = power.ToString(); powerLabel.GetText = () => powerString; powerLabel.GetColor = () => ((pm.PowerProvided - pm.PowerDrained) >= -power || power > 0)