diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index c0938fdadc..93310b99ab 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -775,6 +775,7 @@
+
diff --git a/OpenRA.Mods.Common/Traits/Upgrades/ExternalConditions.cs b/OpenRA.Mods.Common/Traits/Upgrades/ExternalConditions.cs
new file mode 100644
index 0000000000..c544a6c188
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Upgrades/ExternalConditions.cs
@@ -0,0 +1,25 @@
+#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("Lists conditions that are accepted from external sources (Lua, warheads, etc).",
+ "Externally granted conditions that aren't explicitly whitelisted will be silently ignored.")]
+ public class ExternalConditionsInfo : TraitInfo
+ {
+ [UpgradeGrantedReference]
+ public readonly string[] Conditions = { };
+ }
+
+ public class ExternalConditions { }
+}
diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs
index d7a07ef96e..b443593dce 100644
--- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs
+++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs
@@ -24,6 +24,7 @@ namespace OpenRA.Mods.Common.Traits
{
/// Value used to represent an invalid token.
public static readonly int InvalidConditionToken = -1;
+ string[] externalConditions = { };
class TimedCondition
{
@@ -120,6 +121,12 @@ namespace OpenRA.Mods.Common.Traits
// 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();
}
void UpdateConditionState(Actor self, string condition, int token, bool isRevoke)
@@ -141,8 +148,12 @@ namespace OpenRA.Mods.Common.Traits
/// Grants a specified condition.
/// The token that is used to revoke this condition.
- public int GrantCondition(Actor self, string condition)
+ /// Validate against the external condition whitelist.
+ public int GrantCondition(Actor self, string condition, bool external = false)
{
+ if (external && !externalConditions.Contains(condition))
+ return InvalidConditionToken;
+
var token = nextToken++;
tokens.Add(token, condition);
@@ -155,7 +166,7 @@ namespace OpenRA.Mods.Common.Traits
}
/// Revokes a previously granted condition.
- /// The invalid token ID
+ /// The invalid token ID.
/// The token ID returned by GrantCondition.
public int RevokeCondition(Actor self, int token)
{
@@ -172,6 +183,15 @@ namespace OpenRA.Mods.Common.Traits
return InvalidConditionToken;
}
+ /// Returns true if the given external condition will have an effect on this actor.
+ public bool AcceptsExternalCondition(Actor self, string condition)
+ {
+ if (state == null)
+ throw new InvalidOperationException("AcceptsExternalCondition cannot be queried before the actor has been fully created.");
+
+ return externalConditions.Contains(condition) && !conditionCache[condition];
+ }
+
#region Shim methods for legacy upgrade granting code
void CheckCanManageConditions()