diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 0faf5afebb..17b7b4da62 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -15,6 +15,7 @@ using System.Globalization; using System.Linq; using System.Reflection; using OpenRA.Support; +using OpenRA.Traits; namespace OpenRA { @@ -408,6 +409,16 @@ namespace OpenRA { return int.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i); } + + public static bool IsTraitEnabled(this object trait) + { + return trait as IDisabledTrait == null || !(trait as IDisabledTrait).IsTraitDisabled; + } + + public static bool IsTraitEnabled(T t) + { + return IsTraitEnabled(t as object); + } } public static class Enum diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 54cb0fef04..030de1e925 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -104,12 +104,6 @@ namespace OpenRA.Traits public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); } public interface IDisableMove { bool MoveDisabled(Actor self); } - public interface IUpgradable - { - bool AcceptsUpgrade(string type); - void UpgradeAvailable(Actor self, string type, bool available); - } - public interface ISeedableResource { void Seed(Actor self); } public interface IDemolishableInfo { bool IsValidTarget(ActorInfo actorInfo, Actor saboteur); } @@ -140,6 +134,7 @@ namespace OpenRA.Traits bool IsOwnerRowVisible { get; } } + public interface IDisabledTrait { bool IsTraitDisabled { get; } } public interface IDisable { bool Disabled { get; } } public interface IExplodeModifier { bool ShouldExplode(Actor self); } public interface IHuskModifier { string HuskActor(Actor self); } diff --git a/OpenRA.Mods.Common/CommonTraitsInterfaces.cs b/OpenRA.Mods.Common/CommonTraitsInterfaces.cs index 5dce57d896..d1fb1b6395 100644 --- a/OpenRA.Mods.Common/CommonTraitsInterfaces.cs +++ b/OpenRA.Mods.Common/CommonTraitsInterfaces.cs @@ -21,4 +21,11 @@ namespace OpenRA.Mods.Common public interface INotifyChat { bool OnChat(string from, string message); } public interface IRenderActorPreviewInfo { IEnumerable RenderPreview (ActorPreviewInitializer init); } + + public interface IUpgradable + { + IEnumerable UpgradeTypes { get; } + bool AcceptsUpgradeLevel(Actor self, string type, int level); + void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel); + } } diff --git a/OpenRA.Mods.RA/TimedUpgradeBar.cs b/OpenRA.Mods.Common/Modifiers/TimedUpgradeBar.cs similarity index 97% rename from OpenRA.Mods.RA/TimedUpgradeBar.cs rename to OpenRA.Mods.Common/Modifiers/TimedUpgradeBar.cs index b4f2cc56d6..6e14893de8 100644 --- a/OpenRA.Mods.RA/TimedUpgradeBar.cs +++ b/OpenRA.Mods.Common/Modifiers/TimedUpgradeBar.cs @@ -13,7 +13,7 @@ using System.Drawing; using System.Linq; using OpenRA.Traits; -namespace OpenRA.Mods.RA +namespace OpenRA.Mods.Common { [Desc("Visualizes the remaining time for an upgrade.")] class TimedUpgradeBarInfo : ITraitInfo, Requires diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 354671b53c..94f584421f 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -80,6 +80,7 @@ + @@ -136,6 +137,8 @@ + + diff --git a/OpenRA.Mods.Common/Traits/Modifiers/UpgradeOverlay.cs b/OpenRA.Mods.Common/Traits/Modifiers/UpgradeOverlay.cs index abefe185b8..c0c2d429e7 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/UpgradeOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/UpgradeOverlay.cs @@ -15,26 +15,18 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Display a colored overlay when a timed upgrade is active.")] - public class UpgradeOverlayInfo : ITraitInfo + public class UpgradeOverlayInfo : UpgradableTraitInfo, ITraitInfo { - [Desc("Upgrade that is required before this overlay is rendered")] - public readonly string RequiresUpgrade = null; - [Desc("Palette to use when rendering the overlay")] public readonly string Palette = "invuln"; public object Create(ActorInitializer init) { return new UpgradeOverlay(this); } } - public class UpgradeOverlay : IRenderModifier, IUpgradable + public class UpgradeOverlay : UpgradableTrait, IRenderModifier { - readonly UpgradeOverlayInfo info; - bool enabled; - public UpgradeOverlay(UpgradeOverlayInfo info) - { - this.info = info; - } + : base (info) { } public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { @@ -42,22 +34,11 @@ namespace OpenRA.Mods.Common.Traits { yield return a; - if (enabled && !a.IsDecoration) - yield return a.WithPalette(wr.Palette(info.Palette)) + if (!IsTraitDisabled && !a.IsDecoration) + yield return a.WithPalette(wr.Palette(Info.Palette)) .WithZOffset(a.ZOffset + 1) .AsDecoration(); } } - - public bool AcceptsUpgrade(string type) - { - return type == info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == info.RequiresUpgrade) - enabled = available; - } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Upgrades/UpgradableTrait.cs b/OpenRA.Mods.Common/Upgrades/UpgradableTrait.cs new file mode 100644 index 0000000000..1e10ce2e58 --- /dev/null +++ b/OpenRA.Mods.Common/Upgrades/UpgradableTrait.cs @@ -0,0 +1,86 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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. For more information, + * see COPYING. + */ +#endregion +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common +{ + /// Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.) + public abstract class UpgradableTraitInfo + { + [Desc("The upgrade types which can enable or disable this trait.")] + public readonly string[] UpgradeTypes = { }; + + [Desc("The minimum upgrade level at which this trait is enabled.", "Defaults to 0 (enabled by default).")] + public readonly int UpgradeMinEnabledLevel = 0; + + [Desc("The maximum upgrade level at which the trait is enabled.", + "Defaults to UpgradeMaxAcceptedLevel (enabled for all levels greater than UpgradeMinEnabledLevel).", + "Set this to a value smaller than UpgradeMaxAcceptedLevel to disable the trait at higher levels.", + "Use UpgradeMaxAcceptedLevel: 2 (1 more) to be able to extend upgrade time.")] + public readonly int UpgradeMaxEnabledLevel = int.MaxValue; + + [Desc("The maximum upgrade level that this trait will accept.")] + public readonly int UpgradeMaxAcceptedLevel = 1; + } + + /// + /// Abstract base for enabling and disabling trait using upgrades. + /// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor. + /// Note that EnabledByUpgrade is not called at creation even if this starts as enabled. + /// , + public abstract class UpgradableTrait : IUpgradable, IDisabledTrait, ISync where InfoType : UpgradableTraitInfo + { + public readonly InfoType Info; + public IEnumerable UpgradeTypes { get { return Info.UpgradeTypes; } } + [Sync] public bool IsTraitDisabled { get; private set; } + + public UpgradableTrait(InfoType info) + { + Info = info; + IsTraitDisabled = info.UpgradeTypes != null && info.UpgradeTypes.Length > 0 && info.UpgradeMinEnabledLevel > 0; + } + + public bool AcceptsUpgradeLevel(Actor self, string type, int level) + { + return level > 0 && level <= Info.UpgradeMaxAcceptedLevel; + } + + public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel) + { + if (!Info.UpgradeTypes.Contains(type)) + return; + + // Restrict the levels to the allowed range + oldLevel = oldLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); + newLevel = newLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); + if (oldLevel == newLevel) + return; + + var wasDisabled = IsTraitDisabled; + IsTraitDisabled = newLevel < Info.UpgradeMinEnabledLevel || newLevel > Info.UpgradeMaxEnabledLevel; + UpgradeLevelChanged(self, oldLevel, newLevel); + + if (IsTraitDisabled != wasDisabled) + { + if (wasDisabled) + UpgradeEnabled(self); + else + UpgradeDisabled(self); + } + } + + // Subclasses can add upgrade support by querying IsTraitDisabled and/or overriding these methods. + protected virtual void UpgradeLevelChanged(Actor self, int oldLevel, int newLevel) { } + protected virtual void UpgradeEnabled(Actor self) { } + protected virtual void UpgradeDisabled(Actor self) { } + } +} diff --git a/OpenRA.Mods.Common/Upgrades/UpgradeManager.cs b/OpenRA.Mods.Common/Upgrades/UpgradeManager.cs new file mode 100644 index 0000000000..ebf257eab1 --- /dev/null +++ b/OpenRA.Mods.Common/Upgrades/UpgradeManager.cs @@ -0,0 +1,160 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common +{ + public class UpgradeManagerInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new UpgradeManager(init); } + } + + public class UpgradeManager : ITick + { + class TimedUpgrade + { + public readonly string Upgrade; + public readonly int Duration; + public int Remaining; + + public TimedUpgrade(string upgrade, int duration) + { + Upgrade = upgrade; + Duration = duration; + Remaining = duration; + } + + public void Tick() { Remaining--; } + } + + class UpgradeState + { + public readonly List Traits = new List(); + public readonly List Sources = new List(); + public readonly List> Watchers = new List>(); + } + + readonly List timedUpgrades = new List(); + readonly Lazy> upgrades; + readonly Dictionary levels = new Dictionary(); + + public UpgradeManager(ActorInitializer init) + { + upgrades = Exts.Lazy(() => + { + var ret = new Dictionary(); + foreach (var up in init.self.TraitsImplementing()) + foreach (var t in up.UpgradeTypes) + ret.GetOrAdd(t).Traits.Add(up); + + return ret; + }); + } + + public void GrantTimedUpgrade(Actor self, string upgrade, int duration) + { + var timed = timedUpgrades.FirstOrDefault(u => u.Upgrade == upgrade); + if (timed == null) + { + timed = new TimedUpgrade(upgrade, duration); + timedUpgrades.Add(timed); + GrantUpgrade(self, upgrade, timed); + } + else + timed.Remaining = Math.Max(duration, timed.Remaining); + } + + // Different upgradeable traits may define (a) different level ranges for the same upgrade type, + // and (b) multiple upgrade types for the same trait. The unrestricted level for each trait is + // tracked independently so that we can can correctly revoke levels without adding the burden of + // tracking both the overall (unclamped) and effective (clamped) levels on each individual trait. + void NotifyUpgradeLevelChanged(IEnumerable traits, Actor self, string upgrade, int levelAdjust) + { + foreach (var up in traits) + { + var oldLevel = levels.GetOrAdd(up); + var newLevel = levels[up] = oldLevel + levelAdjust; + + // This will internally clamp the levels to its own restricted range + up.UpgradeLevelChanged(self, upgrade, oldLevel, newLevel); + } + } + + int GetOverallLevel(IUpgradable upgradable) + { + int level; + return levels.TryGetValue(upgradable, out level) ? level : 0; + } + + public void GrantUpgrade(Actor self, string upgrade, object source) + { + UpgradeState s; + if (!upgrades.Value.TryGetValue(upgrade, out s)) + return; + + // Track the upgrade source so that the upgrade can be removed without conflicts + s.Sources.Add(source); + + NotifyUpgradeLevelChanged(s.Traits, self, upgrade, 1); + } + + public void RevokeUpgrade(Actor self, string upgrade, object source) + { + UpgradeState s; + if (!upgrades.Value.TryGetValue(upgrade, out s)) + return; + + if (!s.Sources.Remove(source)) + throw new InvalidOperationException("Object <{0}> revoked more levels of upgrade {1} than it granted for {2}.".F(source, upgrade, self)); + + NotifyUpgradeLevelChanged(s.Traits, self, upgrade, -1); + } + + public bool AcceptsUpgrade(Actor self, string upgrade) + { + UpgradeState s; + if (!upgrades.Value.TryGetValue(upgrade, out s)) + return false; + + return s.Traits.Any(up => up.AcceptsUpgradeLevel(self, upgrade, GetOverallLevel(up) + 1)); + } + + public void RegisterWatcher(string upgrade, Action action) + { + UpgradeState s; + if (!upgrades.Value.TryGetValue(upgrade, out s)) + return; + + s.Watchers.Add(action); + } + + public void Tick(Actor self) + { + foreach (var u in timedUpgrades) + { + u.Tick(); + if (u.Remaining <= 0) + RevokeUpgrade(self, u.Upgrade, u); + + foreach (var a in upgrades.Value[u.Upgrade].Watchers) + a(u.Duration, u.Remaining); + } + + timedUpgrades.RemoveAll(u => u.Remaining <= 0); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 2a9b7ec315..f8061685e4 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -664,6 +664,33 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20141121) + { + if (depth == 1) + { + if (node.Value.Nodes.Exists(n => n.Key == "RestrictedByUpgrade")) + { + node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxEnabledLevel", "0")); + node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxAcceptedLevel", "1")); + } + else if (node.Value.Nodes.Exists(n => n.Key == "RequiresUpgrade")) + node.Value.Nodes.Add(new MiniYamlNode("UpgradeMinEnabledLevel", "1")); + + if (node.Key.StartsWith("DisableUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes")) + node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "disable")); + + if (node.Key.StartsWith("InvulnerabilityUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes")) + node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "invulnerability")); + } + else if (depth == 2) + { + if (node.Key == "RequiresUpgrade" || node.Key == "RestrictedByUpgrade") + node.Key = "UpgradeTypes"; + else if (node.Key == "-RequiresUpgrade" || node.Key == "-RestrictedByUpgrade") + node.Key = "-UpgradeTypes"; + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/OpenRA.Mods.RA/Armament.cs b/OpenRA.Mods.RA/Armament.cs index 9f37a30d61..2ab5798dc5 100644 --- a/OpenRA.Mods.RA/Armament.cs +++ b/OpenRA.Mods.RA/Armament.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using OpenRA.GameRules; +using OpenRA.Mods.Common; using OpenRA.Primitives; using OpenRA.Traits; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.RA } [Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")] - public class ArmamentInfo : ITraitInfo, Requires + public class ArmamentInfo : UpgradableTraitInfo, ITraitInfo, Requires { public readonly string Name = "primary"; @@ -53,18 +54,11 @@ namespace OpenRA.Mods.RA [Desc("Use multiple muzzle images if non-zero")] public readonly int MuzzleSplitFacings = 0; - [Desc("Enable only if this upgrade is enabled.")] - public readonly string RequiresUpgrade = null; - - [Desc("Disable if this upgrade is enabled.")] - public readonly string RestrictedByUpgrade = null; - public object Create(ActorInitializer init) { return new Armament(init.self, this); } } - public class Armament : ITick, IExplodeModifier, IUpgradable + public class Armament : UpgradableTrait, ITick, IExplodeModifier { - public readonly ArmamentInfo Info; public readonly WeaponInfo Weapon; public readonly Barrel[] Barrels; @@ -78,13 +72,10 @@ namespace OpenRA.Mods.RA public int FireDelay { get; private set; } public int Burst { get; private set; } - bool requiresUpgrade; - bool restrictedByUpgrade; - public Armament(Actor self, ArmamentInfo info) + : base(info) { this.self = self; - Info = info; // We can't resolve these until runtime Turret = Exts.Lazy(() => self.TraitsImplementing().FirstOrDefault(t => t.Name == info.Turret)); @@ -108,27 +99,11 @@ namespace OpenRA.Mods.RA barrels.Add(new Barrel { Offset = WVec.Zero, Yaw = WAngle.Zero }); Barrels = barrels.ToArray(); - - // Disable if an upgrade is required - requiresUpgrade = info.RequiresUpgrade != null; - } - - public bool AcceptsUpgrade(string type) - { - return type == Info.RequiresUpgrade || type == Info.RestrictedByUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == Info.RequiresUpgrade) - requiresUpgrade = !available; - else if (type == Info.RestrictedByUpgrade) - restrictedByUpgrade = available; } public void Tick(Actor self) { - if (requiresUpgrade || restrictedByUpgrade) + if (IsTraitDisabled) return; if (FireDelay > 0) @@ -226,7 +201,7 @@ namespace OpenRA.Mods.RA return barrel; } - public bool IsReloading { get { return FireDelay > 0 || requiresUpgrade || restrictedByUpgrade; } } + public bool IsReloading { get { return FireDelay > 0 || IsTraitDisabled; } } public bool ShouldExplode(Actor self) { return !IsReloading; } public WVec MuzzleOffset(Actor self, Barrel b) diff --git a/OpenRA.Mods.RA/Cargo.cs b/OpenRA.Mods.RA/Cargo.cs index de7167f0ae..a503d7fa26 100644 --- a/OpenRA.Mods.RA/Cargo.cs +++ b/OpenRA.Mods.RA/Cargo.cs @@ -15,6 +15,7 @@ using OpenRA.Traits; using OpenRA.Primitives; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Traits; +using OpenRA.Mods.Common; using OpenRA.Mods.Common.Orders; namespace OpenRA.Mods.RA diff --git a/OpenRA.Mods.RA/Cloak.cs b/OpenRA.Mods.RA/Cloak.cs index ef673e0659..186471bdda 100644 --- a/OpenRA.Mods.RA/Cloak.cs +++ b/OpenRA.Mods.RA/Cloak.cs @@ -13,12 +13,13 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using OpenRA.Graphics; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA { [Desc("This unit can cloak and uncloak in specific situations.")] - public class CloakInfo : ITraitInfo + public class CloakInfo : UpgradableTraitInfo, ITraitInfo { [Desc("Measured in game ticks.")] public readonly int InitialDelay = 10; @@ -30,9 +31,6 @@ namespace OpenRA.Mods.RA public readonly bool UncloakOnMove = false; public readonly bool UncloakOnUnload = true; - [Desc("Enable only if this upgrade is enabled.")] - public readonly string RequiresUpgrade = null; - public readonly string CloakSound = null; public readonly string UncloakSound = null; public readonly string Palette = "cloak"; @@ -42,44 +40,26 @@ namespace OpenRA.Mods.RA public object Create(ActorInitializer init) { return new Cloak(init.self, this); } } - public class Cloak : IUpgradable, IRenderModifier, INotifyDamageStateChanged, INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier, ISync + public class Cloak : UpgradableTrait, IRenderModifier, INotifyDamageStateChanged, INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier { [Sync] int remainingTime; [Sync] bool damageDisabled; - [Sync] bool disabled; Actor self; - public readonly CloakInfo Info; CPos? lastPos; public Cloak(Actor self, CloakInfo info) + : base (info) { this.self = self; - Info = info; remainingTime = info.InitialDelay; - - // Disable if an upgrade is required - disabled = info.RequiresUpgrade != null; } - public bool AcceptsUpgrade(string type) + protected override void UpgradeDisabled(Actor self) { - return type == Info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == Info.RequiresUpgrade) - { - disabled = !available; - - if (disabled) - { - Uncloak(); - remainingTime = Info.InitialDelay; - } - } + Uncloak(); + remainingTime = Info.InitialDelay; } public void Uncloak() { Uncloak(Info.CloakDelay); } @@ -94,7 +74,7 @@ namespace OpenRA.Mods.RA public void Attacking(Actor self, Target target, Armament a, Barrel barrel) { if (Info.UncloakOnAttack) Uncloak(); } - public bool Cloaked { get { return !disabled && remainingTime <= 0; } } + public bool Cloaked { get { return !IsTraitDisabled && remainingTime <= 0; } } public void DamageStateChanged(Actor self, AttackInfo e) { @@ -105,7 +85,7 @@ namespace OpenRA.Mods.RA public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { - if (remainingTime > 0 || disabled) + if (remainingTime > 0 || IsTraitDisabled) return r; if (Cloaked && IsVisible(self, self.World.RenderPlayer)) @@ -121,10 +101,10 @@ namespace OpenRA.Mods.RA public void Tick(Actor self) { - if (disabled) + if (IsTraitDisabled) return; - if (remainingTime > 0 && !disabled && !damageDisabled && --remainingTime <= 0) + if (remainingTime > 0 && !IsTraitDisabled && !damageDisabled && --remainingTime <= 0) Sound.Play(Info.CloakSound, self.CenterPosition); if (self.IsDisabled()) diff --git a/OpenRA.Mods.RA/Crates/GrantUpgradeCrateAction.cs b/OpenRA.Mods.RA/Crates/GrantUpgradeCrateAction.cs index 6bb2ebfef4..6e69a8c362 100644 --- a/OpenRA.Mods.RA/Crates/GrantUpgradeCrateAction.cs +++ b/OpenRA.Mods.RA/Crates/GrantUpgradeCrateAction.cs @@ -9,6 +9,7 @@ #endregion using System.Linq; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA.Crates diff --git a/OpenRA.Mods.RA/DisableUpgrade.cs b/OpenRA.Mods.RA/DisableUpgrade.cs index aa2b0b4948..a1814e6a92 100644 --- a/OpenRA.Mods.RA/DisableUpgrade.cs +++ b/OpenRA.Mods.RA/DisableUpgrade.cs @@ -11,40 +11,23 @@ using System; using System.Collections.Generic; using OpenRA.GameRules; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class DisableUpgradeInfo : ITraitInfo + public class DisableUpgradeInfo : UpgradableTraitInfo, ITraitInfo { - public readonly string RequiresUpgrade = "disable"; - public object Create(ActorInitializer init) { return new DisableUpgrade(this); } } - public class DisableUpgrade : IUpgradable, IDisable, IDisableMove + public class DisableUpgrade : UpgradableTrait, IDisable, IDisableMove { - readonly DisableUpgradeInfo info; - bool enabled; - public DisableUpgrade(DisableUpgradeInfo info) - { - this.info = info; - } + : base(info) { } - public bool AcceptsUpgrade(string type) - { - return type == info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == info.RequiresUpgrade) - enabled = available; - } - - public bool Disabled { get { return enabled; } } - - public bool MoveDisabled(Actor self) { return enabled; } + // Disable the actor when this trait is enabled. + public bool Disabled { get { return !IsTraitDisabled; } } + public bool MoveDisabled(Actor self) { return !IsTraitDisabled; } } } diff --git a/OpenRA.Mods.RA/GainsExperience.cs b/OpenRA.Mods.RA/GainsExperience.cs index dc524dc614..57d746e6a5 100644 --- a/OpenRA.Mods.RA/GainsExperience.cs +++ b/OpenRA.Mods.RA/GainsExperience.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.RA { 200, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, { 400, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, { 800, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, - { 1600, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "selfheal" } } + { 1600, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "eliteweapon", "selfheal" } } }; } diff --git a/OpenRA.Mods.RA/GainsStatUpgrades.cs b/OpenRA.Mods.RA/GainsStatUpgrades.cs index 89bc57acd8..d6e8b8279d 100644 --- a/OpenRA.Mods.RA/GainsStatUpgrades.cs +++ b/OpenRA.Mods.RA/GainsStatUpgrades.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using OpenRA.GameRules; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -36,7 +37,7 @@ namespace OpenRA.Mods.RA public object Create(ActorInitializer init) { return new GainsStatUpgrades(this); } } - public class GainsStatUpgrades : IUpgradable, IFirepowerModifier, IDamageModifier, ISpeedModifier, IReloadModifier, IInaccuracyModifier + public class GainsStatUpgrades : IUpgradable, IFirepowerModifier, IDamageModifier, ISpeedModifier, IReloadModifier, IInaccuracyModifier, IDisabledTrait { readonly GainsStatUpgradesInfo info; [Sync] int firepowerLevel = 0; @@ -44,34 +45,59 @@ namespace OpenRA.Mods.RA [Sync] int damageLevel = 0; [Sync] int reloadLevel = 0; [Sync] int inaccuracyLevel = 0; + public bool IsTraitDisabled { get { return firepowerLevel == 0 && speedLevel == 0 && damageLevel == 0 && reloadLevel == 0 && inaccuracyLevel == 0; } } + public IEnumerable UpgradeTypes + { + get + { + yield return info.FirepowerUpgrade; + yield return info.DamageUpgrade; + yield return info.SpeedUpgrade; + yield return info.ReloadUpgrade; + yield return info.InaccuracyUpgrade; + } + } public GainsStatUpgrades(GainsStatUpgradesInfo info) { this.info = info; } - public bool AcceptsUpgrade(string type) + public bool AcceptsUpgradeLevel(Actor self, string type, int level) { - return (type == info.FirepowerUpgrade && firepowerLevel < info.FirepowerModifier.Length) - || (type == info.DamageUpgrade && damageLevel < info.DamageModifier.Length) - || (type == info.SpeedUpgrade && speedLevel < info.SpeedModifier.Length) - || (type == info.ReloadUpgrade && reloadLevel < info.ReloadModifier.Length) - || (type == info.InaccuracyUpgrade && inaccuracyLevel < info.InaccuracyModifier.Length); + if (level < 0) + return false; + + if (type == info.FirepowerUpgrade) + return level <= info.FirepowerModifier.Length; + + if (type == info.DamageUpgrade) + return level <= info.DamageModifier.Length; + + if (type == info.SpeedUpgrade) + return level <= info.SpeedModifier.Length; + + if (type == info.ReloadUpgrade) + return level <= info.ReloadModifier.Length; + + if (type == info.InaccuracyUpgrade) + return level <= info.InaccuracyModifier.Length; + + return false; } - public void UpgradeAvailable(Actor self, string type, bool available) + public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel) { - var mod = available ? 1 : -1; if (type == info.FirepowerUpgrade) - firepowerLevel = (firepowerLevel + mod).Clamp(0, info.FirepowerModifier.Length); + firepowerLevel = newLevel.Clamp(0, info.FirepowerModifier.Length); else if (type == info.DamageUpgrade) - damageLevel = (damageLevel + mod).Clamp(0, info.DamageModifier.Length); + damageLevel = newLevel.Clamp(0, info.DamageModifier.Length); else if (type == info.SpeedUpgrade) - speedLevel = (speedLevel + mod).Clamp(0, info.SpeedModifier.Length); + speedLevel = newLevel.Clamp(0, info.SpeedModifier.Length); else if (type == info.ReloadUpgrade) - reloadLevel = (reloadLevel + mod).Clamp(0, info.ReloadModifier.Length); + reloadLevel = newLevel.Clamp(0, info.ReloadModifier.Length); else if (type == info.InaccuracyUpgrade) - inaccuracyLevel = (inaccuracyLevel + mod).Clamp(0, info.InaccuracyModifier.Length); + inaccuracyLevel = newLevel.Clamp(0, info.InaccuracyModifier.Length); } public int GetDamageModifier(Actor attacker, DamageWarhead warhead) diff --git a/OpenRA.Mods.RA/GlobalUpgradable.cs b/OpenRA.Mods.RA/GlobalUpgradable.cs index a38da1f9cd..aa42780c9a 100644 --- a/OpenRA.Mods.RA/GlobalUpgradable.cs +++ b/OpenRA.Mods.RA/GlobalUpgradable.cs @@ -12,7 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; -using OpenRA.Mods.RA; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -30,6 +30,7 @@ namespace OpenRA.Mods.RA readonly GlobalUpgradableInfo info; readonly GlobalUpgradeManager globalManager; readonly UpgradeManager manager; + bool wasAvailable; public GlobalUpgradable(Actor self, GlobalUpgradableInfo info) { @@ -52,13 +53,17 @@ namespace OpenRA.Mods.RA public void PrerequisitesUpdated(Actor self, bool available) { - foreach (var u in info.Upgrades) - { - if (available) + if (available == wasAvailable) + return; + + if (available) + foreach (var u in info.Upgrades) manager.GrantUpgrade(self, u, this); - else + else + foreach (var u in info.Upgrades) manager.RevokeUpgrade(self, u, this); - } + + wasAvailable = available; } } } diff --git a/OpenRA.Mods.RA/InvulnerabilityUpgrade.cs b/OpenRA.Mods.RA/InvulnerabilityUpgrade.cs index ffa34449a3..378f8e3a4f 100644 --- a/OpenRA.Mods.RA/InvulnerabilityUpgrade.cs +++ b/OpenRA.Mods.RA/InvulnerabilityUpgrade.cs @@ -11,41 +11,24 @@ using System; using System.Collections.Generic; using OpenRA.GameRules; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class InvulnerabilityUpgradeInfo : ITraitInfo + public class InvulnerabilityUpgradeInfo : UpgradableTraitInfo, ITraitInfo { - public readonly string RequiresUpgrade = "invulnerability"; - public object Create(ActorInitializer init) { return new InvulnerabilityUpgrade(this); } } - public class InvulnerabilityUpgrade : IUpgradable, IDamageModifier + public class InvulnerabilityUpgrade : UpgradableTrait, IDamageModifier { - readonly InvulnerabilityUpgradeInfo info; - bool enabled; - public InvulnerabilityUpgrade(InvulnerabilityUpgradeInfo info) - { - this.info = info; - } - - public bool AcceptsUpgrade(string type) - { - return type == info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == info.RequiresUpgrade) - enabled = available; - } + : base (info) { } public int GetDamageModifier(Actor attacker, DamageWarhead warhead) { - return enabled ? 0 : 100; + return IsTraitDisabled ? 100 : 0; } } } diff --git a/OpenRA.Mods.RA/KillsSelf.cs b/OpenRA.Mods.RA/KillsSelf.cs index 29167f67de..407de06c91 100644 --- a/OpenRA.Mods.RA/KillsSelf.cs +++ b/OpenRA.Mods.RA/KillsSelf.cs @@ -12,57 +12,38 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using OpenRA.Effects; +using OpenRA.Mods.Common; using OpenRA.Primitives; using OpenRA.Traits; -using OpenRA.Effects; namespace OpenRA.Mods.RA { - class KillsSelfInfo : ITraitInfo + class KillsSelfInfo : UpgradableTraitInfo, ITraitInfo { - [Desc("Enable only if this upgrade is enabled.")] - public readonly string RequiresUpgrade = null; - [Desc("Remove the actor from the world (and destroy it) instead of killing it.")] public readonly bool RemoveInstead = false; - public object Create(ActorInitializer init) { return new KillsSelf(init.self, this); } + public object Create(ActorInitializer init) { return new KillsSelf(this); } } - class KillsSelf : INotifyAddedToWorld, IUpgradable + class KillsSelf : UpgradableTrait, INotifyAddedToWorld { - readonly KillsSelfInfo info; - readonly Actor self; - - public KillsSelf(Actor self, KillsSelfInfo info) - { - this.info = info; - this.self = self; - } + public KillsSelf(KillsSelfInfo info) + : base(info) { } public void AddedToWorld(Actor self) { - if (info.RequiresUpgrade == null) - Kill(); + if (!IsTraitDisabled) + UpgradeEnabled(self); } - public bool AcceptsUpgrade(string type) - { - return type == info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == info.RequiresUpgrade) - Kill(); - } - - void Kill() + protected override void UpgradeEnabled(Actor self) { if (self.IsDead) return; - if (info.RemoveInstead || !self.HasTrait()) + if (Info.RemoveInstead || !self.HasTrait()) self.Destroy(); else self.Kill(self); diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 0ac18fcaaf..d14b5d242f 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -463,10 +463,8 @@ - - diff --git a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs index 1c75bfa17a..b601c899e9 100755 --- a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Power; +using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders { @@ -39,7 +40,8 @@ namespace OpenRA.Mods.RA.Orders if (mi.Button == MouseButton.Left) { var underCursor = world.ScreenMap.ActorsAt(mi) - .FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasTrait()); + .FirstOrDefault(a => a.Owner == world.LocalPlayer && a.TraitsImplementing() + .Any(Exts.IsTraitEnabled)); if (underCursor != null) yield return new Order(order, underCursor, false); diff --git a/OpenRA.Mods.RA/Scripting/Properties/UpgradeProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/UpgradeProperties.cs index 35d1ae9fc2..9af6e5a129 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/UpgradeProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/UpgradeProperties.cs @@ -8,6 +8,7 @@ */ #endregion +using OpenRA.Mods.Common; using OpenRA.Scripting; using OpenRA.Traits; diff --git a/OpenRA.Mods.RA/SelfHealing.cs b/OpenRA.Mods.RA/SelfHealing.cs index 4791ac82d6..89f3a46ecc 100644 --- a/OpenRA.Mods.RA/SelfHealing.cs +++ b/OpenRA.Mods.RA/SelfHealing.cs @@ -8,62 +8,43 @@ */ #endregion +using System; using System.Linq; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA { [Desc("Attach this to actors which should be able to regenerate their health points.")] - class SelfHealingInfo : ITraitInfo, Requires + class SelfHealingInfo : UpgradableTraitInfo, ITraitInfo, Requires { public readonly int Step = 5; public readonly int Ticks = 5; public readonly float HealIfBelow = .5f; public readonly int DamageCooldown = 0; - [Desc("Enable only if this upgrade is enabled.")] - public readonly string RequiresUpgrade = null; - public virtual object Create(ActorInitializer init) { return new SelfHealing(init.self, this); } } - class SelfHealing : ITick, ISync, INotifyDamage, IUpgradable + class SelfHealing : UpgradableTrait, ITick, INotifyDamage { - readonly SelfHealingInfo info; readonly Health health; [Sync] int ticks; [Sync] int damageTicks; - [Sync] bool disabled; - public SelfHealing(Actor self, SelfHealingInfo info) + : base (info) { - this.info = info; - health = self.Trait(); - - // Disable if an upgrade is required - disabled = info.RequiresUpgrade != null; - } - - public bool AcceptsUpgrade(string type) - { - return type == info.RequiresUpgrade; - } - - public void UpgradeAvailable(Actor self, string type, bool available) - { - if (type == info.RequiresUpgrade) - disabled = !available; } public void Tick(Actor self) { - if (self.IsDead || disabled) + if (self.IsDead || IsTraitDisabled) return; - if (health.HP >= info.HealIfBelow * health.MaxHP) + if (health.HP >= Info.HealIfBelow * health.MaxHP) return; if (damageTicks > 0) @@ -74,15 +55,15 @@ namespace OpenRA.Mods.RA if (--ticks <= 0) { - ticks = info.Ticks; - self.InflictDamage(self, -info.Step, null); + ticks = Info.Ticks; + self.InflictDamage(self, -Info.Step, null); } } public void Damaged(Actor self, AttackInfo e) { if (e.Damage > 0) - damageTicks = info.DamageCooldown; + damageTicks = Info.DamageCooldown; } } } diff --git a/OpenRA.Mods.RA/SupportPowers/GrantUpgradePower.cs b/OpenRA.Mods.RA/SupportPowers/GrantUpgradePower.cs index c2003e6fc9..1540718efd 100644 --- a/OpenRA.Mods.RA/SupportPowers/GrantUpgradePower.cs +++ b/OpenRA.Mods.RA/SupportPowers/GrantUpgradePower.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using OpenRA.Graphics; +using OpenRA.Mods.Common; using OpenRA.Mods.RA.Render; using OpenRA.Traits; diff --git a/OpenRA.Mods.RA/UpgradeActorsNear.cs b/OpenRA.Mods.RA/UpgradeActorsNear.cs index 3a099ef2c4..e7b4f40add 100644 --- a/OpenRA.Mods.RA/UpgradeActorsNear.cs +++ b/OpenRA.Mods.RA/UpgradeActorsNear.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using OpenRA.GameRules; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA diff --git a/OpenRA.Mods.RA/UpgradeManager.cs b/OpenRA.Mods.RA/UpgradeManager.cs deleted file mode 100644 index c72fd48155..0000000000 --- a/OpenRA.Mods.RA/UpgradeManager.cs +++ /dev/null @@ -1,134 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2014 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. For more information, - * see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using OpenRA.Graphics; -using OpenRA.Primitives; -using OpenRA.Traits; - -namespace OpenRA.Mods.RA -{ - public class UpgradeManagerInfo : ITraitInfo - { - public object Create(ActorInitializer init) { return new UpgradeManager(init); } - } - - public class UpgradeManager : ITick - { - class TimedUpgrade - { - public readonly string Upgrade; - public readonly int Duration; - public int Remaining; - - public TimedUpgrade(string upgrade, int duration) - { - Upgrade = upgrade; - Duration = duration; - Remaining = duration; - } - - public void Tick() { Remaining--; } - } - - readonly List timedUpgrades = new List(); - readonly Dictionary> sources = new Dictionary>(); - readonly Dictionary>> watchers = new Dictionary>>(); - readonly Lazy> upgradable; - - public UpgradeManager(ActorInitializer init) - { - upgradable = Exts.Lazy(() => init.self.TraitsImplementing()); - } - - public void GrantTimedUpgrade(Actor self, string upgrade, int duration) - { - var timed = timedUpgrades.FirstOrDefault(u => u.Upgrade == upgrade); - if (timed == null) - { - timed = new TimedUpgrade(upgrade, duration); - timedUpgrades.Add(timed); - GrantUpgrade(self, upgrade, timed); - } - else - timed.Remaining = Math.Max(duration, timed.Remaining); - } - - public void GrantUpgrade(Actor self, string upgrade, object source) - { - List ss; - if (!sources.TryGetValue(upgrade, out ss)) - { - ss = new List(); - sources.Add(upgrade, ss); - - foreach (var up in upgradable.Value) - if (up.AcceptsUpgrade(upgrade)) - up.UpgradeAvailable(self, upgrade, true); - } - - // Track the upgrade source so that the upgrade can be removed without conflicts - ss.Add(source); - } - - public void RevokeUpgrade(Actor self, string upgrade, object source) - { - // This upgrade may have been granted by multiple sources - // We must be careful to only remove the upgrade after all - // sources have been revoked - List ss; - if (!sources.TryGetValue(upgrade, out ss)) - return; - - ss.Remove(source); - if (!ss.Any()) - { - foreach (var up in upgradable.Value) - if (up.AcceptsUpgrade(upgrade)) - up.UpgradeAvailable(self, upgrade, false); - - sources.Remove(upgrade); - } - } - - public bool AcceptsUpgrade(Actor self, string upgrade) - { - return upgradable.Value.Any(up => up.AcceptsUpgrade(upgrade)); - } - - public void RegisterWatcher(string upgrade, Action action) - { - if (!watchers.ContainsKey(upgrade)) - watchers.Add(upgrade, new List>()); - - watchers[upgrade].Add(action); - } - - public void Tick(Actor self) - { - foreach (var u in timedUpgrades) - { - u.Tick(); - if (u.Remaining <= 0) - RevokeUpgrade(self, u.Upgrade, u); - - List> actions; - if (watchers.TryGetValue(u.Upgrade, out actions)) - foreach (var a in actions) - a(u.Duration, u.Remaining); - } - - timedUpgrades.RemoveAll(u => u.Remaining <= 0); - } - } -} diff --git a/OpenRA.Mods.RA/Warheads/GrantUpgradeWarhead.cs b/OpenRA.Mods.RA/Warheads/GrantUpgradeWarhead.cs index 8184bf9c60..013db37dd6 100644 --- a/OpenRA.Mods.RA/Warheads/GrantUpgradeWarhead.cs +++ b/OpenRA.Mods.RA/Warheads/GrantUpgradeWarhead.cs @@ -13,6 +13,7 @@ using System.Linq; using OpenRA.Effects; using OpenRA.GameRules; using OpenRA.Traits; +using OpenRA.Mods.Common; using OpenRA.Mods.RA.Effects; namespace OpenRA.Mods.RA diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 1335e130fa..2656b3150e 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -34,7 +34,8 @@ GenericName: Vehicle UpdatesPlayerStatistics: Cloak: - RequiresUpgrade: cloak + UpgradeTypes: cloak + UpgradeMinEnabledLevel: 1 InitialDelay: 15 CloakDelay: 90 CloakSound: trans1.aud @@ -47,7 +48,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Tank: @@ -89,7 +91,8 @@ GenericName: Tank UpdatesPlayerStatistics: Cloak: - RequiresUpgrade: cloak + UpgradeTypes: cloak + UpgradeMinEnabledLevel: 1 InitialDelay: 15 CloakDelay: 90 CloakSound: trans1.aud @@ -102,7 +105,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Helicopter: @@ -142,7 +146,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Infantry: @@ -201,7 +206,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: hospitalheal + UpgradeTypes: hospitalheal + UpgradeMinEnabledLevel: 1 GlobalUpgradable: Upgrades: hospitalheal Prerequisites: hosp @@ -224,7 +230,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^CivInfantry: @@ -330,7 +337,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Ship: @@ -363,7 +371,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Building: diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index eb0d7e4df2..94ab348d86 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -533,7 +533,7 @@ STNK: RevealsShroud: Range: 7c0 Cloak: - -RequiresUpgrade: + -UpgradeTypes: InitialDelay: 90 CloakDelay: 90 CloakSound: trans1.aud diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 661906e392..0552cef2df 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -44,7 +44,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Tank: @@ -93,7 +94,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Husk: @@ -217,7 +219,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Plane: @@ -249,7 +252,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Helicopter: diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml index 20e421c712..47f9532cca 100644 --- a/mods/ra/maps/desert-shellmap/map.yaml +++ b/mods/ra/maps/desert-shellmap/map.yaml @@ -1300,14 +1300,16 @@ Rules: GainsExperience: Upgrades: InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 ^Tank: GivesBounty: Percentage: 0 GainsExperience: Upgrades: InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 ^Infantry: GivesBounty: Percentage: 0 @@ -1320,24 +1322,28 @@ Rules: DeathSounds@ZAPPED: VolumeMultiplier: 0.1 InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 ^Ship: GivesBounty: Percentage: 0 GainsExperience: Upgrades: InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 ^Plane: GivesBounty: Percentage: 0 InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 ^Building: GivesBounty: Percentage: 0 InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 E7: -Selectable: diff --git a/mods/ra/maps/fort-lonestar/map.yaml b/mods/ra/maps/fort-lonestar/map.yaml index 4a6a8284aa..ab363803ed 100644 --- a/mods/ra/maps/fort-lonestar/map.yaml +++ b/mods/ra/maps/fort-lonestar/map.yaml @@ -658,7 +658,8 @@ Rules: AutoTarget: InitialStance: Defend InvulnerabilityUpgrade@UNKILLABLE: - RequiresUpgrade: unkillable + UpgradeTypes: unkillable + UpgradeMinEnabledLevel: 1 SPY: Inherits: ^Infantry Buildable: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 3245fce760..c71ab69a48 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -59,12 +59,16 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: UpgradeOverlay@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 InvulnerabilityUpgrade@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar: Upgrade: invulnerability @@ -129,12 +133,16 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: UpgradeOverlay@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 InvulnerabilityUpgrade@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar: Upgrade: invulnerability @@ -192,7 +200,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: hospitalheal + UpgradeTypes: hospitalheal + UpgradeMinEnabledLevel: 1 GlobalUpgradable: Upgrades: hospitalheal Prerequisites: hosp @@ -221,7 +230,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: ^Ship: @@ -263,14 +273,19 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: UpgradeOverlay@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 InvulnerabilityUpgrade@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar: Upgrade: invulnerability + UpgradeMinEnabledLevel: 1 ^Plane: AppearsOnRadar: @@ -314,12 +329,16 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 UpgradeManager: UpgradeOverlay@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 InvulnerabilityUpgrade@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar: Upgrade: invulnerability @@ -382,9 +401,12 @@ ScriptTriggers: UpgradeManager: UpgradeOverlay@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 InvulnerabilityUpgrade@IRONCURTAIN: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar: Upgrade: invulnerability diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index d03619d4a6..01e85fdc93 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -655,7 +655,8 @@ DTRK: DemoTruck: -InvulnerabilityUpgrade@IRONCURTAIN: KillsSelf: - RequiresUpgrade: invulnerability + UpgradeTypes: invulnerability + UpgradeMinEnabledLevel: 1 Chronoshiftable: ExplodeInstead: yes diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index b2bf081521..f91927d7ca 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -45,7 +45,8 @@ WithMakeAnimation: UpgradeManager: Cloak@CLOAKGENERATOR: - RequiresUpgrade: cloakgenerator + UpgradeTypes: cloakgenerator + UpgradeMinEnabledLevel: 1 InitialDelay: 0 CloakDelay: 90 @@ -87,7 +88,8 @@ ScriptTriggers: UpgradeManager: Cloak@CLOAKGENERATOR: - RequiresUpgrade: cloakgenerator + UpgradeTypes: cloakgenerator + UpgradeMinEnabledLevel: 1 InitialDelay: 0 CloakDelay: 90 @@ -138,7 +140,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -167,7 +170,8 @@ DeathTypes: 6 UpgradeManager: Cloak@CLOAKGENERATOR: - RequiresUpgrade: cloakgenerator + UpgradeTypes: cloakgenerator + UpgradeMinEnabledLevel: 1 InitialDelay: 0 CloakDelay: 90 @@ -238,7 +242,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -258,15 +263,21 @@ EmptyWeapon: UnitExplodeSmall UpgradeManager: UpgradeOverlay@EMPDISABLE: - RequiresUpgrade: empdisable + UpgradeTypes: empdisable + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 Palette: disabled DisableUpgrade@EMPDISABLE: - RequiresUpgrade: empdisable + UpgradeTypes: empdisable + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar@EMPDISABLE: Upgrade: empdisable Color: 255,255,255 Cloak@CLOAKGENERATOR: - RequiresUpgrade: cloakgenerator + UpgradeTypes: cloakgenerator + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 InitialDelay: 0 CloakDelay: 90 @@ -310,7 +321,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -330,15 +342,21 @@ EmptyWeapon: UnitExplodeSmall UpgradeManager: UpgradeOverlay@EMPDISABLE: - RequiresUpgrade: empdisable + UpgradeTypes: empdisable + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 Palette: disabled DisableUpgrade@EMPDISABLE: - RequiresUpgrade: empdisable + UpgradeTypes: empdisable + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 TimedUpgradeBar@EMPDISABLE: Upgrade: empdisable Color: 255,255,255 Cloak@CLOAKGENERATOR: - RequiresUpgrade: cloakgenerator + UpgradeTypes: cloakgenerator + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 InitialDelay: 0 CloakDelay: 90 @@ -375,7 +393,8 @@ Ticks: 100 HealIfBelow: 1 DamageCooldown: 125 - RequiresUpgrade: selfheal + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: diff --git a/mods/ts/rules/infantry.yaml b/mods/ts/rules/infantry.yaml index 0ec2e622c6..476c4fda9a 100644 --- a/mods/ts/rules/infantry.yaml +++ b/mods/ts/rules/infantry.yaml @@ -17,10 +17,13 @@ E1: Speed: 71 Armament@PRIMARY: Weapon: Minigun - RestrictedByUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMaxEnabledLevel: 0 + UpgradeMaxAcceptedLevel: 1 Armament@ELITE: Weapon: M1Carbine - RequiresUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMinEnabledLevel: 1 AttackFrontal: TakeCover: RenderInfantry: diff --git a/mods/ts/rules/structures.yaml b/mods/ts/rules/structures.yaml index 82dfe36028..36c6a70560 100644 --- a/mods/ts/rules/structures.yaml +++ b/mods/ts/rules/structures.yaml @@ -501,12 +501,15 @@ GATICK: Armament@PRIMARY: Weapon: 90mm LocalOffset: 384,0,128 - RestrictedByUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMaxEnabledLevel: 0 + UpgradeMaxAcceptedLevel: 1 MuzzleSequence: muzzle Armament@ELITE: Weapon: 120mmx LocalOffset: 384,0,128 - RequiresUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMinEnabledLevel: 1 MuzzleSequence: muzzle AttackTurreted: BodyOrientation: diff --git a/mods/ts/rules/vehicles.yaml b/mods/ts/rules/vehicles.yaml index 05207b0ba2..e40f326ec0 100644 --- a/mods/ts/rules/vehicles.yaml +++ b/mods/ts/rules/vehicles.yaml @@ -624,11 +624,14 @@ BIKE: Range: 5c0 Armament@PRIMARY: Weapon: BikeMissile - RestrictedByUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMaxEnabledLevel: 0 + UpgradeMaxAcceptedLevel: 1 LocalOffset: -128,-170,213, -128,170,213 Armament@ELITE: Weapon: HoverMissile - RequiresUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMinEnabledLevel: 1 LocalOffset: -128,-170,213, -128,170,213 AttackFrontal: AutoTarget: @@ -785,12 +788,15 @@ TTNK: Armament@PRIMARY: Weapon: 90mm LocalOffset: 256,0,256 - RestrictedByUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMaxEnabledLevel: 0 + UpgradeMaxAcceptedLevel: 1 MuzzleSequence: muzzle Armament@ELITE: Weapon: 120mmx LocalOffset: 256,0,256 - RequiresUpgrade: eliteweapon + UpgradeTypes: eliteweapon + UpgradeMinEnabledLevel: 1 MuzzleSequence: muzzle WithMuzzleFlash: RevealsShroud: