Merge pull request #12796 from pchote/external-conditions-rework
Add support for per-source and total external condition caps.
This commit is contained in:
@@ -777,7 +777,7 @@
|
|||||||
<Compile Include="Traits\AutoCarryall.cs" />
|
<Compile Include="Traits\AutoCarryall.cs" />
|
||||||
<Compile Include="Traits\World\CliffBackImpassabilityLayer.cs" />
|
<Compile Include="Traits\World\CliffBackImpassabilityLayer.cs" />
|
||||||
<Compile Include="Traits\Conditions\GrantCondition.cs" />
|
<Compile Include="Traits\Conditions\GrantCondition.cs" />
|
||||||
<Compile Include="Traits\Conditions\ExternalConditions.cs" />
|
<Compile Include="Traits\Conditions\ExternalCondition.cs" />
|
||||||
<Compile Include="Traits\Conditions\StackedCondition.cs" />
|
<Compile Include="Traits\Conditions\StackedCondition.cs" />
|
||||||
<Compile Include="Traits\Buildings\BridgeHut.cs" />
|
<Compile Include="Traits\Buildings\BridgeHut.cs" />
|
||||||
<Compile Include="Traits\Buildings\BridgePlaceholder.cs" />
|
<Compile Include="Traits\Buildings\BridgePlaceholder.cs" />
|
||||||
|
|||||||
@@ -20,15 +20,16 @@ using OpenRA.Traits;
|
|||||||
namespace OpenRA.Mods.Common.Scripting
|
namespace OpenRA.Mods.Common.Scripting
|
||||||
{
|
{
|
||||||
[ScriptPropertyGroup("General")]
|
[ScriptPropertyGroup("General")]
|
||||||
public class ConditionProperties : ScriptActorProperties, Requires<ConditionManagerInfo>
|
public class ConditionProperties : ScriptActorProperties, Requires<ExternalConditionInfo>
|
||||||
{
|
{
|
||||||
readonly ConditionManager conditionManager;
|
readonly ExternalCondition[] externalConditions;
|
||||||
|
|
||||||
readonly Dictionary<string, Stack<int>> legacyShim = new Dictionary<string, Stack<int>>();
|
readonly Dictionary<string, Stack<int>> legacyShim = new Dictionary<string, Stack<int>>();
|
||||||
|
|
||||||
public ConditionProperties(ScriptContext context, Actor self)
|
public ConditionProperties(ScriptContext context, Actor self)
|
||||||
: base(context, self)
|
: base(context, self)
|
||||||
{
|
{
|
||||||
conditionManager = self.Trait<ConditionManager>();
|
externalConditions = self.TraitsImplementing<ExternalCondition>().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Grant an external condition on this actor and return the revocation token.",
|
[Desc("Grant an external condition on this actor and return the revocation token.",
|
||||||
@@ -36,22 +37,27 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
"If duration > 0 the condition will be automatically revoked after the defined number of ticks")]
|
"If duration > 0 the condition will be automatically revoked after the defined number of ticks")]
|
||||||
public int GrantCondition(string condition, int duration = 0)
|
public int GrantCondition(string condition, int duration = 0)
|
||||||
{
|
{
|
||||||
if (!conditionManager.AcceptsExternalCondition(Self, condition, duration > 0))
|
var external = externalConditions
|
||||||
throw new InvalidDataException("Condition `{0}` has not been listed on an ExternalConditions trait".F(condition));
|
.FirstOrDefault(t => t.Info.Condition == condition && t.CanGrantCondition(Self, this));
|
||||||
|
|
||||||
return conditionManager.GrantCondition(Self, condition, true, duration);
|
if (external == null)
|
||||||
|
throw new InvalidDataException("Condition `{0}` has not been listed on an enabled ExternalCondition trait".F(condition));
|
||||||
|
|
||||||
|
return external.GrantCondition(Self, this, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Revoke a condition using the token returned by GrantCondition.")]
|
[Desc("Revoke a condition using the token returned by GrantCondition.")]
|
||||||
public void RevokeCondition(int token)
|
public void RevokeCondition(int token)
|
||||||
{
|
{
|
||||||
conditionManager.RevokeCondition(Self, token);
|
foreach (var external in externalConditions)
|
||||||
|
external.TryRevokeCondition(Self, this, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Check whether this actor accepts a specific external condition.")]
|
[Desc("Check whether this actor accepts a specific external condition.")]
|
||||||
public bool AcceptsCondition(string condition, bool timed = false)
|
public bool AcceptsCondition(string condition)
|
||||||
{
|
{
|
||||||
return conditionManager.AcceptsExternalCondition(Self, condition, timed);
|
return externalConditions
|
||||||
|
.Any(t => t.Info.Condition == condition && t.CanGrantCondition(Self, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Grant an upgrade to this actor. DEPRECATED! Will be removed.")]
|
[Desc("Grant an upgrade to this actor. DEPRECATED! Will be removed.")]
|
||||||
|
|||||||
@@ -63,9 +63,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
|
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
|
||||||
Dictionary<int, string> tokens = new Dictionary<int, string>();
|
Dictionary<int, string> tokens = new Dictionary<int, string>();
|
||||||
|
|
||||||
/// <summary>Set of whitelisted externally grantable conditions cached from ExternalConditions traits.</summary>
|
|
||||||
string[] externalConditions = { };
|
|
||||||
|
|
||||||
/// <summary>Set of conditions that are monitored for stacked bonuses, and the bonus conditions that they grant.</summary>
|
/// <summary>Set of conditions that are monitored for stacked bonuses, and the bonus conditions that they grant.</summary>
|
||||||
readonly Dictionary<string, string[]> stackedConditions = new Dictionary<string, string[]>();
|
readonly Dictionary<string, string[]> stackedConditions = new Dictionary<string, string[]>();
|
||||||
|
|
||||||
@@ -114,12 +111,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
conditionCache[kv.Value] = conditionState.Tokens.Count > 0;
|
conditionCache[kv.Value] = conditionState.Tokens.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build external condition whitelist
|
|
||||||
externalConditions = self.Info.TraitInfos<ExternalConditionsInfo>()
|
|
||||||
.SelectMany(t => t.Conditions)
|
|
||||||
.Distinct()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var sc in self.Info.TraitInfos<StackedConditionInfo>())
|
foreach (var sc in self.Info.TraitInfos<StackedConditionInfo>())
|
||||||
{
|
{
|
||||||
stackedConditions[sc.Condition] = sc.StackedConditions;
|
stackedConditions[sc.Condition] = sc.StackedConditions;
|
||||||
@@ -172,11 +163,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
/// <returns>The token that is used to revoke this condition.</returns>
|
/// <returns>The token that is used to revoke this condition.</returns>
|
||||||
/// <param name="external">Validate against the external condition whitelist.</param>
|
/// <param name="external">Validate against the external condition whitelist.</param>
|
||||||
/// <param name="duration">Automatically revoke condition after this delay if non-zero.</param>
|
/// <param name="duration">Automatically revoke condition after this delay if non-zero.</param>
|
||||||
public int GrantCondition(Actor self, string condition, bool external = false, int duration = 0)
|
public int GrantCondition(Actor self, string condition, int duration = 0)
|
||||||
{
|
{
|
||||||
if (external && !externalConditions.Contains(condition))
|
|
||||||
return InvalidConditionToken;
|
|
||||||
|
|
||||||
var token = nextToken++;
|
var token = nextToken++;
|
||||||
tokens.Add(token, condition);
|
tokens.Add(token, condition);
|
||||||
|
|
||||||
@@ -218,26 +206,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return InvalidConditionToken;
|
return InvalidConditionToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns true if the given external condition will have an effect on this actor.</summary>
|
|
||||||
public bool AcceptsExternalCondition(Actor self, string condition, bool timed = false)
|
|
||||||
{
|
|
||||||
if (state == null)
|
|
||||||
throw new InvalidOperationException("AcceptsExternalCondition cannot be queried before the actor has been fully created.");
|
|
||||||
|
|
||||||
if (!externalConditions.Contains(condition))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// A timed condition can always replace an existing timed condition (resetting its duration)
|
|
||||||
if (timed && timers.ContainsKey(condition))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
string[] sc;
|
|
||||||
if (stackedConditions.TryGetValue(condition, out sc))
|
|
||||||
return stackedTokens[condition].Count < sc.Length;
|
|
||||||
|
|
||||||
return !conditionCache[condition];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
|
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
|
||||||
public bool TokenValid(Actor self, int token)
|
public bool TokenValid(Actor self, int token)
|
||||||
{
|
{
|
||||||
|
|||||||
142
OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs
Normal file
142
OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2017 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
|
||||||
|
{
|
||||||
|
[Desc("Allows a condition to be granted from an external source (Lua, warheads, etc).")]
|
||||||
|
public class ExternalConditionInfo : ITraitInfo, Requires<ConditionManagerInfo>
|
||||||
|
{
|
||||||
|
[GrantedConditionReference]
|
||||||
|
[FieldLoader.Require]
|
||||||
|
public readonly string Condition = null;
|
||||||
|
|
||||||
|
[Desc("If > 0, restrict the number of times that this condition can be granted by a single source.")]
|
||||||
|
public readonly int SourceCap = 0;
|
||||||
|
|
||||||
|
[Desc("If > 0, restrict the number of times that this condition can be granted by any source.")]
|
||||||
|
public readonly int TotalCap = 0;
|
||||||
|
|
||||||
|
public object Create(ActorInitializer init) { return new ExternalCondition(init.Self, this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternalCondition
|
||||||
|
{
|
||||||
|
class TimedToken
|
||||||
|
{
|
||||||
|
public int Token;
|
||||||
|
public int Expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ExternalConditionInfo Info;
|
||||||
|
readonly ConditionManager conditionManager;
|
||||||
|
readonly Dictionary<object, HashSet<int>> permanentTokens = new Dictionary<object, HashSet<int>>();
|
||||||
|
readonly Dictionary<object, HashSet<TimedToken>> timedTokens = new Dictionary<object, HashSet<TimedToken>>();
|
||||||
|
|
||||||
|
public ExternalCondition(Actor self, ExternalConditionInfo info)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
conditionManager = self.Trait<ConditionManager>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanGrantCondition(Actor self, object source)
|
||||||
|
{
|
||||||
|
if (conditionManager == null || source == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Timed tokens do not count towards the source cap: the condition with the shortest
|
||||||
|
// remaining duration can always be revoked to make room.
|
||||||
|
if (Info.SourceCap > 0 && permanentTokens.GetOrAdd(source).Count >= Info.SourceCap)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Info.TotalCap > 0 && permanentTokens.Values.SelectMany(t => t).Count() >= Info.TotalCap)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GrantCondition(Actor self, object source, int duration = 0)
|
||||||
|
{
|
||||||
|
if (conditionManager == null || source == null || !CanGrantCondition(self, source))
|
||||||
|
return ConditionManager.InvalidConditionToken;
|
||||||
|
|
||||||
|
var token = conditionManager.GrantCondition(self, Info.Condition, duration);
|
||||||
|
var permanent = permanentTokens.GetOrAdd(source);
|
||||||
|
|
||||||
|
if (duration > 0)
|
||||||
|
{
|
||||||
|
var timed = timedTokens.GetOrAdd(source);
|
||||||
|
|
||||||
|
// Remove expired tokens
|
||||||
|
timed.RemoveWhere(t => t.Expires < self.World.WorldTick);
|
||||||
|
|
||||||
|
// Check level caps
|
||||||
|
if (Info.SourceCap > 0)
|
||||||
|
{
|
||||||
|
if (permanent.Count + timed.Count >= Info.SourceCap)
|
||||||
|
{
|
||||||
|
var expire = timed.MinByOrDefault(t => t.Expires);
|
||||||
|
if (expire != null)
|
||||||
|
{
|
||||||
|
timed.Remove(expire);
|
||||||
|
if (conditionManager.TokenValid(self, expire.Token))
|
||||||
|
conditionManager.RevokeCondition(self, expire.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Info.TotalCap > 0)
|
||||||
|
{
|
||||||
|
var totalCount = permanentTokens.Values.SelectMany(t => t).Count() + timedTokens.Values.SelectMany(t => t).Count();
|
||||||
|
if (totalCount >= Info.TotalCap)
|
||||||
|
{
|
||||||
|
// Prefer tokens from the same source
|
||||||
|
var expire = timedTokens.SelectMany(t => t.Value.Select(tt => new Tuple<object, TimedToken>(t.Key, tt)))
|
||||||
|
.MinByOrDefault(t => t.Item2.Expires);
|
||||||
|
if (expire != null)
|
||||||
|
{
|
||||||
|
if (conditionManager.TokenValid(self, expire.Item2.Token))
|
||||||
|
conditionManager.RevokeCondition(self, expire.Item2.Token);
|
||||||
|
|
||||||
|
timedTokens[expire.Item1].Remove(expire.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timed.Add(new TimedToken { Expires = self.World.WorldTick + duration, Token = token });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
permanent.Add(token);
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Revokes the external condition with the given token if it was granted by this trait.</summary>
|
||||||
|
/// <returns><c>true</c> if the now-revoked condition was originally granted by this trait.</returns>
|
||||||
|
public bool TryRevokeCondition(Actor self, object source, int token)
|
||||||
|
{
|
||||||
|
if (conditionManager == null || source == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var removed = permanentTokens.GetOrAdd(source).Remove(token) ||
|
||||||
|
timedTokens.GetOrAdd(source).RemoveWhere(t => t.Token == token) > 0;
|
||||||
|
|
||||||
|
if (removed && conditionManager.TokenValid(self, token))
|
||||||
|
conditionManager.RevokeCondition(self, token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2017 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<ExternalConditions>
|
|
||||||
{
|
|
||||||
[GrantedConditionReference]
|
|
||||||
public readonly string[] Conditions = { };
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ExternalConditions { }
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
@@ -104,13 +105,18 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (a == self && !info.AffectsParent)
|
if (a == self && !info.AffectsParent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (tokens.ContainsKey(a))
|
||||||
|
return;
|
||||||
|
|
||||||
var stance = self.Owner.Stances[a.Owner];
|
var stance = self.Owner.Stances[a.Owner];
|
||||||
if (!info.ValidStances.HasStance(stance))
|
if (!info.ValidStances.HasStance(stance))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
var external = a.TraitsImplementing<ExternalCondition>()
|
||||||
if (cm != null && !tokens.ContainsKey(a) && cm.AcceptsExternalCondition(a, info.Condition))
|
.FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self));
|
||||||
tokens[a] = cm.GrantCondition(a, info.Condition, true);
|
|
||||||
|
if (external != null)
|
||||||
|
tokens[a] = external.GrantCondition(a, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
|
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
|
||||||
@@ -130,9 +136,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!info.ValidStances.HasStance(stance))
|
if (!info.ValidStances.HasStance(stance))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cm = produced.TraitOrDefault<ConditionManager>();
|
var external = produced.TraitsImplementing<ExternalCondition>()
|
||||||
if (cm != null && cm.AcceptsExternalCondition(produced, info.Condition))
|
.FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(produced, self));
|
||||||
tokens[produced] = cm.GrantCondition(produced, info.Condition, true);
|
|
||||||
|
if (external != null)
|
||||||
|
tokens[produced] = external.GrantCondition(produced, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,9 +154,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
tokens.Remove(a);
|
tokens.Remove(a);
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
foreach (var external in a.TraitsImplementing<ExternalCondition>())
|
||||||
if (cm != null)
|
external.TryRevokeCondition(a, self, token);
|
||||||
cm.RevokeCondition(a, token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
bool AcceptsCondition(Actor a)
|
bool AcceptsCondition(Actor a)
|
||||||
{
|
{
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
return a.TraitsImplementing<ExternalCondition>()
|
||||||
return cm != null && cm.AcceptsExternalCondition(a, info.Condition, info.Duration > 0);
|
.Any(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetSelectionShares(Actor collector)
|
public override int GetSelectionShares(Actor collector)
|
||||||
@@ -71,11 +71,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!a.IsInWorld || a.IsDead)
|
if (!a.IsInWorld || a.IsDead)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
var external = a.TraitsImplementing<ExternalCondition>()
|
||||||
|
.FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self));
|
||||||
|
|
||||||
// Condition token is ignored because we never revoke this condition.
|
if (external != null)
|
||||||
if (cm != null)
|
external.GrantCondition(a, self, info.Duration);
|
||||||
cm.GrantCondition(a, info.Condition, true, info.Duration);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
foreach (var a in UnitsInRange(order.TargetLocation))
|
foreach (var a in UnitsInRange(order.TargetLocation))
|
||||||
{
|
{
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
var external = a.TraitsImplementing<ExternalCondition>()
|
||||||
|
.FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self));
|
||||||
|
|
||||||
// Condition token is ignored because we never revoke this condition.
|
if (external != null)
|
||||||
if (cm != null)
|
external.GrantCondition(a, self, info.Duration);
|
||||||
cm.GrantCondition(a, info.Condition, true, info.Duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!a.Owner.IsAlliedWith(Self.Owner))
|
if (!a.Owner.IsAlliedWith(Self.Owner))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
return a.TraitsImplementing<ExternalCondition>()
|
||||||
return cm != null && cm.AcceptsExternalCondition(a, info.Condition, info.Duration > 0);
|
.Any(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, Self));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -850,6 +850,24 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (engineVersion < 20170218)
|
||||||
|
{
|
||||||
|
var externalConditions = node.Value.Nodes.Where(n => n.Key.StartsWith("ExternalConditions", StringComparison.Ordinal));
|
||||||
|
foreach (var ec in externalConditions.ToList())
|
||||||
|
{
|
||||||
|
var conditionsNode = ec.Value.Nodes.FirstOrDefault(n => n.Key == "Conditions");
|
||||||
|
if (conditionsNode != null)
|
||||||
|
{
|
||||||
|
var conditions = FieldLoader.GetValue<string[]>("", conditionsNode.Value.Value);
|
||||||
|
foreach (var c in conditions)
|
||||||
|
node.Value.Nodes.Add(new MiniYamlNode("ExternalCondition@" + c.ToUpperInvariant(),
|
||||||
|
new MiniYaml("", new List<MiniYamlNode>() { new MiniYamlNode("Condition", c) })));
|
||||||
|
|
||||||
|
node.Value.Nodes.Remove(ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -36,11 +37,11 @@ namespace OpenRA.Mods.Common.Warheads
|
|||||||
if (!IsValidAgainst(a, firedBy))
|
if (!IsValidAgainst(a, firedBy))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var cm = a.TraitOrDefault<ConditionManager>();
|
var external = a.TraitsImplementing<ExternalCondition>()
|
||||||
|
.FirstOrDefault(t => t.Info.Condition == Condition && t.CanGrantCondition(a, firedBy));
|
||||||
|
|
||||||
// Condition token is ignored because we never revoke this condition.
|
if (external != null)
|
||||||
if (cm != null && cm.AcceptsExternalCondition(a, Condition, Duration > 0))
|
external.GrantCondition(a, firedBy, Duration);
|
||||||
cm.GrantCondition(a, Condition, true, Duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,8 +123,8 @@
|
|||||||
CloakSound: trans1.aud
|
CloakSound: trans1.aud
|
||||||
UncloakSound: trans1.aud
|
UncloakSound: trans1.aud
|
||||||
RequiresCondition: cloak
|
RequiresCondition: cloak
|
||||||
ExternalConditions@CLOAK:
|
ExternalCondition@CLOAK:
|
||||||
Conditions: cloak
|
Condition: cloak
|
||||||
|
|
||||||
^Vehicle:
|
^Vehicle:
|
||||||
Inherits@1: ^ExistsInWorld
|
Inherits@1: ^ExistsInWorld
|
||||||
|
|||||||
@@ -34,20 +34,20 @@ HACKE6:
|
|||||||
CaptureTypes: building
|
CaptureTypes: building
|
||||||
Targetable:
|
Targetable:
|
||||||
RequiresCondition: !jail
|
RequiresCondition: !jail
|
||||||
ExternalConditions@JAIL:
|
|
||||||
Conditions: jail
|
|
||||||
Targetable@PRISONER:
|
Targetable@PRISONER:
|
||||||
TargetTypes: Prisoner
|
TargetTypes: Prisoner
|
||||||
RenderSprites:
|
RenderSprites:
|
||||||
Image: E6
|
Image: E6
|
||||||
|
ExternalCondition@JAIL:
|
||||||
|
Condition: jail
|
||||||
|
|
||||||
MEDI:
|
MEDI:
|
||||||
Targetable:
|
Targetable:
|
||||||
RequiresCondition: !jail
|
RequiresCondition: !jail
|
||||||
ExternalConditions@JAIL:
|
|
||||||
Conditions: jail
|
|
||||||
Targetable@PRISONER:
|
Targetable@PRISONER:
|
||||||
TargetTypes: Prisoner
|
TargetTypes: Prisoner
|
||||||
|
ExternalCondition@JAIL:
|
||||||
|
Condition: jail
|
||||||
|
|
||||||
PRISON:
|
PRISON:
|
||||||
HiddenUnderShroud:
|
HiddenUnderShroud:
|
||||||
|
|||||||
@@ -35,20 +35,20 @@ HACKE6:
|
|||||||
WithInfantryBody:
|
WithInfantryBody:
|
||||||
Targetable:
|
Targetable:
|
||||||
RequiresCondition: !jail
|
RequiresCondition: !jail
|
||||||
ExternalConditions@JAIL:
|
|
||||||
Conditions: jail
|
|
||||||
Targetable@PRISONER:
|
Targetable@PRISONER:
|
||||||
TargetTypes: Prisoner
|
TargetTypes: Prisoner
|
||||||
RenderSprites:
|
RenderSprites:
|
||||||
Image: E6
|
Image: E6
|
||||||
|
ExternalCondition@JAIL:
|
||||||
|
Condition: jail
|
||||||
|
|
||||||
MEDI:
|
MEDI:
|
||||||
Targetable:
|
Targetable:
|
||||||
RequiresCondition: !jail
|
RequiresCondition: !jail
|
||||||
ExternalConditions@JAIL:
|
|
||||||
Conditions: jail
|
|
||||||
Targetable@PRISONER:
|
Targetable@PRISONER:
|
||||||
TargetTypes: Prisoner
|
TargetTypes: Prisoner
|
||||||
|
ExternalCondition@JAIL:
|
||||||
|
Condition: jail
|
||||||
|
|
||||||
PRISON:
|
PRISON:
|
||||||
HiddenUnderShroud:
|
HiddenUnderShroud:
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
^Tank:
|
^Tank:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
@@ -33,8 +33,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
^Infantry:
|
^Infantry:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
@@ -50,8 +50,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
^Ship:
|
^Ship:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
@@ -61,8 +61,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
^Plane:
|
^Plane:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
@@ -70,8 +70,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
^Building:
|
^Building:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
@@ -79,8 +79,8 @@ World:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|
||||||
OILB:
|
OILB:
|
||||||
CashTrickler:
|
CashTrickler:
|
||||||
|
|||||||
@@ -59,8 +59,14 @@ V05:
|
|||||||
DOG:
|
DOG:
|
||||||
# HACK: Disable experience without killing the linter
|
# HACK: Disable experience without killing the linter
|
||||||
-GainsExperience:
|
-GainsExperience:
|
||||||
ExternalConditions@EXPERIENCE:
|
ExternalCondition@RANK-VETERAN-1:
|
||||||
Conditions: rank-veteran-1, rank-veteran-2, rank-veteran-3, rank-elite
|
Condition: rank-veteran-1
|
||||||
|
ExternalCondition@RANK-VETERAN-2:
|
||||||
|
Condition: rank-veteran-2
|
||||||
|
ExternalCondition@RANK-VETERAN-3:
|
||||||
|
Condition: rank-veteran-3
|
||||||
|
ExternalCondition@RANK-ELITE:
|
||||||
|
Condition: rank-elite
|
||||||
|
|
||||||
SPY:
|
SPY:
|
||||||
Mobile:
|
Mobile:
|
||||||
|
|||||||
@@ -123,8 +123,8 @@
|
|||||||
Modifier: 0
|
Modifier: 0
|
||||||
TimedConditionBar:
|
TimedConditionBar:
|
||||||
Condition: invulnerability
|
Condition: invulnerability
|
||||||
ExternalConditions@INVULNERABILITY:
|
ExternalCondition@INVULNERABILITY:
|
||||||
Conditions: invulnerability
|
Condition: invulnerability
|
||||||
|
|
||||||
^Vehicle:
|
^Vehicle:
|
||||||
Inherits@1: ^ExistsInWorld
|
Inherits@1: ^ExistsInWorld
|
||||||
|
|||||||
@@ -539,8 +539,6 @@ DOME:
|
|||||||
Bib:
|
Bib:
|
||||||
ProvidesRadar:
|
ProvidesRadar:
|
||||||
RequiresCondition: !jammed && !disabled
|
RequiresCondition: !jammed && !disabled
|
||||||
ExternalConditions@JAMMED:
|
|
||||||
Conditions: jammed
|
|
||||||
InfiltrateForExploration:
|
InfiltrateForExploration:
|
||||||
DetectCloaked:
|
DetectCloaked:
|
||||||
Range: 10c0
|
Range: 10c0
|
||||||
@@ -550,6 +548,8 @@ DOME:
|
|||||||
ProvidesPrerequisite@buildingname:
|
ProvidesPrerequisite@buildingname:
|
||||||
GrantConditionOnDisabled@IDISABLE:
|
GrantConditionOnDisabled@IDISABLE:
|
||||||
Condition: disabled
|
Condition: disabled
|
||||||
|
ExternalCondition@JAMMED:
|
||||||
|
Condition: jammed
|
||||||
|
|
||||||
PBOX:
|
PBOX:
|
||||||
Inherits: ^Defense
|
Inherits: ^Defense
|
||||||
|
|||||||
@@ -1448,17 +1448,17 @@ Rules:
|
|||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
NAOBEL:
|
NAOBEL:
|
||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
NALASR:
|
NALASR:
|
||||||
DamageMultiplier@UNKILLABLE:
|
DamageMultiplier@UNKILLABLE:
|
||||||
RequiresCondition: unkillable
|
RequiresCondition: unkillable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions:
|
ExternalCondition@UNKILLABLE:
|
||||||
Conditions: unkillable
|
Condition: unkillable
|
||||||
|
|||||||
@@ -1296,7 +1296,8 @@ GALITE:
|
|||||||
SelectionDecorations:
|
SelectionDecorations:
|
||||||
VisualBounds: 25, 35, 0, -12
|
VisualBounds: 25, 35, 0, -12
|
||||||
-Cloak@EXTERNALCLOAK:
|
-Cloak@EXTERNALCLOAK:
|
||||||
-ExternalConditions@EXTERNALCLOAK:
|
-ExternalCondition@CLOAKGENERATOR:
|
||||||
|
-ExternalCondition@CRATE-CLOAK:
|
||||||
|
|
||||||
TSTLAMP:
|
TSTLAMP:
|
||||||
Inherits: GALITE
|
Inherits: GALITE
|
||||||
|
|||||||
@@ -75,8 +75,12 @@
|
|||||||
SpeedMultiplier@CRATES:
|
SpeedMultiplier@CRATES:
|
||||||
RequiresCondition: crate-speed
|
RequiresCondition: crate-speed
|
||||||
Modifier: 170
|
Modifier: 170
|
||||||
ExternalConditions@CRATES:
|
ExternalCondition@CRATE-FIREPOWER:
|
||||||
Conditions: crate-firepower, crate-damage, crate-speed
|
Condition: crate-firepower
|
||||||
|
ExternalCondition@CRATE-DAMAGE:
|
||||||
|
Condition: crate-damage
|
||||||
|
ExternalCondition@CRATE-SPEED:
|
||||||
|
Condition: crate-speed
|
||||||
|
|
||||||
^EmpDisable:
|
^EmpDisable:
|
||||||
WithColoredOverlay@EMPDISABLE:
|
WithColoredOverlay@EMPDISABLE:
|
||||||
@@ -96,8 +100,8 @@
|
|||||||
PowerMultiplier@EMPDISABLE:
|
PowerMultiplier@EMPDISABLE:
|
||||||
RequiresCondition: empdisable
|
RequiresCondition: empdisable
|
||||||
Modifier: 0
|
Modifier: 0
|
||||||
ExternalConditions@EMPDISABLE:
|
ExternalCondition@EMPDISABLE:
|
||||||
Conditions: empdisable
|
Condition: empdisable
|
||||||
|
|
||||||
^Cloakable:
|
^Cloakable:
|
||||||
Cloak@EXTERNALCLOAK:
|
Cloak@EXTERNALCLOAK:
|
||||||
@@ -108,8 +112,10 @@
|
|||||||
CloakSound: cloak5.aud
|
CloakSound: cloak5.aud
|
||||||
UncloakSound: cloak5.aud
|
UncloakSound: cloak5.aud
|
||||||
UncloakOn: Attack, Unload, Infiltrate, Demolish, Damage
|
UncloakOn: Attack, Unload, Infiltrate, Demolish, Damage
|
||||||
ExternalConditions@EXTERNALCLOAK:
|
ExternalCondition@CLOAKGENERATOR:
|
||||||
Conditions: cloakgenerator, crate-cloak
|
Condition: cloakgenerator
|
||||||
|
ExternalCondition@CRATE-CLOAK:
|
||||||
|
Condition: crate-cloak
|
||||||
|
|
||||||
^BasicBuilding:
|
^BasicBuilding:
|
||||||
Inherits@1: ^ExistsInWorld
|
Inherits@1: ^ExistsInWorld
|
||||||
@@ -206,7 +212,8 @@
|
|||||||
RenderSprites:
|
RenderSprites:
|
||||||
Palette: player
|
Palette: player
|
||||||
-Cloak@EXTERNALCLOAK:
|
-Cloak@EXTERNALCLOAK:
|
||||||
-ExternalConditions@EXTERNALCLOAK:
|
-ExternalCondition@CLOAKGENERATOR:
|
||||||
|
-ExternalCondition@CRATE-CLOAK:
|
||||||
|
|
||||||
^CivBillboard:
|
^CivBillboard:
|
||||||
Inherits: ^CivBuilding
|
Inherits: ^CivBuilding
|
||||||
|
|||||||
Reference in New Issue
Block a user