diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index f2feebe28c..0a6abc5ec1 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -344,7 +344,6 @@
-
@@ -357,6 +356,13 @@
+
+
+
+
+
+
+
@@ -477,7 +483,6 @@
-
diff --git a/OpenRA.Mods.Common/Traits/Invulnerable.cs b/OpenRA.Mods.Common/Traits/Invulnerable.cs
deleted file mode 100644
index 996acb2dcc..0000000000
--- a/OpenRA.Mods.Common/Traits/Invulnerable.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-#region Copyright & License Information
-/*
- * Copyright 2007-2015 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 OpenRA.Traits;
-
-namespace OpenRA.Mods.Common.Traits
-{
- [Desc("This unit cannot be damaged.")]
- class InvulnerableInfo : TraitInfo { }
-
- class Invulnerable : IDamageModifier
- {
- public int GetDamageModifier(Actor attacker, IWarhead warhead) { return 0; }
- }
-}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs
new file mode 100644
index 0000000000..57050bc3e0
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs
@@ -0,0 +1,31 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.GameRules;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("Damage taken by this actor is multiplied based on upgrade level.",
+ "Decrease to increase actor's apparent strength.",
+ "Use 0 to make actor invulnerable.")]
+ public class DamageMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new DamageMultiplier(this, init.Self.Info.Name); }
+ }
+
+ public class DamageMultiplier : UpgradeMultiplierTrait, IDamageModifier
+ {
+ public DamageMultiplier(DamageMultiplierInfo info, string actorType)
+ : base(info, "DamageMultiplier", actorType) { }
+
+ public int GetDamageModifier(Actor attacker, IWarhead warhead) { return GetModifier(); }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs
new file mode 100644
index 0000000000..34de5380ac
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs
@@ -0,0 +1,28 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("The firepower of this actor is multiplied based on upgrade level if specified.")]
+ public class FirepowerMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new FirepowerMultiplier(this, init.Self.Info.Name); }
+ }
+
+ public class FirepowerMultiplier : UpgradeMultiplierTrait, IFirepowerModifier
+ {
+ public FirepowerMultiplier(FirepowerMultiplierInfo info, string actorType)
+ : base(info, "FirepowerMultiplier", actorType) { }
+
+ public int GetFirepowerModifier() { return GetModifier(); }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs
new file mode 100644
index 0000000000..382142ef06
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs
@@ -0,0 +1,28 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("The inaccuracy of this actor is multipled based on upgrade level if specified.")]
+ public class InaccuracyMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new InaccuracyMultiplier(this, init.Self.Info.Name); }
+ }
+
+ public class InaccuracyMultiplier : UpgradeMultiplierTrait, IInaccuracyModifier
+ {
+ public InaccuracyMultiplier(InaccuracyMultiplierInfo info, string actorType)
+ : base(info, "InaccuracyMultiplier", actorType) { }
+
+ public int GetInaccuracyModifier() { return GetModifier(); }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs
new file mode 100644
index 0000000000..2b3a5c8f75
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs
@@ -0,0 +1,36 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Mods.Common.Traits;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common
+{
+ [Desc("The power usage/output of this actor is multiplied based on upgrade level if specified.")]
+ public class PowerMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new PowerMultiplier(init.Self, this); }
+ }
+
+ public class PowerMultiplier : UpgradeMultiplierTrait, IPowerModifier, INotifyOwnerChanged
+ {
+ PowerManager power;
+
+ public PowerMultiplier(Actor self, PowerMultiplierInfo info)
+ : base(info, "PowerMultiplier", self.Info.Name) { power = self.Owner.PlayerActor.Trait(); }
+
+ public int GetPowerModifier() { return GetModifier(); }
+ protected override void Update(Actor self) { power.UpdateActor(self); }
+ public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
+ {
+ power = newOwner.PlayerActor.Trait();
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs
new file mode 100644
index 0000000000..f1fddd1c22
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs
@@ -0,0 +1,28 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("The reloading time of this actor is multiplied based on upgrade level if specified.")]
+ public class ReloadDelayMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new ReloadDelayMultiplier(this, init.Self.Info.Name); }
+ }
+
+ public class ReloadDelayMultiplier : UpgradeMultiplierTrait, IReloadModifier
+ {
+ public ReloadDelayMultiplier(ReloadDelayMultiplierInfo info, string actorType)
+ : base(info, "ReloadDelayMultiplier", actorType) { }
+
+ public int GetReloadModifier() { return GetModifier(); }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs
new file mode 100644
index 0000000000..dfab9a0b68
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs
@@ -0,0 +1,28 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("The speed of this actor is multiplied based on upgrade level if specified.")]
+ public class SpeedMultiplierInfo : UpgradeMultiplierTraitInfo, ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new SpeedMultiplier(this, init.Self.Info.Name); }
+ }
+
+ public class SpeedMultiplier : UpgradeMultiplierTrait, ISpeedModifier
+ {
+ public SpeedMultiplier(SpeedMultiplierInfo info, string actorType)
+ : base(info, "SpeedMultiplier", actorType) { }
+
+ public int GetSpeedModifier() { return GetModifier(); }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs b/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs
new file mode 100644
index 0000000000..c7dc103d40
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs
@@ -0,0 +1,72 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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.Linq;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ public abstract class UpgradeMultiplierTraitInfo
+ {
+ [UpgradeUsedReference]
+ [Desc("Accepted upgrade types.")]
+ public readonly string[] UpgradeTypes = { };
+
+ [Desc("The lowest upgrade level using the scale.")]
+ public readonly int BaseLevel = 1;
+
+ [FieldLoader.Require]
+ [Desc("Percentages to apply with the first being applied at the base level.",
+ "Repeat last entry to accept time extensions.",
+ "If no upgrade types are specified, then the first/only modifier is always applied.")]
+ public readonly int[] Modifier = { };
+ }
+
+ public abstract class UpgradeMultiplierTrait : IUpgradable, IDisabledTrait, ISync
+ {
+ readonly UpgradeMultiplierTraitInfo info;
+ [Sync] int level = 0;
+ [Sync] public bool IsTraitDisabled { get; private set; }
+ public int AdjustedLevel { get { return level - info.BaseLevel; } }
+ public IEnumerable UpgradeTypes { get { return info.UpgradeTypes; } }
+
+ protected UpgradeMultiplierTrait(UpgradeMultiplierTraitInfo info, string modifierType, string actorType)
+ {
+ if (info.Modifier.Length == 0)
+ throw new Exception("No modifiers in " + modifierType + " for " + actorType);
+ this.info = info;
+ IsTraitDisabled = info.UpgradeTypes.Length > 0 && info.BaseLevel > 0;
+ }
+
+ public bool AcceptsUpgradeLevel(Actor self, string type, int level)
+ {
+ return level < info.Modifier.Length + info.BaseLevel;
+ }
+
+ // Override to recieve notice of level change.
+ protected virtual void Update(Actor self) { }
+
+ public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel)
+ {
+ if (!UpgradeTypes.Contains(type))
+ return;
+ level = newLevel.Clamp(0, Math.Max(info.Modifier.Length + info.BaseLevel - 1, 0));
+ IsTraitDisabled = level < info.BaseLevel;
+ Update(self);
+ }
+
+ public int GetModifier()
+ {
+ return IsTraitDisabled ? 100 : info.Modifier[level - info.BaseLevel];
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Upgrades/GainsStatUpgrades.cs b/OpenRA.Mods.Common/Traits/Upgrades/GainsStatUpgrades.cs
deleted file mode 100644
index 313e9a175c..0000000000
--- a/OpenRA.Mods.Common/Traits/Upgrades/GainsStatUpgrades.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-#region Copyright & License Information
-/*
- * Copyright 2007-2015 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 OpenRA.GameRules;
-using OpenRA.Traits;
-
-namespace OpenRA.Mods.Common.Traits
-{
- [Desc("This actor has properties that upgrade when a specific criteria is met.")]
- public class GainsStatUpgradesInfo : ITraitInfo
- {
- public readonly string FirepowerUpgrade = "firepower";
- public readonly int[] FirepowerModifier = { 110, 115, 120, 130 };
-
- public readonly string DamageUpgrade = "damage";
- public readonly int[] DamageModifier = { 91, 87, 83, 65 };
-
- public readonly string SpeedUpgrade = "speed";
- public readonly int[] SpeedModifier = { 110, 115, 120, 150 };
-
- public readonly string ReloadUpgrade = "reload";
- public readonly int[] ReloadModifier = { 95, 90, 85, 75 };
-
- public readonly string InaccuracyUpgrade = "inaccuracy";
- public readonly int[] InaccuracyModifier = { 90, 80, 70, 50 };
-
- public object Create(ActorInitializer init) { return new GainsStatUpgrades(this); }
- }
-
- public class GainsStatUpgrades : IUpgradable, IFirepowerModifier, IDamageModifier, ISpeedModifier, IReloadModifier, IInaccuracyModifier, IDisabledTrait
- {
- readonly GainsStatUpgradesInfo info;
- [Sync] int firepowerLevel = 0;
- [Sync] int speedLevel = 0;
- [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 AcceptsUpgradeLevel(Actor self, string type, int level)
- {
- 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 UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel)
- {
- if (type == info.FirepowerUpgrade)
- firepowerLevel = newLevel.Clamp(0, info.FirepowerModifier.Length);
- else if (type == info.DamageUpgrade)
- damageLevel = newLevel.Clamp(0, info.DamageModifier.Length);
- else if (type == info.SpeedUpgrade)
- speedLevel = newLevel.Clamp(0, info.SpeedModifier.Length);
- else if (type == info.ReloadUpgrade)
- reloadLevel = newLevel.Clamp(0, info.ReloadModifier.Length);
- else if (type == info.InaccuracyUpgrade)
- inaccuracyLevel = newLevel.Clamp(0, info.InaccuracyModifier.Length);
- }
-
- public int GetDamageModifier(Actor attacker, IWarhead warhead)
- {
- return damageLevel > 0 ? info.DamageModifier[damageLevel - 1] : 100;
- }
-
- public int GetFirepowerModifier()
- {
- return firepowerLevel > 0 ? info.FirepowerModifier[firepowerLevel - 1] : 100;
- }
-
- public int GetSpeedModifier()
- {
- return speedLevel > 0 ? info.SpeedModifier[speedLevel - 1] : 100;
- }
-
- public int GetReloadModifier()
- {
- return reloadLevel > 0 ? info.ReloadModifier[reloadLevel - 1] : 100;
- }
-
- public int GetInaccuracyModifier()
- {
- return inaccuracyLevel > 0 ? info.InaccuracyModifier[inaccuracyLevel - 1] : 100;
- }
- }
-}
diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
index 11993fe398..2e23c7453a 100644
--- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
@@ -1380,6 +1380,131 @@ namespace OpenRA.Mods.Common.UtilityCommands
})));
}
+ if (engineVersion < 20150711)
+ {
+ if (depth == 0)
+ {
+ var emptyYaml = new MiniYaml(null);
+
+ // Replace -GainsStatUpgrades
+ var trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-GainsStatUpgrades");
+ if (trait != null)
+ {
+ node.Value.Nodes.Add(new MiniYamlNode("-FirepowerMultiplier@EXPERIENCE", emptyYaml));
+ node.Value.Nodes.Add(new MiniYamlNode("-DamageMultiplier@EXPERIENCE", emptyYaml));
+ node.Value.Nodes.Add(new MiniYamlNode("-SpeedMultiplier@EXPERIENCE", emptyYaml));
+ node.Value.Nodes.Add(new MiniYamlNode("-ReloadDelayMultiplier@EXPERIENCE", emptyYaml));
+ node.Value.Nodes.Add(new MiniYamlNode("-InaccuracyMultiplier@EXPERIENCE", emptyYaml));
+ node.Value.Nodes.Remove(trait);
+ }
+
+ // Replace GainsStatUpgrades
+ trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "GainsStatUpgrades");
+ if (trait != null)
+ {
+ // Common code for making each trait
+ Action addTrait = (type, newType, values) =>
+ {
+ var upgradeTypes = trait.Value.Nodes.FirstOrDefault(n => n.Key == type + "Upgrade");
+ var modifier = trait.Value.Nodes.FirstOrDefault(n => n.Key == type + "Modifier");
+
+ if (upgradeTypes == null || !string.IsNullOrEmpty(upgradeTypes.Value.Value) || modifier == null || !string.IsNullOrEmpty(modifier.Value.Value))
+ {
+ var yaml = new MiniYaml(null);
+ if (modifier == null)
+ modifier = new MiniYamlNode("Modifier", new MiniYaml(values));
+ else
+ modifier.Key = "Modifier";
+ yaml.Nodes.Add(modifier);
+
+ if (upgradeTypes == null)
+ upgradeTypes = new MiniYamlNode("UpgradeTypes", new MiniYaml(type.ToLowerInvariant()));
+ else
+ upgradeTypes.Key = "UpgradeTypes";
+ yaml.Nodes.Add(upgradeTypes);
+
+ node.Value.Nodes.Add(new MiniYamlNode((newType ?? type) + "Multiplier@EXPERIENCE", yaml));
+ }
+ };
+
+ // Execute common code for each trait
+ addTrait("Firepower", null, "110, 115, 120, 130");
+ addTrait("Damage", null, "91, 87, 83, 65");
+ addTrait("Speed", null, "110, 115, 120, 150");
+ addTrait("Reload", "ReloadTime", "95, 90, 85, 75");
+ addTrait("Inaccuracy", null, "90, 80, 70, 50");
+
+ // Remove GainsStatUpgrades
+ node.Value.Nodes.Remove(trait);
+ }
+
+ // Replace -InvulnerabilityUpgrade
+ trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-InvulnerabilityUpgrade");
+ if (trait != null)
+ trait.Key = "-DamageMultiplier@INVULNERABILITY_UPGRADE";
+
+ // Replace InvulnerabilityUpgrade with DamageMultiplier@INVULNERABILITY_UPGRADE
+ trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "InvulnerabilityUpgrade");
+ if (trait != null)
+ {
+ trait.Key = "DamageMultiplier@INVULNERABILITY_UPGRADE";
+ trait.Value.Nodes.Add(new MiniYamlNode("Modifier", "0, 0"));
+
+ // Use UpgradeMinEnabledLevel as BaseLevel; otherwise, 1
+ var min = trait.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel");
+ if (min != null)
+ {
+ if (min.Value.Value != "1")
+ min.Key = "BaseLevel";
+ else
+ trait.Value.Nodes.Remove(min);
+ }
+
+ // Remove since level cap is based of Modifier.Length + BaseLevel
+ trait.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxAcceptedLevel");
+ trait.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxEnabledLevel");
+ }
+
+ // Replace -InvulnerabilityUpgrade@* with -DamageMultiplier@*
+ foreach (var n in node.Value.Nodes.Where(n => n.Key.StartsWith("-InvulnerabilityUpgrade@")))
+ n.Key = "-DamageMultiplier@" + n.Key.Substring("-InvulnerabilityUpgrade@".Length);
+
+ // Replace InvulnerabilityUpgrade@* with DamageMultiplier@*
+ foreach (var t in node.Value.Nodes.Where(n => n.Key.StartsWith("InvulnerabilityUpgrade@")))
+ {
+ t.Key = "DamageMultiplier@" + t.Key.Substring("InvulnerabilityUpgrade@".Length);
+ t.Value.Nodes.Add(new MiniYamlNode("Modifier", "0, 0"));
+
+ // Use UpgradeMinEnabledLevel as BaseLevel; otherwise, 1
+ var min = t.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel");
+ if (min != null)
+ {
+ if (min.Value.Value != "1")
+ min.Key = "BaseLevel";
+ else
+ t.Value.Nodes.Remove(min);
+ }
+
+ // Remove since level cap is based of Modifier.Length + BaseLevel
+ t.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxAcceptedLevel");
+ t.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxEnabledLevel");
+ }
+
+ // Replace -Invulnerable with -DamageMultiplier@INVULNERABLE
+ trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-Invulnerable");
+ if (trait != null)
+ trait.Key = "-DamageMultiplier@INVULNERABLE";
+
+ // Invulnerable with DamageMultiplier@INVULNERABLE
+ trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "Invulnerable");
+ if (trait != null)
+ {
+ trait.Key = "DamageMultiplier@INVULNERABLE";
+ trait.Value.Nodes.Add(new MiniYamlNode("Modifier", "0"));
+ }
+ }
+ }
+
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}
diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 762e4cfb6c..27d39ab054 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -98,7 +98,6 @@
-
diff --git a/OpenRA.Mods.RA/Traits/InvulnerabilityUpgrade.cs b/OpenRA.Mods.RA/Traits/InvulnerabilityUpgrade.cs
deleted file mode 100644
index 26b457fc61..0000000000
--- a/OpenRA.Mods.RA/Traits/InvulnerabilityUpgrade.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-#region Copyright & License Information
-/*
- * Copyright 2007-2015 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 OpenRA.Mods.Common.Traits;
-using OpenRA.Traits;
-
-namespace OpenRA.Mods.RA
-{
- public class InvulnerabilityUpgradeInfo : UpgradableTraitInfo
- {
- public override object Create(ActorInitializer init) { return new InvulnerabilityUpgrade(this); }
- }
-
- public class InvulnerabilityUpgrade : UpgradableTrait, IDamageModifier
- {
- public InvulnerabilityUpgrade(InvulnerabilityUpgradeInfo info)
- : base(info) { }
-
- public int GetDamageModifier(Actor attacker, IWarhead warhead)
- {
- return IsTraitDisabled ? 100 : 0;
- }
- }
-}
diff --git a/mods/cnc/maps/gdi04c/map.yaml b/mods/cnc/maps/gdi04c/map.yaml
index edf37d37ed..9b2f384ba3 100644
--- a/mods/cnc/maps/gdi04c/map.yaml
+++ b/mods/cnc/maps/gdi04c/map.yaml
@@ -831,7 +831,8 @@ Rules:
Health:
HP: 125
^Bridge:
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
Sequences:
diff --git a/mods/cnc/maps/nod01/map.yaml b/mods/cnc/maps/nod01/map.yaml
index 85d17c774d..afd8124a7b 100644
--- a/mods/cnc/maps/nod01/map.yaml
+++ b/mods/cnc/maps/nod01/map.yaml
@@ -295,7 +295,8 @@ Rules:
Tooltip:
Name: Nikoomba
^Bridge:
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
^CivBuilding:
MustBeDestroyed:
^CivInfantry:
diff --git a/mods/cnc/maps/nod06c/map.yaml b/mods/cnc/maps/nod06c/map.yaml
index 9bf7c2d860..5707b73468 100644
--- a/mods/cnc/maps/nod06c/map.yaml
+++ b/mods/cnc/maps/nod06c/map.yaml
@@ -530,7 +530,8 @@ Rules:
GenericVisibility: Enemy
ShowOwnerRow: false
^Bridge:
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
E2:
Buildable:
Prerequisites: ~pyle
diff --git a/mods/cnc/rules/aircraft.yaml b/mods/cnc/rules/aircraft.yaml
index 1e329ccdc3..0c86af2b04 100644
--- a/mods/cnc/rules/aircraft.yaml
+++ b/mods/cnc/rules/aircraft.yaml
@@ -162,7 +162,8 @@ C17:
Cargo:
MaxWeight: 10
PipCount: 10
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
Contrail@1:
Offset: -261,-650,0
TrailLength: 15
diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml
index a49a1a9fb8..878a5739a3 100644
--- a/mods/cnc/rules/defaults.yaml
+++ b/mods/cnc/rules/defaults.yaml
@@ -15,7 +15,21 @@
400: firepower, damage, speed, reload, inaccuracy, rank
800: firepower, damage, speed, reload, inaccuracy, rank
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal
- GainsStatUpgrades:
+ FirepowerMultiplier@EXPERIENCE:
+ UpgradeTypes: firepower
+ Modifier: 110, 115, 120, 130
+ DamageMultiplier@EXPERIENCE:
+ UpgradeTypes: damage
+ Modifier: 91, 87, 83, 65
+ SpeedMultiplier@EXPERIENCE:
+ UpgradeTypes: speed
+ Modifier: 110, 115, 120, 150
+ ReloadDelayMultiplier@EXPERIENCE:
+ UpgradeTypes: reload
+ Modifier: 95, 90, 85, 75
+ InaccuracyMultiplier@EXPERIENCE:
+ UpgradeTypes: inaccuracy
+ Modifier: 90, 80, 70, 50
SelfHealing@ELITE:
Step: 2
Ticks: 100
diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml
index bd17d1d208..0a4bd7a44f 100644
--- a/mods/d2k/rules/aircraft.yaml
+++ b/mods/d2k/rules/aircraft.yaml
@@ -85,7 +85,8 @@ frigate:
Cargo:
MaxWeight: 20
PipCount: 10
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
FlyAwayOnIdle:
RejectsOrders:
diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml
index 5c1c5770c9..f0f59ae2da 100644
--- a/mods/d2k/rules/defaults.yaml
+++ b/mods/d2k/rules/defaults.yaml
@@ -15,7 +15,21 @@
400: firepower, damage, speed, reload, inaccuracy, rank
800: firepower, damage, speed, reload, inaccuracy, rank
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal
- GainsStatUpgrades:
+ FirepowerMultiplier@EXPERIENCE:
+ UpgradeTypes: firepower
+ Modifier: 110, 115, 120, 130
+ DamageMultiplier@EXPERIENCE:
+ UpgradeTypes: damage
+ Modifier: 91, 87, 83, 65
+ SpeedMultiplier@EXPERIENCE:
+ UpgradeTypes: speed
+ Modifier: 110, 115, 120, 150
+ ReloadDelayMultiplier@EXPERIENCE:
+ UpgradeTypes: reload
+ Modifier: 95, 90, 85, 75
+ InaccuracyMultiplier@EXPERIENCE:
+ UpgradeTypes: inaccuracy
+ Modifier: 90, 80, 70, 50
SelfHealing@ELITE:
Step: 2
Ticks: 100
diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml
index 04ffff3df0..816f9062a8 100644
--- a/mods/ra/maps/desert-shellmap/map.yaml
+++ b/mods/ra/maps/desert-shellmap/map.yaml
@@ -1267,17 +1267,17 @@ Rules:
Percentage: 0
GainsExperience:
Upgrades:
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
^Tank:
GivesBounty:
Percentage: 0
GainsExperience:
Upgrades:
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
^Infantry:
GivesBounty:
Percentage: 0
@@ -1289,29 +1289,29 @@ Rules:
VolumeMultiplier: 0.1
DeathSounds@ZAPPED:
VolumeMultiplier: 0.1
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
^Ship:
GivesBounty:
Percentage: 0
GainsExperience:
Upgrades:
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
^Plane:
GivesBounty:
Percentage: 0
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
^Building:
GivesBounty:
Percentage: 0
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
OILB:
CashTrickler:
ShowTicks: false
@@ -1319,7 +1319,8 @@ Rules:
Burns:
Damage: 0
MISS:
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
APC:
Cargo:
InitialUnits: e1, e1, e2, e3, e4
diff --git a/mods/ra/maps/fort-lonestar/map.yaml b/mods/ra/maps/fort-lonestar/map.yaml
index 9697d503f1..71d8d667d0 100644
--- a/mods/ra/maps/fort-lonestar/map.yaml
+++ b/mods/ra/maps/fort-lonestar/map.yaml
@@ -645,9 +645,9 @@ Rules:
Buildable:
Prerequisites: ~disabled
MustBeDestroyed:
- InvulnerabilityUpgrade@UNKILLABLE:
+ DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable
- UpgradeMinEnabledLevel: 1
+ Modifier: 0, 0
RenderSprites:
Image: SNIPER
SPY:
diff --git a/mods/ra/maps/koth-hopes-anchor/map.yaml b/mods/ra/maps/koth-hopes-anchor/map.yaml
index 3f5d08e75e..dfa0317730 100644
--- a/mods/ra/maps/koth-hopes-anchor/map.yaml
+++ b/mods/ra/maps/koth-hopes-anchor/map.yaml
@@ -641,7 +641,8 @@ Rules:
Name: Strategic Point
RevealsShroud:
Range: 20c0
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
-Selectable:
-TargetableBuilding:
Player:
diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml
index f5695ae1c3..f90fd9582c 100644
--- a/mods/ra/rules/defaults.yaml
+++ b/mods/ra/rules/defaults.yaml
@@ -16,7 +16,21 @@
400: firepower, damage, speed, reload, inaccuracy, rank
800: firepower, damage, speed, reload, inaccuracy, rank
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal
- GainsStatUpgrades:
+ DamageMultiplier@EXPERIENCE:
+ UpgradeTypes: damage
+ Modifier: 91, 87, 83, 65
+ FirepowerMultiplier@EXPERIENCE:
+ UpgradeTypes: firepower
+ Modifier: 110, 115, 120, 130
+ SpeedMultiplier@EXPERIENCE:
+ UpgradeTypes: speed
+ Modifier: 110, 115, 120, 150
+ ReloadDelayMultiplier@EXPERIENCE:
+ UpgradeTypes: reload
+ Modifier: 95, 90, 85, 75
+ InaccuracyMultiplier@EXPERIENCE:
+ UpgradeTypes: inaccuracy
+ Modifier: 90, 80, 70, 50
SelfHealing@ELITE:
Step: 2
Ticks: 100
@@ -39,10 +53,9 @@
UpgradeOverlay@IRONCURTAIN:
UpgradeTypes: invulnerability
UpgradeMinEnabledLevel: 1
- InvulnerabilityUpgrade@IRONCURTAIN:
+ DamageMultiplier@IRONCURTAIN:
UpgradeTypes: invulnerability
- UpgradeMinEnabledLevel: 1
- UpgradeMaxAcceptedLevel: 2
+ Modifier: 0, 0
TimedUpgradeBar:
Upgrade: invulnerability
diff --git a/mods/ra/rules/misc.yaml b/mods/ra/rules/misc.yaml
index eb4f803953..e6a0dc285d 100644
--- a/mods/ra/rules/misc.yaml
+++ b/mods/ra/rules/misc.yaml
@@ -403,7 +403,8 @@ CTFLAG:
Name: Flag
Bib:
HasMinibib: Yes
- Invulnerable:
+ DamageMultiplier@INVULNERABLE:
+ Modifier: 0
-Selectable:
-TargetableBuilding:
diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml
index aa3274788a..7610d70065 100644
--- a/mods/ra/rules/vehicles.yaml
+++ b/mods/ra/rules/vehicles.yaml
@@ -648,7 +648,7 @@ DTRK:
Weapon: MiniNuke
EmptyWeapon: MiniNuke
DemoTruck:
- -InvulnerabilityUpgrade@IRONCURTAIN:
+ -DamageMultiplier@IRONCURTAIN:
KillsSelf:
UpgradeTypes: invulnerability
UpgradeMinEnabledLevel: 1
diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml
index 93463b5910..3f3ac37626 100644
--- a/mods/ts/rules/defaults.yaml
+++ b/mods/ts/rules/defaults.yaml
@@ -13,11 +13,18 @@
Upgrades:
500: rank, firepower, damage, speed, reload
1000: rank, firepower, damage, speed, reload, selfheal, eliteweapon
- GainsStatUpgrades:
- FirepowerModifier: 110, 130
- DamageModifier: 83, 66
- SpeedModifier: 120, 150
- ReloadModifier: 90, 75
+ FirepowerMultiplier@EXPERIENCE:
+ UpgradeTypes: firepower
+ Modifier: 110, 130
+ DamageMultiplier@EXPERIENCE:
+ UpgradeTypes: damage
+ Modifier: 83, 66
+ SpeedMultiplier@EXPERIENCE:
+ UpgradeTypes: speed
+ Modifier: 120, 150
+ ReloadDelayMultiplier@EXPERIENCE:
+ UpgradeTypes: reload
+ Modifier: 90, 75
SelfHealing@ELITE:
Step: 2
Ticks: 100