diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index 5fff2b49ea..bf6a6dc457 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -15,12 +15,14 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; +using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Text.RegularExpressions; using OpenRA.Graphics; using OpenRA.Primitives; +using OpenRA.Support; namespace OpenRA { @@ -396,6 +398,22 @@ namespace OpenRA return InvalidValueAction(value, fieldType, fieldName); } + else if (fieldType == typeof(BooleanExpression)) + { + if (value != null) + { + try + { + return new BooleanExpression(value); + } + catch (InvalidDataException e) + { + throw new YamlException(e.Message); + } + } + + return InvalidValueAction(value, fieldType, fieldName); + } else if (fieldType.IsEnum) { try diff --git a/OpenRA.Game/Support/BooleanExpression.cs b/OpenRA.Game/Support/BooleanExpression.cs index 00b56181a1..a31b9aa402 100644 --- a/OpenRA.Game/Support/BooleanExpression.cs +++ b/OpenRA.Game/Support/BooleanExpression.cs @@ -18,6 +18,7 @@ namespace OpenRA.Support { public class BooleanExpression { + public readonly string Expression; readonly HashSet variables = new HashSet(); public IEnumerable Variables { get { return variables; } } @@ -68,6 +69,7 @@ namespace OpenRA.Support public BooleanExpression(string expression) { + Expression = expression; var openParens = 0; var closeParens = 0; var tokens = new List(); @@ -220,7 +222,7 @@ namespace OpenRA.Support return new VariableToken(start, expression.Substring(start)); } - static bool ParseSymbol(VariableToken t, Dictionary symbols) + static bool ParseSymbol(VariableToken t, IReadOnlyDictionary symbols) { bool value; symbols.TryGetValue(t.Symbol, out value); @@ -269,7 +271,7 @@ namespace OpenRA.Support yield return s.Pop(); } - public bool Evaluate(Dictionary symbols) + public bool Evaluate(IReadOnlyDictionary symbols) { var s = new Stack(); foreach (var t in postfix) diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs index ebdd6a346d..03cc029aaf 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs @@ -47,7 +47,11 @@ namespace OpenRA.Mods.Cnc.Traits public readonly string OnFireSound = null; public override object Create(ActorInitializer init) { return new IonCannonPower(init.Self, this); } - public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; } + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; + base.RulesetLoaded(rules, ai); + } } class IonCannonPower : SupportPower diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index 4f4ac164f7..92c7b711c5 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.AI @@ -193,7 +194,7 @@ namespace OpenRA.Mods.Common.AI bool HasSufficientPowerForActor(ActorInfo actorInfo) { - return (actorInfo.TraitInfos().Where(i => i.UpgradeMinEnabledLevel < 1) + return (actorInfo.TraitInfos().Where(i => i.EnabledByDefault) .Sum(p => p.Amount) + playerPower.ExcessPower) >= ai.Info.MinimumExcessPower; } @@ -203,12 +204,12 @@ namespace OpenRA.Mods.Common.AI // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding(ai.Info.BuildingCommonNames.Power, buildableThings, - a => a.TraitInfos().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); + a => a.TraitInfos().Where(i => i.EnabledByDefault).Sum(p => p.Amount)); // First priority is to get out of a low power situation if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) { - if (power != null && power.TraitInfos().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) + if (power != null && power.TraitInfos().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return power; @@ -314,7 +315,7 @@ namespace OpenRA.Mods.Common.AI if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) { // Try building a power plant instead - if (power != null && power.TraitInfos().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) + if (power != null && power.TraitInfos().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0) { if (playerPower.PowerOutageRemainingTicks > 0) HackyAI.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); diff --git a/OpenRA.Mods.Common/Lint/CheckUpgrades.cs b/OpenRA.Mods.Common/Lint/CheckUpgrades.cs deleted file mode 100644 index 88b7cd76db..0000000000 --- a/OpenRA.Mods.Common/Lint/CheckUpgrades.cs +++ /dev/null @@ -1,166 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRA.Mods.Common.Traits; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Lint -{ - public class CheckUpgrades : ILintRulesPass - { - public void Run(Action emitError, Action emitWarning, Ruleset rules) - { - CheckUpgradesValidity(emitError, rules); - CheckUpgradesUsage(emitError, emitWarning, rules); - } - - static void CheckUpgradesValidity(Action emitError, Ruleset rules) - { - var upgradesGranted = GetAllGrantedUpgrades(emitError, rules).ToHashSet(); - - foreach (var actorInfo in rules.Actors) - { - if (actorInfo.Key.StartsWith("^")) - continue; - - foreach (var trait in actorInfo.Value.TraitInfos()) - { - var fields = trait.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(trait, field, emitError); - foreach (var value in values) - { - if (!upgradesGranted.Contains(value)) - emitError("Actor type `{0}` uses upgrade `{1}` that is not granted by anything!".F(actorInfo.Key, value)); - - if (actorInfo.Value.TraitInfoOrDefault() == null) - emitError("Actor type `{0}` uses upgrade `{1}`, but doesn't have the UpgradeManager trait.".F(actorInfo.Key, value)); - } - } - } - } - } - - static void CheckUpgradesUsage(Action emitError, Action emitWarning, Ruleset rules) - { - var upgradesUsed = GetAllUsedUpgrades(emitError, rules).ToHashSet(); - - // Check all upgrades granted by traits. - foreach (var actorInfo in rules.Actors) - { - if (actorInfo.Key.StartsWith("^")) - continue; - - foreach (var trait in actorInfo.Value.TraitInfos()) - { - var fields = trait.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(trait, field, emitError); - foreach (var value in values.Where(x => !upgradesUsed.Contains(x))) - emitWarning("Actor type `{0}` grants upgrade `{1}` that is not used by anything!".F(actorInfo.Key, value)); - } - } - } - - // Check all upgrades granted by warheads. - foreach (var weapon in rules.Weapons) - { - foreach (var warhead in weapon.Value.Warheads) - { - var fields = warhead.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(warhead, field, emitError); - foreach (var value in values.Where(x => !upgradesUsed.Contains(x))) - emitWarning("Weapon type `{0}` grants upgrade `{1}` that is not used by anything!".F(weapon.Key, value)); - } - } - } - } - - static IEnumerable GetAllGrantedUpgrades(Action emitError, Ruleset rules) - { - // Get all upgrades granted by traits. - foreach (var actorInfo in rules.Actors) - { - foreach (var trait in actorInfo.Value.TraitInfos()) - { - var fields = trait.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(trait, field, emitError); - foreach (var value in values) - yield return value; - } - } - } - - // Get all upgrades granted by warheads. - foreach (var weapon in rules.Weapons) - { - foreach (var warhead in weapon.Value.Warheads) - { - var fields = warhead.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(warhead, field, emitError); - foreach (var value in values) - yield return value; - } - } - } - - // TODO: HACK because GainsExperience grants upgrades differently to most other sources. - var gainsExperience = rules.Actors.SelectMany(x => x.Value.TraitInfos() - .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); - - foreach (var upgrade in gainsExperience) - yield return upgrade; - - // TODO: HACK because Pluggable grants upgrades differently to most other sources. - var pluggable = rules.Actors.SelectMany(x => x.Value.TraitInfos() - .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); - - foreach (var upgrade in pluggable) - yield return upgrade; - } - - static IEnumerable GetAllUsedUpgrades(Action emitError, Ruleset rules) - { - foreach (var actorInfo in rules.Actors) - { - foreach (var trait in actorInfo.Value.TraitInfos()) - { - var fields = trait.GetType().GetFields(); - foreach (var field in fields.Where(x => x.HasAttribute())) - { - var values = LintExts.GetFieldValues(trait, field, emitError); - foreach (var value in values) - yield return value; - } - } - } - - // TODO: HACK because GainsExperience and GainsStatUpgrades do not play by the rules... - // We assume everything GainsExperience grants is used by GainsStatUpgrade - var gainsExperience = rules.Actors.SelectMany(x => x.Value.TraitInfos() - .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); - - foreach (var upgrade in gainsExperience) - yield return upgrade; - } - } -} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Lint/LintExts.cs b/OpenRA.Mods.Common/Lint/LintExts.cs index cfebb72fae..582573790e 100644 --- a/OpenRA.Mods.Common/Lint/LintExts.cs +++ b/OpenRA.Mods.Common/Lint/LintExts.cs @@ -11,7 +11,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using OpenRA.Support; namespace OpenRA.Mods.Common.Lint { @@ -26,8 +28,13 @@ namespace OpenRA.Mods.Common.Lint return (string[])fieldInfo.GetValue(ruleInfo); if (type == typeof(HashSet)) return (HashSet)fieldInfo.GetValue(ruleInfo); + if (type == typeof(BooleanExpression)) + { + var expr = (BooleanExpression)fieldInfo.GetValue(ruleInfo); + return expr != null ? expr.Variables : Enumerable.Empty(); + } - emitError("Bad type for reference on {0}.{1}. Supported types: string, string[], HashSet" + emitError("Bad type for reference on {0}.{1}. Supported types: string, string[], HashSet, BooleanExpression" .F(ruleInfo.GetType().Name, fieldInfo.Name)); return new string[] { }; diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index da6755fc87..982265900a 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -181,7 +181,6 @@ - @@ -357,7 +356,6 @@ - @@ -443,7 +441,6 @@ - diff --git a/OpenRA.Mods.Common/Traits/Armament.cs b/OpenRA.Mods.Common/Traits/Armament.cs index fc09205ed1..06f493fc13 100644 --- a/OpenRA.Mods.Common/Traits/Armament.cs +++ b/OpenRA.Mods.Common/Traits/Armament.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits } [Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")] - public class ArmamentInfo : UpgradableTraitInfo, IRulesetLoaded, Requires + public class ArmamentInfo : UpgradableTraitInfo, Requires { public readonly string Name = "primary"; @@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new Armament(init.Self, this); } - public void RulesetLoaded(Ruleset rules, ActorInfo ai) + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo weaponInfo; @@ -91,10 +91,12 @@ namespace OpenRA.Mods.Common.Traits ModifiedRange = new WDist(Util.ApplyPercentageModifiers( WeaponInfo.Range.Length, ai.TraitInfos().Select(m => m.GetRangeModifierDefault()))); + + base.RulesetLoaded(rules, ai); } } - public class Armament : UpgradableTrait, INotifyCreated, ITick, IExplodeModifier + public class Armament : UpgradableTrait, ITick, IExplodeModifier { public readonly WeaponInfo Weapon; public readonly Barrel[] Barrels; @@ -139,12 +141,14 @@ namespace OpenRA.Mods.Common.Traits return new WDist(Util.ApplyPercentageModifiers(Weapon.Range.Length, rangeModifiers)); } - protected virtual void Created(Actor self) + protected override void Created(Actor self) { turret = self.TraitsImplementing().FirstOrDefault(t => t.Name == Info.Turret); ammoPool = self.TraitsImplementing().FirstOrDefault(la => la.Info.Name == Info.AmmoPoolName); coords = self.Trait(); rangeModifiers = self.TraitsImplementing().ToArray().Select(m => m.GetRangeModifier()); + + base.Created(self); } protected virtual void Tick(Actor self) @@ -168,12 +172,6 @@ namespace OpenRA.Mods.Common.Traits delayedActions.RemoveAll(a => a.First <= 0); } - void INotifyCreated.Created(Actor self) - { - // Split into a protected method to allow subclassing - Created(self); - } - void ITick.Tick(Actor self) { // Split into a protected method to allow subclassing diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs b/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs index ddf2ed53b6..5e314d50e8 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs @@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits [PaletteReference] public readonly string MuzzlePalette = "effect"; public override object Create(ActorInitializer init) { return new AttackGarrisoned(init.Self, this); } - public void RulesetLoaded(Ruleset rules, ActorInfo ai) + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { if (PortOffsets.Length == 0) throw new YamlException("PortOffsets must have at least one entry."); @@ -67,6 +67,8 @@ namespace OpenRA.Mods.Common.Traits Cone = PortCones[i], }; } + + base.RulesetLoaded(rules, ai); } } diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index 5e66f98e38..0ce4b9b853 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -20,7 +20,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("This actor can transport Passenger actors.")] - public class CargoInfo : ITraitInfo, Requires, Requires + public class CargoInfo : ITraitInfo, Requires { [Desc("The maximum sum of Passenger.Weight that this actor can support.")] public readonly int MaxWeight = 0; @@ -67,7 +67,6 @@ namespace OpenRA.Mods.Common.Traits { public readonly CargoInfo Info; readonly Actor self; - readonly UpgradeManager upgradeManager; readonly Stack cargo = new Stack(); readonly HashSet reserves = new HashSet(); readonly Lazy facing; @@ -76,6 +75,7 @@ namespace OpenRA.Mods.Common.Traits int totalWeight = 0; int reservedWeight = 0; Aircraft aircraft; + UpgradeManager upgradeManager; CPos currentCell; public IEnumerable CurrentAdjacentCells { get; private set; } @@ -89,7 +89,6 @@ namespace OpenRA.Mods.Common.Traits Info = info; Unloading = false; checkTerrainType = info.UnloadTerrainTypes.Count > 0; - upgradeManager = self.Trait(); if (init.Contains()) { @@ -127,6 +126,7 @@ namespace OpenRA.Mods.Common.Traits void INotifyCreated.Created(Actor self) { aircraft = self.TraitOrDefault(); + upgradeManager = self.Trait(); } static int GetWeight(Actor a) { return a.Info.TraitInfo().Weight; } diff --git a/OpenRA.Mods.Common/Traits/Carryable.cs b/OpenRA.Mods.Common/Traits/Carryable.cs index ba26914040..7674266188 100644 --- a/OpenRA.Mods.Common/Traits/Carryable.cs +++ b/OpenRA.Mods.Common/Traits/Carryable.cs @@ -15,7 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Can be carried by actors with the `Carryall` trait.")] - public class CarryableInfo : ITraitInfo, Requires + public class CarryableInfo : ITraitInfo { [UpgradeGrantedReference] [Desc("The upgrades to grant to self while waiting or being carried.")] @@ -27,10 +27,10 @@ namespace OpenRA.Mods.Common.Traits public virtual object Create(ActorInitializer init) { return new Carryable(init.Self, this); } } - public class Carryable + public class Carryable : INotifyCreated { readonly CarryableInfo info; - readonly UpgradeManager upgradeManager; + UpgradeManager upgradeManager; public Actor Carrier { get; private set; } public bool Reserved { get { return state != State.Free; } } @@ -44,6 +44,10 @@ namespace OpenRA.Mods.Common.Traits public Carryable(Actor self, CarryableInfo info) { this.info = info; + } + + void INotifyCreated.Created(Actor self) + { upgradeManager = self.Trait(); } diff --git a/OpenRA.Mods.Common/Traits/KillsSelf.cs b/OpenRA.Mods.Common/Traits/KillsSelf.cs index 1411d8364f..65365028a5 100644 --- a/OpenRA.Mods.Common/Traits/KillsSelf.cs +++ b/OpenRA.Mods.Common/Traits/KillsSelf.cs @@ -29,10 +29,10 @@ namespace OpenRA.Mods.Common.Traits public void AddedToWorld(Actor self) { if (!IsTraitDisabled) - UpgradeEnabled(self); + TraitEnabled(self); } - protected override void UpgradeEnabled(Actor self) + protected override void TraitEnabled(Actor self) { if (self.IsDead) return; diff --git a/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs index 6ba58d0921..8d133ea0da 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/DamageMultiplier.cs @@ -13,19 +13,25 @@ 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.", + [Desc("Modifies the damage applied to this actor.", "Use 0 to make actor invulnerable.")] - public class DamageMultiplierInfo : UpgradeMultiplierTraitInfo + public class DamageMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new DamageMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + + public override object Create(ActorInitializer init) { return new DamageMultiplier(this); } } - public class DamageMultiplier : UpgradeMultiplierTrait, IDamageModifier + public class DamageMultiplier : UpgradableTrait, IDamageModifier { - public DamageMultiplier(DamageMultiplierInfo info, string actorType) - : base(info, "DamageMultiplier", actorType) { } + public DamageMultiplier(DamageMultiplierInfo info) + : base(info) { } - int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage) { return GetModifier(); } + int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage) + { + return IsTraitDisabled ? 100 : Info.Modifier; + } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs index cead62c54b..8248089e0c 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/FirepowerMultiplier.cs @@ -11,17 +11,21 @@ namespace OpenRA.Mods.Common.Traits { - [Desc("The firepower of this actor is multiplied based on upgrade level if specified.")] - public class FirepowerMultiplierInfo : UpgradeMultiplierTraitInfo + [Desc("Modifies the damage applied by this actor.")] + public class FirepowerMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new FirepowerMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + + public override object Create(ActorInitializer init) { return new FirepowerMultiplier(this); } } - public class FirepowerMultiplier : UpgradeMultiplierTrait, IFirepowerModifier + public class FirepowerMultiplier : UpgradableTrait, IFirepowerModifier { - public FirepowerMultiplier(FirepowerMultiplierInfo info, string actorType) - : base(info, "FirepowerMultiplier", actorType) { } + public FirepowerMultiplier(FirepowerMultiplierInfo info) + : base(info) { } - int IFirepowerModifier.GetFirepowerModifier() { return GetModifier(); } + int IFirepowerModifier.GetFirepowerModifier() { return IsTraitDisabled ? 100 : Info.Modifier; } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs index c65d7e1f38..c3dc05bd13 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/InaccuracyMultiplier.cs @@ -11,17 +11,21 @@ namespace OpenRA.Mods.Common.Traits { - [Desc("The inaccuracy of this actor is multiplied based on upgrade level if specified.")] - public class InaccuracyMultiplierInfo : UpgradeMultiplierTraitInfo + [Desc("Modifies the inaccuracy of weapons fired by this actor.")] + public class InaccuracyMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new InaccuracyMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + + public override object Create(ActorInitializer init) { return new InaccuracyMultiplier(this); } } - public class InaccuracyMultiplier : UpgradeMultiplierTrait, IInaccuracyModifier + public class InaccuracyMultiplier : UpgradableTrait, IInaccuracyModifier { - public InaccuracyMultiplier(InaccuracyMultiplierInfo info, string actorType) - : base(info, "InaccuracyMultiplier", actorType) { } + public InaccuracyMultiplier(InaccuracyMultiplierInfo info) + : base(info) { } - int IInaccuracyModifier.GetInaccuracyModifier() { return GetModifier(); } + int IInaccuracyModifier.GetInaccuracyModifier() { return IsTraitDisabled ? 100 : Info.Modifier; } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs index 04ac350055..68d493b6bb 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/PowerMultiplier.cs @@ -13,22 +13,30 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - [Desc("The power usage/output of this actor is multiplied based on upgrade level if specified.")] - public class PowerMultiplierInfo : UpgradeMultiplierTraitInfo + [Desc("Modifies the power usage/output of this actor.")] + public class PowerMultiplierInfo : UpgradableTraitInfo { + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + public override object Create(ActorInitializer init) { return new PowerMultiplier(init.Self, this); } } - public class PowerMultiplier : UpgradeMultiplierTrait, IPowerModifier, INotifyOwnerChanged + public class PowerMultiplier : UpgradableTrait, IPowerModifier, INotifyOwnerChanged { PowerManager power; public PowerMultiplier(Actor self, PowerMultiplierInfo info) - : base(info, "PowerMultiplier", self.Info.Name) { power = self.Owner.PlayerActor.Trait(); } + : base(info) + { + power = self.Owner.PlayerActor.Trait(); + } - int IPowerModifier.GetPowerModifier() { return GetModifier(); } + protected override void TraitEnabled(Actor self) { power.UpdateActor(self); } + protected override void TraitDisabled(Actor self) { power.UpdateActor(self); } - protected override void Update(Actor self) { power.UpdateActor(self); } + int IPowerModifier.GetPowerModifier() { return IsTraitDisabled ? 100 : Info.Modifier; } void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { diff --git a/OpenRA.Mods.Common/Traits/Multipliers/RangeMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/RangeMultiplier.cs index df857f759b..11979e3ad6 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/RangeMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/RangeMultiplier.cs @@ -11,22 +11,21 @@ namespace OpenRA.Mods.Common.Traits { - [Desc("Range of this actor is multiplied based on upgrade level.")] - public class RangeMultiplierInfo : UpgradeMultiplierTraitInfo, IRangeModifierInfo + [Desc("Modifies the range of weapons fired by this actor.")] + public class RangeMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new RangeMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; - int IRangeModifierInfo.GetRangeModifierDefault() - { - return BaseLevel > 0 || UpgradeTypes.Length == 0 ? 100 : Modifier[0]; - } + public override object Create(ActorInitializer init) { return new RangeMultiplier(this); } } - public class RangeMultiplier : UpgradeMultiplierTrait, IRangeModifier + public class RangeMultiplier : UpgradableTrait, IRangeModifierInfo { - public RangeMultiplier(RangeMultiplierInfo info, string actorType) - : base(info, "RangeMultiplier", actorType) { } + public RangeMultiplier(RangeMultiplierInfo info) + : base(info) { } - int IRangeModifier.GetRangeModifier() { return GetModifier(); } + int IRangeModifierInfo.GetRangeModifierDefault() { return IsTraitDisabled ? 100 : Info.Modifier; } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs index 2a3806ab93..793367d7d3 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/ReloadDelayMultiplier.cs @@ -11,17 +11,21 @@ namespace OpenRA.Mods.Common.Traits { - [Desc("The reloading time of this actor is multiplied based on upgrade level if specified.")] - public class ReloadDelayMultiplierInfo : UpgradeMultiplierTraitInfo + [Desc("Modifies the reload time of weapons fired by this actor.")] + public class ReloadDelayMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new ReloadDelayMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + + public override object Create(ActorInitializer init) { return new ReloadDelayMultiplier(this); } } - public class ReloadDelayMultiplier : UpgradeMultiplierTrait, IReloadModifier + public class ReloadDelayMultiplier : UpgradableTrait, IReloadModifier { - public ReloadDelayMultiplier(ReloadDelayMultiplierInfo info, string actorType) - : base(info, "ReloadDelayMultiplier", actorType) { } + public ReloadDelayMultiplier(ReloadDelayMultiplierInfo info) + : base(info) { } - int IReloadModifier.GetReloadModifier() { return GetModifier(); } + int IReloadModifier.GetReloadModifier() { return IsTraitDisabled ? 100 : Info.Modifier; } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs index 7528e8707a..2b885d4b25 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/SpeedMultiplier.cs @@ -11,17 +11,21 @@ namespace OpenRA.Mods.Common.Traits { - [Desc("The speed of this actor is multiplied based on upgrade level if specified.")] - public class SpeedMultiplierInfo : UpgradeMultiplierTraitInfo + [Desc("Modifies the movement speed of this actor.")] + public class SpeedMultiplierInfo : UpgradableTraitInfo { - public override object Create(ActorInitializer init) { return new SpeedMultiplier(this, init.Self.Info.Name); } + [FieldLoader.Require] + [Desc("Percentage modifier to apply.")] + public readonly int Modifier = 100; + + public override object Create(ActorInitializer init) { return new SpeedMultiplier(this); } } - public class SpeedMultiplier : UpgradeMultiplierTrait, ISpeedModifier + public class SpeedMultiplier : UpgradableTrait, ISpeedModifier { - public SpeedMultiplier(SpeedMultiplierInfo info, string actorType) - : base(info, "SpeedMultiplier", actorType) { } + public SpeedMultiplier(SpeedMultiplierInfo info) + : base(info) { } - int ISpeedModifier.GetSpeedModifier() { return GetModifier(); } + int ISpeedModifier.GetSpeedModifier() { return IsTraitDisabled ? 100 : Info.Modifier; } } } diff --git a/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs b/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs deleted file mode 100644 index 8afddb927e..0000000000 --- a/OpenRA.Mods.Common/Traits/Multipliers/UpgradableMultiplierTrait.cs +++ /dev/null @@ -1,76 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - public abstract class UpgradeMultiplierTraitInfo : ITraitInfo - { - [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 object Create(ActorInitializer init); - } - - 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 ArgumentException("No modifiers in " + modifierType + " for " + actorType); - this.info = info; - IsTraitDisabled = info.UpgradeTypes.Length > 0 && info.BaseLevel > 0; - level = IsTraitDisabled ? 0 : info.BaseLevel; - } - - public bool AcceptsUpgradeLevel(Actor self, string type, int level) - { - return level < info.Modifier.Length + info.BaseLevel; - } - - // Override to receive 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/Power/CanPowerDown.cs b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs index 0cbb9d9ef9..6b5953a3b8 100644 --- a/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs +++ b/OpenRA.Mods.Common/Traits/Power/CanPowerDown.cs @@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits power = newOwner.PlayerActor.Trait(); } - protected override void UpgradeDisabled(Actor self) + protected override void TraitDisabled(Actor self) { if (!disabled || !Info.CancelWhenDisabled) return; diff --git a/OpenRA.Mods.Common/Traits/Power/Power.cs b/OpenRA.Mods.Common/Traits/Power/Power.cs index 839165a668..148d614bc1 100644 --- a/OpenRA.Mods.Common/Traits/Power/Power.cs +++ b/OpenRA.Mods.Common/Traits/Power/Power.cs @@ -41,8 +41,8 @@ namespace OpenRA.Mods.Common.Traits powerModifiers = Exts.Lazy(() => self.TraitsImplementing().ToArray()); } - protected override void UpgradeEnabled(Actor self) { PlayerPower.UpdateActor(self); } - protected override void UpgradeDisabled(Actor self) { PlayerPower.UpdateActor(self); } + protected override void TraitEnabled(Actor self) { PlayerPower.UpdateActor(self); } + protected override void TraitDisabled(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) diff --git a/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs b/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs index 824ecfb9ba..8fc813c2de 100644 --- a/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs +++ b/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs @@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits.Render public override object Create(ActorInitializer init) { return new LeavesTrails(init.Self, this); } } - public class LeavesTrails : UpgradableTrait, ITick, INotifyCreated + public class LeavesTrails : UpgradableTrait, ITick { BodyOrientation body; IFacing facing; @@ -75,12 +75,14 @@ namespace OpenRA.Mods.Common.Traits.Render } WPos cachedPosition; - public void Created(Actor self) + protected override void Created(Actor self) { body = self.Trait(); facing = self.TraitOrDefault(); cachedFacing = facing != null ? facing.Facing : 0; cachedPosition = self.CenterPosition; + + base.Created(self); } int ticks; @@ -132,7 +134,7 @@ namespace OpenRA.Mods.Common.Traits.Render } } - protected override void UpgradeEnabled(Actor self) + protected override void TraitEnabled(Actor self) { cachedPosition = self.CenterPosition; } diff --git a/OpenRA.Mods.Common/Traits/Render/RenderRangeCircle.cs b/OpenRA.Mods.Common/Traits/Render/RenderRangeCircle.cs index f2a919b486..10d9d5e7b2 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderRangeCircle.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderRangeCircle.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits.Render // Defer this lookup until we really need it to ensure we get the correct value. range = Exts.Lazy(() => { - var armaments = ai.TraitInfos().Where(a => a.UpgradeMinEnabledLevel == 0); + var armaments = ai.TraitInfos().Where(a => a.EnabledByDefault); if (!armaments.Any()) return FallbackRange; diff --git a/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs index 520ef26767..ec779e2cc7 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithIdleOverlay.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; if (Palette != null) diff --git a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs index f862814e4d..ad1eaa0195 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class WithInfantryBody : UpgradableTrait, ITick, INotifyAttack, INotifyIdle, INotifyCreated + public class WithInfantryBody : UpgradableTrait, ITick, INotifyAttack, INotifyIdle { readonly IMove move; protected readonly Animation DefaultAnimation; @@ -90,9 +90,11 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public void Created(Actor self) + protected override void Created(Actor self) { rsm = self.TraitOrDefault(); + + base.Created(self); } protected virtual string NormalizeInfantrySequence(Actor self, string baseSequence) diff --git a/OpenRA.Mods.Common/Traits/Render/WithParachute.cs b/OpenRA.Mods.Common/Traits/Render/WithParachute.cs index 2c5ae38056..aa3692950b 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithParachute.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithParachute.cs @@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; if (image == null) @@ -128,7 +128,7 @@ namespace OpenRA.Mods.Common.Traits.Render rs.Add(anim, info.Palette, info.IsPlayerPalette); } - protected override void UpgradeEnabled(Actor self) + protected override void TraitEnabled(Actor self) { if (info.Image == null) return; @@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Traits.Render anim.Animation.PlayThen(info.OpeningSequence, () => anim.Animation.PlayRepeating(info.Sequence)); } - protected override void UpgradeDisabled(Actor self) + protected override void TraitDisabled(Actor self) { if (info.Image == null) return; diff --git a/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs deleted file mode 100644 index 4a23787c42..0000000000 --- a/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs +++ /dev/null @@ -1,28 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -namespace OpenRA.Mods.Common.Traits.Render -{ - public class WithRankDecorationInfo : WithDecorationInfo - { - public override object Create(ActorInitializer init) { return new WithRankDecoration(init.Self, this); } - } - - public class WithRankDecoration : WithDecoration - { - public WithRankDecoration(Actor self, WithRankDecorationInfo info) : base(self, info) { } - - protected override void UpgradeLevelChanged(Actor self, int oldLevel, int newLevel) - { - Anim.PlayFetchIndex(Info.Sequence, () => newLevel - 1); - } - } -} diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs index 25236c18ef..894ed089c7 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; var body = init.Actor.TraitInfo(); diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs index 2be28793f0..26bc9af9cc 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs @@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits.Render public virtual IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; var anim = new Animation(init.World, image); diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs index 17b5603b2b..9bc4e41e79 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs @@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; var body = init.Actor.TraitInfo(); diff --git a/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs index 6f6c242225..d78a144fd7 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs @@ -20,7 +20,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [Desc("Displays a text overlay relative to the selection box.")] - public class WithTextDecorationInfo : UpgradableTraitInfo, IRulesetLoaded + public class WithTextDecorationInfo : UpgradableTraitInfo { [FieldLoader.Require] [Translate] public readonly string Text = null; @@ -47,10 +47,12 @@ namespace OpenRA.Mods.Common.Traits.Render public override object Create(ActorInitializer init) { return new WithTextDecoration(init.Self, this); } - void IRulesetLoaded.RulesetLoaded(Ruleset rules, ActorInfo info) + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { if (!Game.ModData.Manifest.Fonts.ContainsKey(Font)) throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font)); + + base.RulesetLoaded(rules, ai); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs index edfc419ce2..d531e6fa99 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewVoxels( ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func orientation, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; var body = init.Actor.TraitInfo(); diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs index 5a85056013..421e833665 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs @@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable RenderPreviewVoxels( ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func orientation, int facings, PaletteReference p) { - if (UpgradeMinEnabledLevel > 0) + if (!EnabledByDefault) yield break; var body = init.Actor.TraitInfo(); diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs index 2d3423b689..5630466860 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs @@ -74,7 +74,11 @@ namespace OpenRA.Mods.Common.Traits public WeaponInfo WeaponInfo { get; private set; } public override object Create(ActorInitializer init) { return new NukePower(init.Self, this); } - public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[MissileWeapon.ToLowerInvariant()]; } + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + WeaponInfo = rules.Weapons[MissileWeapon.ToLowerInvariant()]; + base.RulesetLoaded(rules, ai); + } } class NukePower : SupportPower diff --git a/OpenRA.Mods.Common/Traits/Targetable.cs b/OpenRA.Mods.Common/Traits/Targetable.cs index a955e23ba0..11a32908ee 100644 --- a/OpenRA.Mods.Common/Traits/Targetable.cs +++ b/OpenRA.Mods.Common/Traits/Targetable.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new Targetable(init.Self, this); } } - public class Targetable : UpgradableTrait, ITargetable, INotifyCreated + public class Targetable : UpgradableTrait, ITargetable { protected static readonly string[] None = new string[] { }; protected Cloak[] cloaks; @@ -35,9 +35,11 @@ namespace OpenRA.Mods.Common.Traits public Targetable(Actor self, TargetableInfo info) : base(info) { } - void INotifyCreated.Created(Actor self) + protected override void Created(Actor self) { cloaks = self.TraitsImplementing().ToArray(); + + base.Created(self); } public virtual bool TargetableBy(Actor self, Actor viewer) diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs index 02d94da697..527679875f 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs @@ -10,30 +10,32 @@ #endregion using System.Collections.Generic; +using System.Linq; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { /// Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.) - public abstract class UpgradableTraitInfo : IUpgradableInfo + public abstract class UpgradableTraitInfo : IConditionConsumerInfo, IRulesetLoaded { + static readonly IReadOnlyDictionary NoConditions = new ReadOnlyDictionary(new Dictionary()); + [UpgradeUsedReference] - [Desc("The upgrade types which can enable or disable this trait.")] - public readonly HashSet UpgradeTypes = new HashSet(); - - [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; + [Desc("Boolean expression defining the condition to enable this trait.")] + public readonly BooleanExpression RequiresCondition = null; public abstract object Create(ActorInitializer init); + + // HACK: A shim for all the ActorPreview code that used to query UpgradeMinEnabledLevel directly + // This can go away after we introduce an InitialUpgrades ActorInit and have the traits query the + // condition directly + public bool EnabledByDefault { get; private set; } + + public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + EnabledByDefault = RequiresCondition != null ? RequiresCondition.Evaluate(NoConditions) : true; + } } /// @@ -41,50 +43,59 @@ namespace OpenRA.Mods.Common.Traits /// 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 abstract class UpgradableTrait : IConditionConsumer, IDisabledTrait, INotifyCreated, ISync where InfoType : UpgradableTraitInfo { public readonly InfoType Info; - public IEnumerable UpgradeTypes { get { return Info.UpgradeTypes; } } + + IEnumerable IConditionConsumer.Conditions + { + get + { + if (Info.RequiresCondition != null) + return Info.RequiresCondition.Variables; + + return Enumerable.Empty(); + } + } + [Sync] public bool IsTraitDisabled { get; private set; } public UpgradableTrait(InfoType info) { Info = info; - IsTraitDisabled = info.UpgradeTypes != null && info.UpgradeTypes.Count > 0 && info.UpgradeMinEnabledLevel > 0; + + // Conditional traits will be enabled (if appropriate) by the UpgradeManager + // calling IConditionConsumer.ConditionsChanged at the end of INotifyCreated. + IsTraitDisabled = Info.RequiresCondition != null; } - public bool AcceptsUpgradeLevel(Actor self, string type, int level) + protected virtual void Created(Actor self) { - return level > 0 && level <= Info.UpgradeMaxAcceptedLevel; + if (Info.RequiresCondition == null) + TraitEnabled(self); } - public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel) - { - if (!Info.UpgradeTypes.Contains(type)) - return; + void INotifyCreated.Created(Actor self) { Created(self); } - // Restrict the levels to the allowed range - oldLevel = oldLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); - newLevel = newLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); - if (oldLevel == newLevel) + void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary conditions) + { + if (Info.RequiresCondition == null) return; var wasDisabled = IsTraitDisabled; - IsTraitDisabled = newLevel < Info.UpgradeMinEnabledLevel || newLevel > Info.UpgradeMaxEnabledLevel; - UpgradeLevelChanged(self, oldLevel, newLevel); + IsTraitDisabled = !Info.RequiresCondition.Evaluate(conditions); if (IsTraitDisabled != wasDisabled) { if (wasDisabled) - UpgradeEnabled(self); + TraitEnabled(self); else - UpgradeDisabled(self); + TraitDisabled(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) { } + // Subclasses can add condition support by querying IsTraitDisabled and/or overriding these methods. + protected virtual void TraitEnabled(Actor self) { } + protected virtual void TraitDisabled(Actor self) { } } } diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs index 81d2e0e4ad..d7a07ef96e 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs @@ -12,49 +12,44 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Attach this to a unit to enable dynamic upgrades by warheads, experience, crates, support powers, etc.")] - public class UpgradeManagerInfo : TraitInfo, IRulesetLoaded - { - public void RulesetLoaded(Ruleset rules, ActorInfo info) - { - if (!info.Name.StartsWith("^") && !info.TraitInfos().Any()) - throw new YamlException( - "There are no upgrades to be managed for actor '{0}'. You are either missing some upgradeable traits, or this UpgradeManager trait is not required.".F( - info.Name)); - } - } + public class UpgradeManagerInfo : TraitInfo, Requires { } public class UpgradeManager : INotifyCreated, ITick { - class TimedUpgrade + /// Value used to represent an invalid token. + public static readonly int InvalidConditionToken = -1; + + class TimedCondition { - public class UpgradeSource + public class ConditionSource { public readonly object Source; public int Remaining; - public UpgradeSource(int duration, object source) + public ConditionSource(int duration, object source) { Remaining = duration; Source = source; } } - public readonly string Upgrade; + public readonly string Condition; public readonly int Duration; - public readonly HashSet Sources; + public readonly HashSet Sources; public int Remaining; // Equal to maximum of all Sources.Remaining - public TimedUpgrade(string upgrade, int duration, object source) + public TimedCondition(string condition, int duration, object source) { - Upgrade = upgrade; + Condition = condition; Duration = duration; Remaining = duration; - Sources = new HashSet { new UpgradeSource(duration, source) }; + Sources = new HashSet { new ConditionSource(duration, source) }; } public void Tick() @@ -65,29 +60,124 @@ namespace OpenRA.Mods.Common.Traits } } - class UpgradeState + class ConditionState { - public readonly List Traits = new List(); - public readonly List Sources = new List(); + /// Traits that have registered to be notified when this condition changes. + public readonly List Consumers = new List(); + + /// Unique integers identifying granted instances of the condition. + public readonly HashSet Tokens = new HashSet(); + + /// External callbacks that are to be executed when a timed condition changes. public readonly List> Watchers = new List>(); } - readonly List timedUpgrades = new List(); - readonly Dictionary levels = new Dictionary(); - Dictionary upgrades; + readonly List timedConditions = new List(); + + Dictionary state; + + /// Each granted condition receives a unique token that is used when revoking. + Dictionary tokens = new Dictionary(); + + int nextToken = 1; + + /// Temporary shim between the old and new upgrade/condition grant and revoke methods. + Dictionary, int> objectTokenShim = new Dictionary, int>(); + + /// Cache of condition -> enabled state for quick evaluation of boolean conditions. + Dictionary conditionCache = new Dictionary(); + + /// Read-only version of conditionCache that is passed to IConditionConsumers. + IReadOnlyDictionary readOnlyConditionCache; void INotifyCreated.Created(Actor self) { - upgrades = new Dictionary(); - foreach (var up in self.TraitsImplementing()) - foreach (var t in up.UpgradeTypes) - upgrades.GetOrAdd(t).Traits.Add(up); + state = new Dictionary(); + readOnlyConditionCache = new ReadOnlyDictionary(conditionCache); + + var allConsumers = new HashSet(); + foreach (var consumer in self.TraitsImplementing()) + { + allConsumers.Add(consumer); + foreach (var condition in consumer.Conditions) + { + state.GetOrAdd(condition).Consumers.Add(consumer); + conditionCache[condition] = false; + } + } + + // Enable any conditions granted during trait setup + foreach (var kv in tokens) + { + ConditionState conditionState; + if (!state.TryGetValue(kv.Value, out conditionState)) + continue; + + conditionState.Tokens.Add(kv.Key); + conditionCache[kv.Value] = conditionState.Tokens.Count > 0; + } + + // Update all traits with their initial condition state + foreach (var consumer in allConsumers) + consumer.ConditionsChanged(self, readOnlyConditionCache); } - void CheckCanManageUpgrades() + void UpdateConditionState(Actor self, string condition, int token, bool isRevoke) { - if (upgrades == null) - throw new InvalidOperationException("Upgrades cannot be managed until the actor has been fully created."); + ConditionState conditionState; + if (!state.TryGetValue(condition, out conditionState)) + return; + + if (isRevoke) + conditionState.Tokens.Remove(token); + else + conditionState.Tokens.Add(token); + + conditionCache[condition] = conditionState.Tokens.Count > 0; + + foreach (var t in conditionState.Consumers) + t.ConditionsChanged(self, readOnlyConditionCache); + } + + /// Grants a specified condition. + /// The token that is used to revoke this condition. + public int GrantCondition(Actor self, string condition) + { + var token = nextToken++; + tokens.Add(token, condition); + + // Conditions may be granted before the state is initialized. + // These conditions will be processed in INotifyCreated.Created. + if (state != null) + UpdateConditionState(self, condition, token, false); + + return token; + } + + /// Revokes a previously granted condition. + /// The invalid token ID + /// The token ID returned by GrantCondition. + public int RevokeCondition(Actor self, int token) + { + string condition; + if (!tokens.TryGetValue(token, out condition)) + throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, self)); + + tokens.Remove(token); + + // Conditions may be granted and revoked before the state is initialized. + if (state != null) + UpdateConditionState(self, condition, token, true); + + return InvalidConditionToken; + } + + #region Shim methods for legacy upgrade granting code + + void CheckCanManageConditions() + { + if (state == null) + throw new InvalidOperationException("Conditions cannot be managed until the actor has been fully created."); } /// Upgrade level increments are limited to dupesAllowed per source, i.e., if a single @@ -98,11 +188,11 @@ namespace OpenRA.Mods.Common.Traits /// remaining upgrade duration will be replaced by the new source. public void GrantTimedUpgrade(Actor self, string upgrade, int duration, object source = null, int dupesAllowed = 1) { - var timed = timedUpgrades.FirstOrDefault(u => u.Upgrade == upgrade); + var timed = timedConditions.FirstOrDefault(u => u.Condition == upgrade); if (timed == null) { - timed = new TimedUpgrade(upgrade, duration, source); - timedUpgrades.Add(timed); + timed = new TimedCondition(upgrade, duration, source); + timedConditions.Add(timed); GrantUpgrade(self, upgrade, timed); return; } @@ -110,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits var srcs = timed.Sources.Where(s => s.Source == source); if (srcs.Count() < dupesAllowed) { - timed.Sources.Add(new TimedUpgrade.UpgradeSource(duration, source)); + timed.Sources.Add(new TimedCondition.ConditionSource(duration, source)); if (AcceptsUpgrade(self, upgrade)) GrantUpgrade(self, upgrade, timed); else @@ -122,108 +212,69 @@ namespace OpenRA.Mods.Common.Traits 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 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) { - CheckCanManageUpgrades(); - - UpgradeState s; - if (!upgrades.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); + CheckCanManageConditions(); + objectTokenShim[Pair.New(source, upgrade)] = GrantCondition(self, upgrade); } public void RevokeUpgrade(Actor self, string upgrade, object source) { - CheckCanManageUpgrades(); - - UpgradeState s; - if (!upgrades.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); + CheckCanManageConditions(); + RevokeCondition(self, objectTokenShim[Pair.New(source, upgrade)]); } /// Returns true if the actor uses the given upgrade. Does not check the actual level of the upgrade. public bool AcknowledgesUpgrade(Actor self, string upgrade) { - CheckCanManageUpgrades(); - return upgrades.ContainsKey(upgrade); + CheckCanManageConditions(); + return state.ContainsKey(upgrade); } /// Returns true only if the actor can accept another level of the upgrade. public bool AcceptsUpgrade(Actor self, string upgrade) { - CheckCanManageUpgrades(); - - UpgradeState s; - if (!upgrades.TryGetValue(upgrade, out s)) + CheckCanManageConditions(); + bool enabled; + if (!conditionCache.TryGetValue(upgrade, out enabled)) return false; - return s.Traits.Any(up => up.AcceptsUpgradeLevel(self, upgrade, GetOverallLevel(up) + 1)); + return !enabled; } public void RegisterWatcher(string upgrade, Action action) { - CheckCanManageUpgrades(); + CheckCanManageConditions(); - UpgradeState s; - if (!upgrades.TryGetValue(upgrade, out s)) + ConditionState s; + if (!state.TryGetValue(upgrade, out s)) return; s.Watchers.Add(action); } - /// Watchers will be receiving notifications while the upgrade's level is nonzero. - /// They will also be provided with the number of ticks before the level returns to zero, + /// Watchers will be receiving notifications while the condition is enabled. + /// They will also be provided with the number of ticks before the condition is disabled, /// as well as the duration in ticks of the timed upgrade (provided in the first call to /// GrantTimedUpgrade). - public void Tick(Actor self) + void ITick.Tick(Actor self) { - CheckCanManageUpgrades(); - - foreach (var u in timedUpgrades) + foreach (var u in timedConditions) { u.Tick(); foreach (var source in u.Sources) if (source.Remaining <= 0) - RevokeUpgrade(self, u.Upgrade, u); + RevokeUpgrade(self, u.Condition, u); u.Sources.RemoveWhere(source => source.Remaining <= 0); - foreach (var a in upgrades[u.Upgrade].Watchers) + foreach (var a in state[u.Condition].Watchers) a(u.Duration, u.Remaining); } - timedUpgrades.RemoveAll(u => u.Remaining <= 0); + timedConditions.RemoveAll(u => u.Remaining <= 0); } + + #endregion } } diff --git a/OpenRA.Mods.Common/Traits/Wanders.cs b/OpenRA.Mods.Common/Traits/Wanders.cs index 02a963cc91..3c0f5983d5 100644 --- a/OpenRA.Mods.Common/Traits/Wanders.cs +++ b/OpenRA.Mods.Common/Traits/Wanders.cs @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new Wanders(init.Self, this); } } - public class Wanders : UpgradableTrait, INotifyCreated, INotifyIdle, INotifyBecomingIdle + public class Wanders : UpgradableTrait, INotifyIdle, INotifyBecomingIdle { readonly Actor self; readonly WandersInfo info; @@ -50,9 +50,11 @@ namespace OpenRA.Mods.Common.Traits effectiveMoveRadius = info.WanderMoveRadius; } - void INotifyCreated.Created(Actor self) + protected override void Created(Actor self) { move = self.Trait() as IResolveOrder; + + base.Created(self); } public virtual void OnBecomingIdle(Actor self) diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 38044bd452..405ba38190 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -103,15 +103,14 @@ namespace OpenRA.Mods.Common.Traits [RequireExplicitImplementation] public interface INotifyPassengerExited { void OnPassengerExited(Actor self, Actor passenger); } - public interface IUpgradable - { - IEnumerable UpgradeTypes { get; } - bool AcceptsUpgradeLevel(Actor self, string type, int level); - void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel); - } + [RequireExplicitImplementation] + public interface IConditionConsumerInfo : ITraitInfo { } - // Implement to construct before UpgradeManager - public interface IUpgradableInfo : ITraitInfo { } + public interface IConditionConsumer + { + IEnumerable Conditions { get; } + void ConditionsChanged(Actor self, IReadOnlyDictionary conditions); + } public interface INotifyHarvesterAction { diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 782bee908a..636f2c55ba 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -435,6 +435,48 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // Rename Replaced upgrade consumers with conditions + if (engineVersion < 20161117) + { + var upgradeTypesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeTypes"); + if (upgradeTypesNode != null) + { + var upgradeMinEnabledLevel = 0; + var upgradeMaxEnabledLevel = int.MaxValue; + var upgradeMaxAcceptedLevel = 1; + var upgradeTypes = FieldLoader.GetValue("", upgradeTypesNode.Value.Value); + var minEnabledNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel"); + if (minEnabledNode != null) + upgradeMinEnabledLevel = FieldLoader.GetValue("", minEnabledNode.Value.Value); + + var maxEnabledNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMaxEnabledLevel"); + if (maxEnabledNode != null) + upgradeMaxEnabledLevel = FieldLoader.GetValue("", maxEnabledNode.Value.Value); + + var maxAcceptedNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMaxAcceptedLevel"); + if (maxAcceptedNode != null) + upgradeMaxAcceptedLevel = FieldLoader.GetValue("", maxAcceptedNode.Value.Value); + + var processed = false; + if (upgradeTypes.Length == 1 && upgradeMinEnabledLevel == 0 && upgradeMaxEnabledLevel == 0 && upgradeMaxAcceptedLevel == 1) + { + node.Value.Nodes.Add(new MiniYamlNode("RequiresCondition", "!" + upgradeTypes.First())); + processed = true; + } + else if (upgradeTypes.Length == 1 && upgradeMinEnabledLevel == 1 && upgradeMaxEnabledLevel == int.MaxValue && upgradeMaxAcceptedLevel == 1) + { + node.Value.Nodes.Add(new MiniYamlNode("RequiresCondition", upgradeTypes.First())); + processed = true; + } + + if (processed) + node.Value.Nodes.RemoveAll(n => n.Key == "UpgradeTypes" || n.Key == "UpgradeMinEnabledLevel" || + n.Key == "UpgradeMaxEnabledLevel" || n.Key == "UpgradeMaxAcceptedLevel"); + else + Console.WriteLine("Unable to automatically migrate {0}:{1} UpgradeTypes to RequiresCondition. This must be corrected manually", parent.Key, node.Key); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs index 9a7243ca9d..bcd799f00b 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs @@ -13,6 +13,7 @@ using System; using System.Drawing; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Support; using OpenRA.Traits; using OpenRA.Widgets; @@ -74,7 +75,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : ""; requiresLabel.GetText = () => requiresString; - var power = actor.TraitInfos().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(i => i.Amount); + var power = actor.TraitInfos().Where(i => i.EnabledByDefault).Sum(i => i.Amount); var powerString = power.ToString(); powerLabel.GetText = () => powerString; powerLabel.GetColor = () => ((pm.PowerProvided - pm.PowerDrained) >= -power || power > 0) diff --git a/OpenRA.Test/OpenRA.Game/BooleanExpressionTest.cs b/OpenRA.Test/OpenRA.Game/BooleanExpressionTest.cs index 1b40536ff9..214778f623 100644 --- a/OpenRA.Test/OpenRA.Game/BooleanExpressionTest.cs +++ b/OpenRA.Test/OpenRA.Game/BooleanExpressionTest.cs @@ -21,11 +21,11 @@ namespace OpenRA.Test [TestFixture] public class BooleanExpressionTest { - Dictionary testValues = new Dictionary() + IReadOnlyDictionary testValues = new ReadOnlyDictionary(new Dictionary() { { "true", true }, { "false", false } - }; + }); void AssertFalse(string expression) { diff --git a/mods/cnc/rules/aircraft.yaml b/mods/cnc/rules/aircraft.yaml index 4898f6cac5..484a3fb1f6 100644 --- a/mods/cnc/rules/aircraft.yaml +++ b/mods/cnc/rules/aircraft.yaml @@ -26,23 +26,19 @@ TRAN: WithIdleOverlay@ROTOR1AIR: Offset: 597,0,85 Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTOR1GROUND: Offset: 597,0,85 Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne WithIdleOverlay@ROTOR2AIR: Offset: -597,0,171 Sequence: rotor2 - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTOR2GROUND: Offset: -597,0,171 Sequence: slow-rotor2 - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Cargo: Types: Infantry MaxWeight: 10 @@ -98,13 +94,11 @@ HELI: WithIdleOverlay@ROTORAIR: Offset: 0,0,85 Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTORGROUND: Offset: 0,0,85 Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne WithMuzzleOverlay: SpawnActorOnDeath: Actor: HELI.Husk diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 7d51e55f28..6786586e8c 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -17,41 +17,104 @@ ^GainsExperience: GainsExperience: Upgrades: - 200: firepower, damage, speed, reload, inaccuracy, rank - 400: firepower, damage, speed, reload, inaccuracy, rank - 800: firepower, damage, speed, reload, inaccuracy, rank - 1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal - FirepowerMultiplier@EXPERIENCE: - UpgradeTypes: firepower - Modifier: 105, 110, 120, 130 - DamageMultiplier@EXPERIENCE: - UpgradeTypes: damage - Modifier: 95, 90, 85, 75 - SpeedMultiplier@EXPERIENCE: - UpgradeTypes: speed - Modifier: 105, 110, 120, 140 - ReloadDelayMultiplier@EXPERIENCE: - UpgradeTypes: reload - Modifier: 95, 90, 85, 75 - InaccuracyMultiplier@EXPERIENCE: - UpgradeTypes: inaccuracy - Modifier: 90, 80, 70, 50 + 200: rank-veteran-1 + 400: rank-veteran-2 + 800: rank-veteran-3 + 1600: rank-elite + DamageMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 95 + DamageMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 90 + DamageMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 85 + DamageMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 75 + FirepowerMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + FirepowerMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + FirepowerMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 120 + FirepowerMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 130 + SpeedMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + SpeedMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + SpeedMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 120 + SpeedMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 140 + ReloadDelayMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 95 + ReloadDelayMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 90 + ReloadDelayMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 85 + ReloadDelayMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 75 + InaccuracyMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 90 + InaccuracyMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 80 + InaccuracyMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 70 + InaccuracyMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 50 SelfHealing@ELITE: Step: 2 Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 - WithRankDecoration: + RequiresCondition: rank-elite + WithDecoration@RANK-1: Image: rank - Sequence: rank + Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - UpgradeTypes: rank + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + ZOffset: 256 + WithDecoration@RANK-2: + Image: rank + Sequence: rank-veteran-2 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + ZOffset: 256 + WithDecoration@RANK-3: + Image: rank + Sequence: rank-veteran-3 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-3 && !rank-elite + ZOffset: 256 + WithDecoration@RANK-ELITE: + Image: rank + Sequence: rank-elite + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-elite ZOffset: 256 - UpgradeMinEnabledLevel: 1 - UpgradeMaxAcceptedLevel: 4 ^Vehicle: Inherits@1: ^ExistsInWorld @@ -91,12 +154,11 @@ Tooltip: GenericName: Vehicle Cloak: - UpgradeTypes: cloak - UpgradeMinEnabledLevel: 1 InitialDelay: 15 CloakDelay: 90 CloakSound: trans1.aud UncloakSound: trans1.aud + RequiresCondition: cloak MustBeDestroyed: Voiced: VoiceSet: VehicleVoice @@ -127,12 +189,10 @@ UseLocation: yes Targetable@GROUND: TargetTypes: Ground, Vehicle - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Targetable@AIRBORNE: TargetTypes: Air - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne SelectionDecorations: WithSpriteControlGroupDecoration: Selectable: @@ -160,8 +220,7 @@ Offset: 43, 128, 0 ZOffset: -129 Hovers@CRUISING: - UpgradeTypes: cruising - UpgradeMinEnabledLevel: 1 + RequiresCondition: cruising MustBeDestroyed: Voiced: VoiceSet: VehicleVoice @@ -217,12 +276,11 @@ CargoType: Infantry HiddenUnderFog: DamagedByTerrain: - UpgradeTypes: hazmatsuits - UpgradeMaxEnabledLevel: 0 Terrain: Tiberium, BlueTiberium Damage: 2 DamageInterval: 16 DamageTypes: TiberiumDeath + RequiresCondition: !hazmatsuits GlobalUpgradable@BIO: Upgrades: hazmatsuits Prerequisites: bio @@ -230,8 +288,7 @@ Image: pips Sequence: pip-hazmat ReferencePoint: Bottom, Right - UpgradeTypes: hazmatsuits - UpgradeMinEnabledLevel: 1 + RequiresCondition: hazmatsuits ActorLostNotification: SpawnActorOnDeath: Probability: 10 @@ -249,8 +306,7 @@ Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: hospitalheal - UpgradeMinEnabledLevel: 1 + RequiresCondition: hospitalheal GlobalUpgradable@HOSPITAL: Upgrades: hospitalheal Prerequisites: hosp @@ -258,8 +314,7 @@ Image: pips Sequence: pip-heal ReferencePoint: Bottom, Right - UpgradeTypes: hospitalheal - UpgradeMinEnabledLevel: 1 + RequiresCondition: hospitalheal DetectCloaked: Range: 2c0 DeathSounds@NORMAL: diff --git a/mods/cnc/rules/ships.yaml b/mods/cnc/rules/ships.yaml index b7c3532f2b..b3031205a0 100644 --- a/mods/cnc/rules/ships.yaml +++ b/mods/cnc/rules/ships.yaml @@ -46,8 +46,6 @@ LST: BuildPaletteOrder: 1000 Prerequisites: ~disabled Mobile: - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 Crushes: crate TerrainSpeeds: Clear: 100 @@ -61,6 +59,7 @@ LST: InitialFacing: 0 TurnSpeed: 4 Speed: 142 + RequiresCondition: !notmobile Health: HP: 400 Armor: diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 598d4f9ca0..34b3b10f55 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -88,10 +88,9 @@ APC: Queue: Vehicle.GDI Description: Armed infantry transport.\nCan attack Aircraft.\n Strong vs Vehicles\n Weak vs Infantry Mobile: - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 TurnSpeed: 8 Speed: 128 + RequiresCondition: !notmobile Health: HP: 210 Armor: @@ -517,7 +516,7 @@ STNK: RevealsShroud: Range: 7c0 Cloak: - -UpgradeTypes: + -RequiresCondition: InitialDelay: 90 CloakDelay: 90 CloakSound: trans1.aud diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index c63506abfe..8c8aa35f96 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -124,8 +124,13 @@ explosion: FlipX: true rank: - rank: - Length: * + rank-veteran-1: + rank-veteran-2: + Start: 1 + rank-veteran-3: + Start: 2 + rank-elite: + Start: 3 rallypoint: flag: flagfly diff --git a/mods/d2k/maps/shellmap/rules.yaml b/mods/d2k/maps/shellmap/rules.yaml index f2baf3e2fd..27a52e0df1 100644 --- a/mods/d2k/maps/shellmap/rules.yaml +++ b/mods/d2k/maps/shellmap/rules.yaml @@ -21,11 +21,11 @@ World: ^Building: DamageMultiplier@UNKILLABLE: - Modifier: 0, 0 + Modifier: 0 wall: DamageMultiplier@UNKILLABLE: - Modifier: 0, 0 + Modifier: 0 upgrade.conyard: Valued: diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index 77c8055252..9d94195832 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -20,12 +20,10 @@ carryall.reinforce: CanHover: True Targetable@GROUND: TargetTypes: Ground, Vehicle - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Targetable@AIRBORNE: TargetTypes: Air - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne SpawnActorOnDeath: Actor: carryall.husk Carryall: diff --git a/mods/d2k/rules/arrakis.yaml b/mods/d2k/rules/arrakis.yaml index 3de4f67602..1fec4f6d62 100644 --- a/mods/d2k/rules/arrakis.yaml +++ b/mods/d2k/rules/arrakis.yaml @@ -72,13 +72,11 @@ sandworm: WithIdleAnimation: Interval: 160 Sequences: lightninga, lightningb, lightningc, lightningd, lightninge, lightningf - UpgradeTypes: attacking - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !attacking AmbientSound: SoundFile: WRMSIGN1.WAV Interval: 160 - UpgradeTypes: attacking - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !attacking WithAttackOverlay@mouth: Sequence: mouth WithAttackOverlay@sand: @@ -106,8 +104,7 @@ sandworm: Type: CenterPosition TerrainTypes: Sand, Dune, SpiceSand, Spice MovingInterval: 3 - UpgradeTypes: attacking - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !attacking UpgradeManager: Buildable: Description: Attracted by vibrations in the sand.\nWill eat units whole and has a large appetite. diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 9a5e9404f0..368100b356 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -17,42 +17,105 @@ ^GainsExperience: GainsExperience: Upgrades: - 200: firepower, damage, speed, reload, inaccuracy, rank - 400: firepower, damage, speed, reload, inaccuracy, rank - 800: firepower, damage, speed, reload, inaccuracy, rank - 1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal - FirepowerMultiplier@EXPERIENCE: - UpgradeTypes: firepower - Modifier: 105, 110, 115, 125 - DamageMultiplier@EXPERIENCE: - UpgradeTypes: damage - Modifier: 96, 92, 88, 80 - SpeedMultiplier@EXPERIENCE: - UpgradeTypes: speed - Modifier: 105, 110, 115, 125 - ReloadDelayMultiplier@EXPERIENCE: - UpgradeTypes: reload - Modifier: 96, 92, 88, 80 - InaccuracyMultiplier@EXPERIENCE: - UpgradeTypes: inaccuracy - Modifier: 90, 80, 70, 50 + 200: rank-veteran-1 + 400: rank-veteran-2 + 800: rank-veteran-3 + 1600: rank-elite + DamageMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 96 + DamageMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 92 + DamageMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 88 + DamageMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 80 + FirepowerMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + FirepowerMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + FirepowerMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 115 + FirepowerMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 125 + SpeedMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + SpeedMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + SpeedMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 115 + SpeedMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 125 + ReloadDelayMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 96 + ReloadDelayMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 92 + ReloadDelayMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 88 + ReloadDelayMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 80 + InaccuracyMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 90 + InaccuracyMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 80 + InaccuracyMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 70 + InaccuracyMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 50 SelfHealing@ELITE: Step: 0 PercentageStep: 4 Delay: 125 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 - WithRankDecoration: + RequiresCondition: rank-elite + WithDecoration@RANK-1: Image: rank - Sequence: rank + Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - UpgradeTypes: rank + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + ZOffset: 256 + WithDecoration@RANK-2: + Image: rank + Sequence: rank-veteran-2 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + ZOffset: 256 + WithDecoration@RANK-3: + Image: rank + Sequence: rank-veteran-3 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-3 && !rank-elite + ZOffset: 256 + WithDecoration@RANK-ELITE: + Image: rank + Sequence: rank-elite + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-elite ZOffset: 256 - UpgradeMinEnabledLevel: 1 - UpgradeMaxAcceptedLevel: 4 ^Vehicle: Inherits@1: ^ExistsInWorld @@ -60,8 +123,6 @@ Inherits@3: ^SpriteActor Huntable: Mobile: - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 Crushes: crate, spicebloom TerrainSpeeds: Sand: 100 @@ -73,6 +134,7 @@ SpiceBlobs: 100 Dune: 50 TurnSpeed: 5 + RequiresCondition: !notmobile SelectionDecorations: WithSpriteControlGroupDecoration: Selectable: diff --git a/mods/d2k/rules/infantry.yaml b/mods/d2k/rules/infantry.yaml index bc40a2491a..a9ce819312 100644 --- a/mods/d2k/rules/infantry.yaml +++ b/mods/d2k/rules/infantry.yaml @@ -94,34 +94,28 @@ thumper: RevealsShroud: Range: 2c768 Mobile: - UpgradeTypes: deployed - UpgradeMaxEnabledLevel: 0 Speed: 43 + RequiresCondition: !deployed DeployToUpgrade: DeployedUpgrades: deployed Facing: 128 AllowedTerrainTypes: Sand, Spice, Dune, SpiceSand WithInfantryBody: - UpgradeTypes: deployed - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !deployed WithSpriteBody@DEPLOYED: Sequence: thump - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed WithIdleOverlay@DEPLOYED: Sequence: thump-sand - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed AmbientSound: SoundFile: THUMPER1.WAV Interval: 60 - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed AttractsWorms: Intensity: 1000 Falloff: 0, 0, 0, 100, 100, 100, 25, 11, 6, 4, 3, 2, 1, 0 - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed Passenger: PipType: Blue Voiced: diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 15ac57a353..1aa889744e 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -98,15 +98,13 @@ construction_yard: Sequence: tag-upgraded ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: stardecoration - UpgradeMinEnabledLevel: 1 + RequiresCondition: stardecoration WithTextDecoration@primary: RequiresSelection: true Text: PRIMARY ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary wind_trap: Inherits: ^Building @@ -212,15 +210,13 @@ barracks: Sequence: tag-upgraded ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: stardecoration - UpgradeMinEnabledLevel: 1 + RequiresCondition: stardecoration WithTextDecoration@primary: RequiresSelection: true Text: PRIMARY ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary refinery: Inherits: ^Building @@ -393,15 +389,13 @@ light_factory: Sequence: tag-upgraded ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: stardecoration - UpgradeMinEnabledLevel: 1 + RequiresCondition: stardecoration WithTextDecoration@primary: RequiresSelection: true Text: PRIMARY ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary heavy_factory: Inherits: ^Building @@ -474,15 +468,13 @@ heavy_factory: Sequence: tag-upgraded ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: stardecoration - UpgradeMinEnabledLevel: 1 + RequiresCondition: stardecoration WithTextDecoration@primary: RequiresSelection: true Text: PRIMARY ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary outpost: Inherits: ^Building @@ -597,8 +589,7 @@ starport: Text: PRIMARY ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary wall: Inherits@1: ^SpriteActor @@ -854,8 +845,7 @@ high_tech_factory: Sequence: tag-upgraded ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: stardecoration - UpgradeMinEnabledLevel: 1 + RequiresCondition: stardecoration research_centre: Inherits: ^Building diff --git a/mods/d2k/sequences/misc.yaml b/mods/d2k/sequences/misc.yaml index 8da79d2fd6..f487686470 100644 --- a/mods/d2k/sequences/misc.yaml +++ b/mods/d2k/sequences/misc.yaml @@ -151,8 +151,13 @@ poweroff: ZOffset: 2047 rank: - rank: rank.shp - Length: * + rank-veteran-1: rank.shp + rank-veteran-2: rank.shp + Start: 1 + rank-veteran-3: rank.shp + Start: 2 + rank-elite: rank.shp + Start: 3 overlay: Defaults: DATA.R8 diff --git a/mods/ra/maps/allies-03a/rules.yaml b/mods/ra/maps/allies-03a/rules.yaml index ccc672b505..2955853830 100644 --- a/mods/ra/maps/allies-03a/rules.yaml +++ b/mods/ra/maps/allies-03a/rules.yaml @@ -33,8 +33,7 @@ HACKE6: Captures: CaptureTypes: building Targetable: - UpgradeTypes: jail - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !jail Targetable@PRISONER: TargetTypes: Prisoner RenderSprites: @@ -42,8 +41,7 @@ HACKE6: MEDI: Targetable: - UpgradeTypes: jail - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !jail Targetable@PRISONER: TargetTypes: Prisoner diff --git a/mods/ra/maps/allies-03b/rules.yaml b/mods/ra/maps/allies-03b/rules.yaml index 27e9ddd615..530c8d2656 100644 --- a/mods/ra/maps/allies-03b/rules.yaml +++ b/mods/ra/maps/allies-03b/rules.yaml @@ -34,8 +34,7 @@ HACKE6: CaptureTypes: building WithInfantryBody: Targetable: - UpgradeTypes: jail - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !jail Targetable@PRISONER: TargetTypes: Prisoner RenderSprites: @@ -43,8 +42,7 @@ HACKE6: MEDI: Targetable: - UpgradeTypes: jail - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !jail Targetable@PRISONER: TargetTypes: Prisoner diff --git a/mods/ra/maps/desert-shellmap/rules.yaml b/mods/ra/maps/desert-shellmap/rules.yaml index cca4d37d12..2163dffcae 100644 --- a/mods/ra/maps/desert-shellmap/rules.yaml +++ b/mods/ra/maps/desert-shellmap/rules.yaml @@ -21,8 +21,8 @@ World: GainsExperience: Upgrades: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 ^Tank: GivesBounty: @@ -30,8 +30,8 @@ World: GainsExperience: Upgrades: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 ^Infantry: GivesBounty: @@ -45,8 +45,8 @@ World: DeathSounds@ZAPPED: VolumeMultiplier: 0.1 DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 ^Ship: GivesBounty: @@ -54,22 +54,22 @@ World: GainsExperience: Upgrades: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 ^Plane: GivesBounty: Percentage: 0 DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 ^Building: GivesBounty: Percentage: 0 DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 OILB: CashTrickler: diff --git a/mods/ra/maps/infiltration/rules.yaml b/mods/ra/maps/infiltration/rules.yaml index 335a78f22f..4c5e72a40b 100644 --- a/mods/ra/maps/infiltration/rules.yaml +++ b/mods/ra/maps/infiltration/rules.yaml @@ -71,8 +71,7 @@ TRUK.Hijackable: Buildable: Prerequisites: ~disabled Mobile: - UpgradeTypes: mobile - UpgradeMinEnabledLevel: 1 + RequiresCondition: mobile Cargo: Types: Infantry MaxWeight: 5 diff --git a/mods/ra/rules/aircraft.yaml b/mods/ra/rules/aircraft.yaml index 504c05ff2e..a37bcb017f 100644 --- a/mods/ra/rules/aircraft.yaml +++ b/mods/ra/rules/aircraft.yaml @@ -223,23 +223,19 @@ TRAN: WithIdleOverlay@ROTOR1AIR: Offset: 597,0,213 Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTOR1GROUND: Offset: 597,0,213 Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne WithIdleOverlay@ROTOR2AIR: Offset: -597,0,341 Sequence: rotor2 - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTOR2GROUND: Offset: -597,0,341 Sequence: slow-rotor2 - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Cargo: Types: Infantry MaxWeight: 8 @@ -287,13 +283,11 @@ HELI: WithIdleOverlay@ROTORAIR: Offset: 0,0,85 Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTORGROUND: Offset: 0,0,85 Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne AmmoPool: Ammo: 8 SelectionDecorations: @@ -345,12 +339,10 @@ HIND: InitialStanceAI: HoldFire WithIdleOverlay@ROTORAIR: Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTORGROUND: Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne AmmoPool: Ammo: 24 PipCount: 6 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 3548dbed3b..d7ad8b18c0 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -15,49 +15,111 @@ ^GainsExperience: GainsExperience: - Upgrades: - 200: firepower, damage, speed, reload, inaccuracy, rank - 400: firepower, damage, speed, reload, inaccuracy, rank - 800: firepower, damage, speed, reload, inaccuracy, rank - 1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal - DamageMultiplier@EXPERIENCE: - UpgradeTypes: damage - Modifier: 95, 90, 85, 75 - FirepowerMultiplier@EXPERIENCE: - UpgradeTypes: firepower - Modifier: 105, 110, 120, 130 - SpeedMultiplier@EXPERIENCE: - UpgradeTypes: speed - Modifier: 105, 110, 120, 140 - ReloadDelayMultiplier@EXPERIENCE: - UpgradeTypes: reload - Modifier: 95, 90, 85, 75 - InaccuracyMultiplier@EXPERIENCE: - UpgradeTypes: inaccuracy - Modifier: 90, 80, 70, 50 + Upgrades: + 200: rank-veteran-1 + 400: rank-veteran-2 + 800: rank-veteran-3 + 1600: rank-elite + DamageMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 95 + DamageMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 90 + DamageMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 85 + DamageMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 75 + FirepowerMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + FirepowerMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + FirepowerMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 120 + FirepowerMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 130 + SpeedMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 105 + SpeedMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 110 + SpeedMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 120 + SpeedMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 140 + ReloadDelayMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 95 + ReloadDelayMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 90 + ReloadDelayMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 85 + ReloadDelayMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 75 + InaccuracyMultiplier@RANK-1: + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + Modifier: 90 + InaccuracyMultiplier@RANK-2: + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + Modifier: 80 + InaccuracyMultiplier@RANK-3: + RequiresCondition: rank-veteran-3 && !rank-elite + Modifier: 70 + InaccuracyMultiplier@RANK-ELITE: + RequiresCondition: rank-elite + Modifier: 50 SelfHealing@ELITE: Step: 2 Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 - WithRankDecoration: + RequiresCondition: rank-elite + WithDecoration@RANK-1: Image: rank - Sequence: rank + Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - UpgradeTypes: rank + RequiresCondition: rank-veteran-1 && !rank-veteran-2 + ZOffset: 256 + WithDecoration@RANK-2: + Image: rank + Sequence: rank-veteran-2 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-2 && !rank-veteran-3 + ZOffset: 256 + WithDecoration@RANK-3: + Image: rank + Sequence: rank-veteran-3 + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-veteran-3 && !rank-elite + ZOffset: 256 + WithDecoration@RANK-ELITE: + Image: rank + Sequence: rank-elite + Palette: effect + ReferencePoint: Bottom, Right + RequiresCondition: rank-elite ZOffset: 256 - UpgradeMinEnabledLevel: 1 - UpgradeMaxAcceptedLevel: 4 ^IronCurtainable: UpgradeOverlay@IRONCURTAIN: - UpgradeTypes: invulnerability - UpgradeMinEnabledLevel: 1 + RequiresCondition: invulnerability DamageMultiplier@IRONCURTAIN: - UpgradeTypes: invulnerability + RequiresCondition: invulnerability Modifier: 0 TimedUpgradeBar: Upgrade: invulnerability @@ -87,8 +149,7 @@ Bounds: 24, 24 Targetable: TargetTypes: Ground, Repair, Vehicle - UpgradeTypes: parachute - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !parachute Repairable: Chronoshiftable: Passenger: @@ -133,14 +194,13 @@ EmptyWeapon: UnitExplodeSmall WithFacingSpriteBody: WithParachute: - UpgradeTypes: parachute - UpgradeMinEnabledLevel: 1 ShadowImage: parach-shadow ShadowSequence: idle Image: parach Sequence: idle OpeningSequence: open Offset: 0,0,200 + RequiresCondition: parachute BodyOrientation: UseClassicFacingFudge: True @@ -195,8 +255,7 @@ Bounds: 12,18,0,-8 Targetable: TargetTypes: Ground, Infantry, Disguise - UpgradeTypes: parachute - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !parachute QuantizeFacingsFromSequence: Sequence: stand WithInfantryBody: @@ -228,8 +287,7 @@ Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: hospitalheal - UpgradeMinEnabledLevel: 1 + RequiresCondition: hospitalheal GlobalUpgradable: Upgrades: hospitalheal Prerequisites: hosp @@ -252,14 +310,13 @@ Voiced: VoiceSet: GenericVoice WithParachute: - UpgradeTypes: parachute - UpgradeMinEnabledLevel: 1 ShadowImage: parach-shadow ShadowSequence: idle Image: parach Sequence: idle OpeningSequence: open Offset: 0,0,427 + RequiresCondition: parachute ^Soldier: Inherits: ^Infantry @@ -370,12 +427,10 @@ CruisingUpgrades: cruising Targetable@GROUND: TargetTypes: Ground, Repair, Vehicle - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Targetable@AIRBORNE: TargetTypes: Air - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne HiddenUnderFog: Type: CenterPosition AttackMove: @@ -414,8 +469,7 @@ GpsDot: String: Helicopter Hovers@CRUISING: - UpgradeTypes: cruising - UpgradeMinEnabledLevel: 1 + RequiresCondition: cruising BodyOrientation: UseClassicFacingFudge: True @@ -825,13 +879,12 @@ KilledOnImpassableTerrain: false Passenger: WithParachute: - UpgradeTypes: parachute - UpgradeMinEnabledLevel: 1 Image: parach Sequence: idle OpeningSequence: open ShadowImage: parach-shadow ShadowSequence: idle + RequiresCondition: parachute UpgradeManager: ^Mine: diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index e589bcc3e7..dac456257e 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -226,8 +226,7 @@ SPY: Palette: effect ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: disguise - UpgradeMinEnabledLevel: 1 + RequiresCondition: disguise IgnoresDisguise: DetectCloaked: CloakTypes: Cloak, Hijacker diff --git a/mods/ra/rules/ships.yaml b/mods/ra/rules/ships.yaml index 61c661375b..2c2c0fb2d6 100644 --- a/mods/ra/rules/ships.yaml +++ b/mods/ra/rules/ships.yaml @@ -21,12 +21,10 @@ SS: Range: 6c0 Targetable: TargetTypes: Ground, Water, Repair - UpgradeTypes: underwater - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !underwater Targetable@UNDERWATER: TargetTypes: Underwater, Repair - UpgradeTypes: underwater - UpgradeMinEnabledLevel: 1 + RequiresCondition: underwater Cloak: CloakTypes: Underwater InitialDelay: 0 @@ -77,12 +75,10 @@ MSUB: Range: 6c0 Targetable: TargetTypes: Ground, Water, Repair - UpgradeTypes: underwater - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !underwater Targetable@UNDERWATER: TargetTypes: Underwater, Repair - UpgradeTypes: underwater - UpgradeMinEnabledLevel: 1 + RequiresCondition: underwater Cloak: CloakTypes: Underwater InitialDelay: 0 @@ -230,12 +226,11 @@ LST: Armor: Type: Heavy Mobile: - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 TurnSpeed: 10 Speed: 113 TerrainSpeeds: Beach: 70 + RequiresCondition: !notmobile RevealsShroud: Range: 6c0 SelectionDecorations: diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 646650367b..58730edadc 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -174,8 +174,7 @@ SPEN: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary SYRD: Inherits: ^Building @@ -270,8 +269,7 @@ SYRD: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary IRON: Inherits: ^ScienceBuilding @@ -877,8 +875,7 @@ WEAP: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary FACT: Inherits: ^Building @@ -1116,8 +1113,7 @@ HPAD: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary WithRearmAnimation: AFLD: @@ -1233,8 +1229,7 @@ AFLD: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary WithRearmAnimation: POWR: @@ -1413,8 +1408,7 @@ BARR: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary KENN: Inherits: ^Building @@ -1461,8 +1455,7 @@ KENN: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary TENT: Inherits: ^Building @@ -1541,8 +1534,7 @@ TENT: Sequence: tag-primary ReferencePoint: Top ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary FIX: Inherits: ^Building diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index 86fb5b6507..2578daeaf4 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -338,8 +338,7 @@ JEEP: Mobile: TurnSpeed: 10 Speed: 170 - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !notmobile RevealsShroud: Range: 8c0 Turreted: @@ -378,8 +377,7 @@ APC: Mobile: Speed: 142 Crushes: wall, mine, crate, infantry - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !notmobile RevealsShroud: Range: 5c0 Armament: @@ -651,8 +649,7 @@ DTRK: AttackSuicides: -DamageMultiplier@IRONCURTAIN: KillsSelf: - UpgradeTypes: invulnerability - UpgradeMinEnabledLevel: 1 + RequiresCondition: invulnerability Chronoshiftable: ExplodeInstead: yes @@ -738,8 +735,7 @@ STNK: Mobile: Speed: 142 Crushes: wall, mine, crate, infantry - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !notmobile RevealsShroud: Range: 7c0 AutoTarget: diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index ed9365c5b1..fde6800ea3 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -424,8 +424,13 @@ fire: ZOffset: 1023 rank: - rank: - Length: * + rank-veteran-1: + rank-veteran-2: + Start: 1 + rank-veteran-3: + Start: 2 + rank-elite: + Start: 3 iconchevrons: veteran: diff --git a/mods/ts/maps/fields-of-green/map.yaml b/mods/ts/maps/fields-of-green/map.yaml index a56cf3f9c8..581710380b 100644 --- a/mods/ts/maps/fields-of-green/map.yaml +++ b/mods/ts/maps/fields-of-green/map.yaml @@ -1447,13 +1447,13 @@ Rules: InitialUnits: e1, e1, e2, e2, medic GACTWR: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 NAOBEL: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 NALASR: DamageMultiplier@UNKILLABLE: - UpgradeTypes: unkillable - Modifier: 0, 0 + RequiresCondition: unkillable + Modifier: 0 diff --git a/mods/ts/rules/aircraft.yaml b/mods/ts/rules/aircraft.yaml index 270e23ca80..baf4bc07a0 100644 --- a/mods/ts/rules/aircraft.yaml +++ b/mods/ts/rules/aircraft.yaml @@ -147,8 +147,7 @@ ORCAB: AutoTarget: RenderSprites: Hovers@CRUISING: - UpgradeTypes: cruising - UpgradeMinEnabledLevel: 1 + RequiresCondition: cruising SpawnActorOnDeath: Actor: ORCAB.Husk @@ -310,13 +309,11 @@ APACHE: WithIdleOverlay@ROTORAIR: Offset: 85,0,384 Sequence: rotor - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne WithIdleOverlay@ROTORGROUND: Offset: 85,0,384 Sequence: slow-rotor - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne RenderSprites: SpawnActorOnDeath: Actor: APACHE.Husk @@ -351,8 +348,7 @@ HUNTER: Image: GGHUNT WithFacingSpriteBody: Hovers@CRUISING: - UpgradeTypes: cruising - UpgradeMinEnabledLevel: 1 + RequiresCondition: cruising QuantizeFacingsFromSequence: AutoSelectionSize: DrawLineToTarget: diff --git a/mods/ts/rules/civilian-infantry.yaml b/mods/ts/rules/civilian-infantry.yaml index a8906b1208..b74e1c2654 100644 --- a/mods/ts/rules/civilian-infantry.yaml +++ b/mods/ts/rules/civilian-infantry.yaml @@ -70,8 +70,7 @@ CHAMSPY: Palette: pips ReferencePoint: Top, Right ZOffset: 256 - UpgradeTypes: disguise - UpgradeMinEnabledLevel: 1 + RequiresCondition: disguise Infiltrates: Types: SpyInfiltrate -AutoTarget: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 1faf7dce35..679010a8d5 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -18,69 +18,80 @@ ^GainsExperience: GainsExperience: Upgrades: - 500: rank, firepower, damage, speed, reload - 1000: rank, firepower, damage, speed, reload, selfheal, eliteweapon - FirepowerMultiplier@EXPERIENCE: - UpgradeTypes: firepower - Modifier: 110, 130 - DamageMultiplier@EXPERIENCE: - UpgradeTypes: damage - Modifier: 90, 75 - SpeedMultiplier@EXPERIENCE: - UpgradeTypes: speed - Modifier: 120, 140 - ReloadDelayMultiplier@EXPERIENCE: - UpgradeTypes: reload - Modifier: 90, 75 + 500: rank-veteran + 1000: rank-elite + FirepowerMultiplier@VETERAN: + RequiresCondition: (rank-veteran && !rank-elite) || crate-firepower + Modifier: 110 + FirepowerMultiplier@ELITE: + RequiresCondition: rank-elite || (rank-veteran && crate-firepower) + Modifier: 130 + DamageMultiplier@VETERAN: + RequiresCondition: (rank-veteran && !rank-elite) || crate-damage + Modifier: 90 + DamageMultiplier@ELITE: + RequiresCondition: rank-elite || (rank-veteran && crate-damage) + Modifier: 75 + SpeedMultiplier@VETERAN: + RequiresCondition: (rank-veteran && !rank-elite) || crate-speed + Modifier: 120 + SpeedMultiplier@ELITE: + RequiresCondition: rank-elite || (rank-veteran && crate-speed) + Modifier: 140 + ReloadDelayMultiplier@VETERAN: + RequiresCondition: rank-veteran && !rank-elite + Modifier: 90 + ReloadDelayMultiplier@ELITE: + RequiresCondition: rank-elite + Modifier: 75 SelfHealing@ELITE: Step: 2 Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 - WithRankDecoration: + RequiresCondition: rank-elite + WithDecoration@VETERAN: Image: rank - Sequence: rank + Sequence: veteran Palette: ra ReferencePoint: Bottom, Right - UpgradeTypes: rank + RequiresCondition: rank-veteran && !rank-elite + ZOffset: 256 + WithDecoration@ELITE: + Image: rank + Sequence: elite + Palette: ra + ReferencePoint: Bottom, Right + RequiresCondition: rank-elite ZOffset: 256 - UpgradeMinEnabledLevel: 1 - UpgradeMaxAcceptedLevel: 2 ^EmpDisable: UpgradeOverlay@EMPDISABLE: - UpgradeTypes: empdisable - UpgradeMinEnabledLevel: 1 + RequiresCondition: empdisable Palette: disabled DisableOnUpgrade@EMPDISABLE: - UpgradeTypes: empdisable - UpgradeMinEnabledLevel: 1 + RequiresCondition: empdisable TimedUpgradeBar@EMPDISABLE: Upgrade: empdisable Color: FFFFFF WithIdleOverlay@EMPDISABLE: Sequence: emp-overlay Palette: effect - UpgradeTypes: empdisable + RequiresCondition: empdisable ShowToEnemies: true ZOffset: 512 - UpgradeMinEnabledLevel: 1 PowerMultiplier@EMPDISABLE: - UpgradeTypes: empdisable + RequiresCondition: empdisable Modifier: 0 ^EmpDisableMobile: Inherits: ^EmpDisable Mobile: - UpgradeTypes: notmobile - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !notmobile ^Cloakable: Cloak@CLOAKGENERATOR: - UpgradeTypes: cloakgenerator - UpgradeMinEnabledLevel: 1 + RequiresCondition: cloakgenerator || crate-cloak InitialDelay: 0 CloakDelay: 90 IsPlayerPalette: true @@ -306,8 +317,7 @@ Delay: 100 HealIfBelow: 100 DamageCooldown: 125 - UpgradeTypes: hospitalheal - UpgradeMinEnabledLevel: 1 + RequiresCondition: hospitalheal GlobalUpgradable@HOSPITAL: Upgrades: hospitalheal Prerequisites: cahosp @@ -316,8 +326,7 @@ Sequence: medic Palette: pips ReferencePoint: Bottom, Right - UpgradeTypes: hospitalheal - UpgradeMinEnabledLevel: 1 + RequiresCondition: hospitalheal ^RegularInfantryDeath: WithDeathAnimation@normal: @@ -406,7 +415,7 @@ ValidDamageStates: Critical GrantPermanently: true SpeedMultiplier@CRITICAL: - UpgradeTypes: criticalspeed + RequiresCondition: criticalspeed Modifier: 50 ^CivilianInfantry: @@ -488,10 +497,10 @@ Upgrades: criticalspeed ValidDamageStates: Critical SpeedMultiplier@DAMAGED: - UpgradeTypes: damagedspeed + RequiresCondition: damagedspeed Modifier: 80 SpeedMultiplier@CRITICAL: - UpgradeTypes: criticalspeed + RequiresCondition: criticalspeed Modifier: 60 Carryable: @@ -543,12 +552,10 @@ UseLocation: true Targetable@GROUND: TargetTypes: Ground, Vehicle - UpgradeTypes: airborne - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !airborne Targetable@AIRBORNE: TargetTypes: Air - UpgradeTypes: airborne - UpgradeMinEnabledLevel: 1 + RequiresCondition: airborne Selectable: WithTextControlGroupDecoration: SelectionDecorations: @@ -584,8 +591,7 @@ CruiseAltitude: 2048 CanHover: True Hovers@CRUISING: - UpgradeTypes: cruising - UpgradeMinEnabledLevel: 1 + RequiresCondition: cruising ^Plane: Inherits: ^Aircraft @@ -939,5 +945,4 @@ Upgrades: veins WithIdleOverlay@VEINS: Sequence: veins - UpgradeTypes: veins - UpgradeMinEnabledLevel: 1 + RequiresCondition: veins diff --git a/mods/ts/rules/gdi-structures.yaml b/mods/ts/rules/gdi-structures.yaml index 3c0a1733d7..b3005c315c 100644 --- a/mods/ts/rules/gdi-structures.yaml +++ b/mods/ts/rules/gdi-structures.yaml @@ -41,24 +41,20 @@ GAPOWR: Upgrades: powrup: powrup.a Power@pluga: - UpgradeTypes: powrup.a - UpgradeMinEnabledLevel: 1 + RequiresCondition: powrup.a Amount: 50 WithIdleOverlay@pluga: - UpgradeTypes: powrup.a - UpgradeMinEnabledLevel: 1 + RequiresCondition: powrup.a Sequence: idle-powrupa Pluggable@plugb: Offset: 1,1 Upgrades: powrup: powrup.b WithIdleOverlay@plugb: - UpgradeTypes: powrup.b - UpgradeMinEnabledLevel: 1 + RequiresCondition: powrup.b Sequence: idle-powrupb Power@plugb: - UpgradeTypes: powrup.b - UpgradeMinEnabledLevel: 1 + RequiresCondition: powrup.b Amount: 50 ProvidesPrerequisite@buildingname: SelectionDecorations: @@ -118,8 +114,7 @@ GAPILE: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary GAWEAP: Inherits: ^Building @@ -181,8 +176,7 @@ GAWEAP: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary GAHPAD: Inherits: ^Building @@ -236,8 +230,7 @@ GAHPAD: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary GADEPT: Inherits: ^Building @@ -406,8 +399,7 @@ GAPLUG: MaxHeightDelta: 3 IonCannonPower: Cursor: ioncannon - UpgradeTypes: plug.ioncannon - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.ioncannona || plug.ioncannonb Icon: ioncannon Effect: explosion EffectSequence: ionring @@ -421,8 +413,7 @@ GAPLUG: DisplayRadarPing: True CameraActor: camera ProduceActorPower: - UpgradeTypes: plug.hunterseeker - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.hunterseekera || plug.hunterseekerb Description: Hunter Seeker LongDesc: Releases a drone that will acquire and destroy an enemy target. Icon: hunterseeker @@ -437,38 +428,32 @@ GAPLUG: Power: Amount: -150 Power@ioncannon: - UpgradeTypes: plug.ioncannon - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.ioncannona || plug.ioncannonb Amount: -100 Power@hunterseeker: - UpgradeTypes: plug.hunterseeker - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.hunterseekera || plug.hunterseekerb Amount: -50 Pluggable@pluga: Offset: 0,2 Upgrades: - plug.ioncannon: plug.ioncannon, plug.ioncannona - plug.hunterseeker: plug.hunterseeker, plug.hunterseekera + plug.ioncannon: plug.ioncannona + plug.hunterseeker: plug.hunterseekera WithIdleOverlay@ioncannona: - UpgradeTypes: plug.ioncannona - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.ioncannona Sequence: idle-ioncannona WithIdleOverlay@hunterseekera: - UpgradeTypes: plug.hunterseekera - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.hunterseekera Sequence: idle-hunterseekera Pluggable@plugb: Offset: 1,2 Upgrades: - plug.ioncannon: plug.ioncannon, plug.ioncannonb - plug.hunterseeker: plug.hunterseeker, plug.hunterseekerb + plug.ioncannon: plug.ioncannonb + plug.hunterseeker: plug.hunterseekerb WithIdleOverlay@ioncannonb: - UpgradeTypes: plug.ioncannonb - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.ioncannonb Sequence: idle-ioncannonb WithIdleOverlay@hunterseekerb: - UpgradeTypes: plug.hunterseekerb - UpgradeMinEnabledLevel: 1 + RequiresCondition: plug.hunterseekerb Sequence: idle-hunterseekerb ProvidesPrerequisite@buildingname: SelectionDecorations: diff --git a/mods/ts/rules/gdi-support.yaml b/mods/ts/rules/gdi-support.yaml index 1306aaed33..c4469e53b8 100644 --- a/mods/ts/rules/gdi-support.yaml +++ b/mods/ts/rules/gdi-support.yaml @@ -68,61 +68,51 @@ GACTWR: BodyOrientation: QuantizedFacings: 32 DetectCloaked: - UpgradeTypes: tower - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan || tower.rocket || tower.sam Turreted: TurnSpeed: 10 InitialFacing: 224 AttackTurreted: - UpgradeTypes: tower - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan || tower.rocket || tower.sam CanPowerDown: IndicatorPalette: mouse PowerupSpeech: EnablePower PowerdownSpeech: DisablePower WithSpriteTurret@VULC: - UpgradeTypes: tower.vulcan - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan Recoils: false Sequence: turret-vulcan WithSpriteTurret@ROCKET: - UpgradeTypes: tower.rocket - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.rocket Recoils: false Sequence: turret-rocket WithSpriteTurret@SAM: - UpgradeTypes: tower.sam - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.sam Recoils: false Sequence: turret-sam Armament@VULCPRIMARY: - UpgradeTypes: tower.vulcan - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan Weapon: VulcanTower LocalOffset: 416,85,960 MuzzleSequence: muzzle MuzzleSplitFacings: 8 Armament@VULCSECONDARY: - UpgradeTypes: tower.vulcan - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan Name: secondary Weapon: VulcanTower LocalOffset: 416,-85,960 MuzzleSequence: muzzle MuzzleSplitFacings: 8 Armament@ROCKET: - UpgradeTypes: tower.rocket - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.rocket Weapon: RPGTower LocalOffset: 192,-65,1056 Armament@SAM: - UpgradeTypes: tower.sam - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.sam Weapon: RedEye2 LocalOffset: 384,0,1200 WithMuzzleOverlay: - UpgradeTypes: tower.vulcan - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan WithIdleOverlay@LIGHTS: Sequence: idle-lights LineBuildNode: @@ -130,18 +120,16 @@ GACTWR: Power@base: Amount: -10 Power@turrets: - UpgradeTypes: tower - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.vulcan || tower.rocket || tower.sam Amount: -20 Power@samextra: - UpgradeTypes: tower.sam - UpgradeMinEnabledLevel: 1 + RequiresCondition: tower.sam Amount: -10 Pluggable: Upgrades: - tower.vulcan: tower, tower.vulcan - tower.rocket: tower, tower.rocket - tower.sam: tower, tower.sam + tower.vulcan: tower.vulcan + tower.rocket: tower.rocket + tower.sam: tower.sam ProvidesPrerequisite@buildingname: SelectionDecorations: VisualBounds: 48, 48, 0, -12 diff --git a/mods/ts/rules/gdi-vehicles.yaml b/mods/ts/rules/gdi-vehicles.yaml index db6bd3da65..86c58f6dee 100644 --- a/mods/ts/rules/gdi-vehicles.yaml +++ b/mods/ts/rules/gdi-vehicles.yaml @@ -34,12 +34,10 @@ APC: Upgrades: inwater TerrainTypes: Water WithVoxelBody: - UpgradeTypes: inwater - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !inwater WithVoxelBody@water: Sequence: water - UpgradeTypes: inwater - UpgradeMinEnabledLevel: 1 + RequiresCondition: inwater LeavesTrails: Image: wake Palette: effect diff --git a/mods/ts/rules/misc.yaml b/mods/ts/rules/misc.yaml index 1e5f7c345b..aa176c15f2 100644 --- a/mods/ts/rules/misc.yaml +++ b/mods/ts/rules/misc.yaml @@ -77,21 +77,21 @@ CRATE: GrantUpgradeCrateAction@cloak: SelectionShares: 5 Effect: stealth - Upgrades: cloakgenerator + Upgrades: crate-cloak Notification: cloak5.aud GrantUpgradeCrateAction@firepower: SelectionShares: 5 Effect: firepower - Upgrades: firepower + Upgrades: crate-firepower Notification: 00-i070.aud GrantUpgradeCrateAction@armor: SelectionShares: 5 Effect: armor - Upgrades: damage + Upgrades: crate-damage Notification: 00-i068.aud GrantUpgradeCrateAction@speed: SelectionShares: 5 - Upgrades: speed + Upgrades: crate-speed Notification: 00-i080.aud SROCK01: diff --git a/mods/ts/rules/nod-structures.yaml b/mods/ts/rules/nod-structures.yaml index 5cef90a257..c1d8a21afa 100644 --- a/mods/ts/rules/nod-structures.yaml +++ b/mods/ts/rules/nod-structures.yaml @@ -128,8 +128,7 @@ NAHAND: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary NAWEAP: Inherits: ^Building @@ -187,8 +186,7 @@ NAWEAP: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary NAHPAD: Inherits: ^Building @@ -242,8 +240,7 @@ NAHPAD: ReferencePoint: Top Color: E0D048 ZOffset: 256 - UpgradeTypes: primary - UpgradeMinEnabledLevel: 1 + RequiresCondition: primary NARADR: Inherits: ^Building diff --git a/mods/ts/rules/nod-vehicles.yaml b/mods/ts/rules/nod-vehicles.yaml index 8efed6ee36..1be6da809d 100644 --- a/mods/ts/rules/nod-vehicles.yaml +++ b/mods/ts/rules/nod-vehicles.yaml @@ -55,14 +55,11 @@ BIKE: MaxHeightDelta: 3 Armament@PRIMARY: Weapon: BikeMissile - UpgradeTypes: eliteweapon - UpgradeMaxEnabledLevel: 0 - UpgradeMaxAcceptedLevel: 1 + RequiresCondition: !rank-elite LocalOffset: -108,-144,360, -108,144,360 Armament@ELITE: Weapon: HoverMissile - UpgradeTypes: eliteweapon - UpgradeMinEnabledLevel: 1 + RequiresCondition: rank-elite LocalOffset: -108,-144,360, -108,144,360 AttackFrontal: Voice: Attack @@ -74,12 +71,10 @@ TTNK: Cost: 800 Tooltip: Name: Tick Tank - UpgradeTypes: deployed - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !deployed Tooltip@DEPLOYED: Name: Tick Tank (deployed) - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed Buildable: Queue: Vehicle BuildPaletteOrder: 60 @@ -93,20 +88,16 @@ TTNK: HP: 350 Armor: Type: Light - UpgradeTypes: undeployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: undeployed Armament@PRIMARY: Weapon: 90mm LocalOffset: 288,0,256 - UpgradeTypes: eliteweapon - UpgradeMaxEnabledLevel: 0 - UpgradeMaxAcceptedLevel: 1 + RequiresCondition: !rank-elite MuzzleSequence: muzzle Armament@ELITE: Weapon: 120mmx LocalOffset: 288,0,256 - UpgradeTypes: eliteweapon - UpgradeMinEnabledLevel: 1 + RequiresCondition: rank-elite MuzzleSequence: muzzle WithMuzzleOverlay: RevealsShroud: @@ -123,16 +114,12 @@ TTNK: DeploySound: place2.aud UndeploySound: clicky1.aud WithVoxelBody: - UpgradeTypes: undeployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: undeployed WithSpriteBody@deployed: - UpgradeTypes: undeployed - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !undeployed AttackFrontal: Voice: Attack - UpgradeTypes: undeployed - UpgradeMinEnabledLevel: 1 - UpgradeMaxAcceptedLevel: 1 + RequiresCondition: undeployed Turreted: TurnSpeed: 6 Turret: deployed @@ -141,38 +128,31 @@ TTNK: WithVoxelBarrel: Armament: deployed LocalOffset: 128, 0, 256 - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed WithVoxelTurret@deployed: Turret: deployed - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed AttackTurreted@deployed: Voice: Attack Armaments: deployed - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed Armament@deployed: Name: deployed Turret: deployed Weapon: 90mm LocalOffset: 384,0,256 - UpgradeTypes: eliteweapon - UpgradeMaxEnabledLevel: 0 - UpgradeMaxAcceptedLevel: 1 + RequiresCondition: !rank-elite MuzzleSequence: muzzle Armament@deployedElite: Name: deployed Turret: deployed Weapon: 120mmx LocalOffset: 384,0,256 - UpgradeTypes: eliteweapon - UpgradeMinEnabledLevel: 1 + RequiresCondition: rank-elite MuzzleSequence: muzzle Armor@deployed: Type: Concrete - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed AutoTarget: ART2: diff --git a/mods/ts/rules/shared-infantry.yaml b/mods/ts/rules/shared-infantry.yaml index d8639598e7..6855a769f3 100644 --- a/mods/ts/rules/shared-infantry.yaml +++ b/mods/ts/rules/shared-infantry.yaml @@ -15,13 +15,10 @@ E1: Speed: 71 Armament@PRIMARY: Weapon: Minigun - UpgradeTypes: eliteweapon - UpgradeMaxEnabledLevel: 0 - UpgradeMaxAcceptedLevel: 1 + RequiresCondition: !rank-elite Armament@ELITE: Weapon: M1Carbine - UpgradeTypes: eliteweapon - UpgradeMinEnabledLevel: 1 + RequiresCondition: rank-elite AttackFrontal: Voice: Attack WithInfantryBody: diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml index 72f62fd590..e069fbbdb8 100644 --- a/mods/ts/rules/shared-vehicles.yaml +++ b/mods/ts/rules/shared-vehicles.yaml @@ -116,12 +116,10 @@ LPST: Cost: 950 Tooltip: Name: Mobile Sensor Array - UpgradeTypes: deployed - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !deployed Tooltip@DEPLOYED: Name: Mobile Sensor Array (deployed) - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed Health: HP: 600 Armor: @@ -147,14 +145,11 @@ LPST: UndeploySound: clicky1.aud WithVoxelBody: Image: lpst - UpgradeTypes: undeployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: undeployed WithSpriteBody@deployed: - UpgradeTypes: undeployed - UpgradeMaxEnabledLevel: 0 + RequiresCondition: !undeployed DetectCloaked: - UpgradeTypes: deployed - UpgradeMinEnabledLevel: 1 + RequiresCondition: deployed Range: 18c0 RenderDetectionCircle: TrailCount: 3 diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index dc9b2de88b..dd52cb7b65 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -62,9 +62,10 @@ crate-effects: levelup: veteran rank: - rank: pips + veteran: pips Start: 7 - Length: 2 + elite: pips + Start: 8 mpspawn: idle: