diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 5a1e2c55d7..12f913b9e7 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -776,6 +776,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Upgrades/StackedCondition.cs b/OpenRA.Mods.Common/Traits/Upgrades/StackedCondition.cs new file mode 100644 index 0000000000..3d0e6d2c69 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Upgrades/StackedCondition.cs @@ -0,0 +1,32 @@ +#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 OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Grant additional conditions when a specified condition has been granted multiple times.")] + public class StackedConditionInfo : TraitInfo + { + [FieldLoader.Require] + [UpgradeUsedReference] + [Desc("Condition to monitor.")] + public readonly string Condition = null; + + [FieldLoader.Require] + [UpgradeGrantedReference] + [Desc("Conditions to grant when the monitored condition is granted multiple times.", + "The first entry is activated at 2x grants, second entry at 3x grants, and so on.")] + public readonly string[] StackedConditions = { }; + } + + public class StackedCondition { } +} diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs index 29ff0cc0ef..4c49bbbfc8 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs @@ -31,7 +31,6 @@ namespace OpenRA.Mods.Common.Traits { /// Value used to represent an invalid token. public static readonly int InvalidConditionToken = -1; - string[] externalConditions = { }; class ConditionTimer { @@ -64,6 +63,15 @@ namespace OpenRA.Mods.Common.Traits /// Each granted condition receives a unique token that is used when revoking. Dictionary tokens = new Dictionary(); + /// Set of whitelisted externally grantable conditions cached from ExternalConditions traits. + string[] externalConditions = { }; + + /// Set of conditions that are monitored for stacked bonuses, and the bonus conditions that they grant. + readonly Dictionary stackedConditions = new Dictionary(); + + /// Tokens granted by the stacked condition bonuses defined in stackedConditions. + readonly Dictionary> stackedTokens = new Dictionary>(); + int nextToken = 1; /// Temporary shim between the old and new upgrade/condition grant and revoke methods. @@ -109,15 +117,21 @@ namespace OpenRA.Mods.Common.Traits conditionCache[kv.Value] = conditionState.Tokens.Count > 0; } - // Update all traits with their initial condition state - foreach (var consumer in allConsumers) - consumer.ConditionsChanged(self, readOnlyConditionCache); - // Build external condition whitelist externalConditions = self.Info.TraitInfos() .SelectMany(t => t.Conditions) .Distinct() .ToArray(); + + foreach (var sc in self.Info.TraitInfos()) + { + stackedConditions[sc.Condition] = sc.StackedConditions; + stackedTokens[sc.Condition] = new Stack(); + } + + // Update all traits with their initial condition state + foreach (var consumer in allConsumers) + consumer.ConditionsChanged(self, readOnlyConditionCache); } void UpdateConditionState(Actor self, string condition, int token, bool isRevoke) @@ -135,6 +149,18 @@ namespace OpenRA.Mods.Common.Traits foreach (var t in conditionState.Consumers) t.ConditionsChanged(self, readOnlyConditionCache); + + string[] sc; + if (stackedConditions.TryGetValue(condition, out sc)) + { + var target = (conditionState.Tokens.Count - 1).Clamp(0, sc.Length); + var st = stackedTokens[condition]; + for (var i = st.Count; i < target; i++) + st.Push(GrantCondition(self, sc[i])); + + for (var i = st.Count; i > target; i--) + RevokeCondition(self, st.Pop()); + } } /// Grants a specified condition. @@ -193,7 +219,14 @@ namespace OpenRA.Mods.Common.Traits if (state == null) throw new InvalidOperationException("AcceptsExternalCondition cannot be queried before the actor has been fully created."); - return externalConditions.Contains(condition) && !conditionCache[condition]; + if (!externalConditions.Contains(condition)) + return false; + + string[] sc; + if (stackedConditions.TryGetValue(condition, out sc)) + return stackedTokens[condition].Count < sc.Length; + + return !conditionCache[condition]; } /// Returns whether the specified token is valid for RevokeCondition