Merge pull request #12796 from pchote/external-conditions-rework

Add support for per-source and total external condition caps.
This commit is contained in:
reaperrr
2017-02-19 15:44:53 +01:00
committed by GitHub
20 changed files with 266 additions and 135 deletions

View File

@@ -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" />

View File

@@ -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.")]

View File

@@ -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)
{ {

View 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;
}
}
}

View File

@@ -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 { }
}

View File

@@ -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);
} }
} }
} }

View File

@@ -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);
} }
}); });

View File

@@ -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));
}); });
} }

View File

@@ -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);
} }

View File

@@ -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);
} }
} }
} }

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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