Merge pull request #12406 from pchote/external-conditions
Upgrades overhaul part 3: Timed, external, and stacked conditions.
This commit is contained in:
@@ -307,7 +307,7 @@
|
||||
<Compile Include="Traits\Crates\GiveCashCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\GiveMcvCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\GiveUnitCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\GrantUpgradeCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\GrantExternalConditionCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\HealUnitsCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\HideMapCrateAction.cs" />
|
||||
<Compile Include="Traits\Crates\LevelUpCrateAction.cs" />
|
||||
@@ -417,7 +417,7 @@
|
||||
<Compile Include="Traits\Render\RenderVoxels.cs" />
|
||||
<Compile Include="Traits\Render\ProductionBar.cs" />
|
||||
<Compile Include="Traits\Render\SupportPowerChargeBar.cs" />
|
||||
<Compile Include="Traits\Render\TimedUpgradeBar.cs" />
|
||||
<Compile Include="Traits\Render\TimedConditionBar.cs" />
|
||||
<Compile Include="Traits\Render\WithSpriteBarrel.cs" />
|
||||
<Compile Include="Traits\Render\WithBuildingExplosion.cs" />
|
||||
<Compile Include="Traits\Render\WithAttackAnimation.cs" />
|
||||
@@ -480,7 +480,7 @@
|
||||
<Compile Include="Traits\Sound\AttackSounds.cs" />
|
||||
<Compile Include="Traits\SupplyTruck.cs" />
|
||||
<Compile Include="Traits\SupportPowers\AirstrikePower.cs" />
|
||||
<Compile Include="Traits\SupportPowers\GrantUpgradePower.cs" />
|
||||
<Compile Include="Traits\SupportPowers\GrantExternalConditionPower.cs" />
|
||||
<Compile Include="Traits\SupportPowers\NukePower.cs" />
|
||||
<Compile Include="Traits\SupportPowers\SupportPower.cs" />
|
||||
<Compile Include="Traits\SupportPowers\SupportPowerManager.cs" />
|
||||
@@ -564,7 +564,7 @@
|
||||
<Compile Include="Warheads\CreateResourceWarhead.cs" />
|
||||
<Compile Include="Warheads\DamageWarhead.cs" />
|
||||
<Compile Include="Warheads\DestroyResourceWarhead.cs" />
|
||||
<Compile Include="Warheads\GrantUpgradeWarhead.cs" />
|
||||
<Compile Include="Warheads\GrantExternalConditionWarhead.cs" />
|
||||
<Compile Include="Warheads\HealthPercentageDamageWarhead.cs" />
|
||||
<Compile Include="Warheads\LeaveSmudgeWarhead.cs" />
|
||||
<Compile Include="Warheads\SpreadDamageWarhead.cs" />
|
||||
@@ -775,6 +775,8 @@
|
||||
<Compile Include="Traits\AutoCarryall.cs" />
|
||||
<Compile Include="Traits\World\CliffBackImpassabilityLayer.cs" />
|
||||
<Compile Include="Traits\Upgrades\GrantCondition.cs" />
|
||||
<Compile Include="Traits\Upgrades\ExternalConditions.cs" />
|
||||
<Compile Include="Traits\Upgrades\StackedCondition.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="AfterBuild">
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
protected override void Created(Actor self)
|
||||
{
|
||||
upgradeManager = self.Trait<UpgradeManager>();
|
||||
|
||||
base.Created(self);
|
||||
}
|
||||
|
||||
public virtual void Attached(Actor self)
|
||||
|
||||
@@ -15,11 +15,11 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Grants an upgrade to the collector.")]
|
||||
public class GrantUpgradeCrateActionInfo : CrateActionInfo
|
||||
public class GrantExternalConditionCrateActionInfo : CrateActionInfo
|
||||
{
|
||||
[UpgradeGrantedReference, FieldLoader.Require]
|
||||
[Desc("The upgrades to apply.")]
|
||||
public readonly string[] Upgrades = { };
|
||||
[FieldLoader.Require]
|
||||
[Desc("The condition to apply. Must be included in the target actor's ExternalConditions list.")]
|
||||
public readonly string Condition = null;
|
||||
|
||||
[Desc("Duration of the upgrade (in ticks). Set to 0 for a permanent upgrade.")]
|
||||
public readonly int Duration = 0;
|
||||
@@ -30,37 +30,36 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("The maximum number of extra collectors to grant the crate action to.", "-1 = no limit")]
|
||||
public readonly int MaxExtraCollectors = 4;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new GrantUpgradeCrateAction(init.Self, this); }
|
||||
public override object Create(ActorInitializer init) { return new GrantExternalConditionCrateAction(init.Self, this); }
|
||||
}
|
||||
|
||||
public class GrantUpgradeCrateAction : CrateAction
|
||||
public class GrantExternalConditionCrateAction : CrateAction
|
||||
{
|
||||
readonly Actor self;
|
||||
readonly GrantUpgradeCrateActionInfo info;
|
||||
readonly GrantExternalConditionCrateActionInfo info;
|
||||
|
||||
public GrantUpgradeCrateAction(Actor self, GrantUpgradeCrateActionInfo info)
|
||||
public GrantExternalConditionCrateAction(Actor self, GrantExternalConditionCrateActionInfo info)
|
||||
: base(self, info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
bool AcceptsUpgrade(Actor a)
|
||||
bool AcceptsCondition(Actor a)
|
||||
{
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
return um != null && (info.Duration > 0 ?
|
||||
info.Upgrades.Any(u => um.AcknowledgesUpgrade(a, u)) : info.Upgrades.Any(u => um.AcceptsUpgrade(a, u)));
|
||||
return um != null && um.AcceptsExternalCondition(a, info.Condition);
|
||||
}
|
||||
|
||||
public override int GetSelectionShares(Actor collector)
|
||||
{
|
||||
return AcceptsUpgrade(collector) ? info.SelectionShares : 0;
|
||||
return AcceptsCondition(collector) ? info.SelectionShares : 0;
|
||||
}
|
||||
|
||||
public override void Activate(Actor collector)
|
||||
{
|
||||
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, info.Range)
|
||||
.Where(a => a != self && a != collector && a.Owner == collector.Owner && AcceptsUpgrade(a));
|
||||
.Where(a => a != self && a != collector && a.Owner == collector.Owner && AcceptsCondition(a));
|
||||
|
||||
if (info.MaxExtraCollectors > -1)
|
||||
actorsInRange = actorsInRange.Take(info.MaxExtraCollectors);
|
||||
@@ -73,19 +72,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
continue;
|
||||
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
foreach (var u in info.Upgrades)
|
||||
{
|
||||
if (info.Duration > 0)
|
||||
{
|
||||
if (um.AcknowledgesUpgrade(a, u))
|
||||
um.GrantTimedUpgrade(a, u, info.Duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (um.AcceptsUpgrade(a, u))
|
||||
um.GrantUpgrade(a, u, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Condition token is ignored because we never revoke this condition.
|
||||
if (um != null)
|
||||
um.GrantCondition(a, info.Condition, true, info.Duration);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -15,38 +15,35 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
[Desc("Visualizes the remaining time for an upgrade.")]
|
||||
class TimedUpgradeBarInfo : ITraitInfo, Requires<UpgradeManagerInfo>
|
||||
class TimedConditionBarInfo : ITraitInfo, Requires<UpgradeManagerInfo>
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
[Desc("Upgrade that this bar corresponds to")]
|
||||
public readonly string Upgrade = null;
|
||||
[Desc("Condition that this bar corresponds to")]
|
||||
public readonly string Condition = null;
|
||||
|
||||
public readonly Color Color = Color.Red;
|
||||
|
||||
public object Create(ActorInitializer init) { return new TimedUpgradeBar(init.Self, this); }
|
||||
public object Create(ActorInitializer init) { return new TimedConditionBar(init.Self, this); }
|
||||
}
|
||||
|
||||
class TimedUpgradeBar : ISelectionBar, INotifyCreated
|
||||
class TimedConditionBar : ISelectionBar, IConditionTimerWatcher
|
||||
{
|
||||
readonly TimedUpgradeBarInfo info;
|
||||
readonly TimedConditionBarInfo info;
|
||||
readonly Actor self;
|
||||
float value;
|
||||
|
||||
public TimedUpgradeBar(Actor self, TimedUpgradeBarInfo info)
|
||||
public TimedConditionBar(Actor self, TimedConditionBarInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void Created(Actor self)
|
||||
void IConditionTimerWatcher.Update(int duration, int remaining)
|
||||
{
|
||||
self.Trait<UpgradeManager>().RegisterWatcher(info.Upgrade, Update);
|
||||
value = duration > 0 ? remaining * 1f / duration : 0;
|
||||
}
|
||||
|
||||
public void Update(int duration, int remaining)
|
||||
{
|
||||
value = remaining * 1f / duration;
|
||||
}
|
||||
string IConditionTimerWatcher.Condition { get { return info.Condition; } }
|
||||
|
||||
float ISelectionBar.GetValue()
|
||||
{
|
||||
@@ -19,31 +19,33 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
class GrantUpgradePowerInfo : SupportPowerInfo
|
||||
class GrantExternalConditionPowerInfo : SupportPowerInfo
|
||||
{
|
||||
[UpgradeGrantedReference, FieldLoader.Require]
|
||||
[Desc("The upgrades to apply.")]
|
||||
public readonly string[] Upgrades = { };
|
||||
[FieldLoader.Require]
|
||||
[Desc("The condition to apply. Must be included in the target actor's ExternalConditions list.")]
|
||||
public readonly string Condition = null;
|
||||
|
||||
[Desc("Duration of the upgrade (in ticks). Set to 0 for a permanent upgrade.")]
|
||||
[Desc("Duration of the upgrade (in ticks). Set to 0 for a permanent condition.")]
|
||||
public readonly int Duration = 0;
|
||||
|
||||
[Desc("Cells - affects whole cells only")]
|
||||
public readonly int Range = 1;
|
||||
public readonly string GrantUpgradeSound = "ironcur9.aud";
|
||||
|
||||
[Desc("Sound to instantly play at the targeted area.")]
|
||||
public readonly string OnFireSound = null;
|
||||
|
||||
[SequenceReference, Desc("Sequence to play for granting actor when activated.",
|
||||
"This requires the actor to have the WithSpriteBody trait or one of its derivatives.")]
|
||||
public readonly string GrantUpgradeSequence = "active";
|
||||
public readonly string Sequence = "active";
|
||||
|
||||
public override object Create(ActorInitializer init) { return new GrantUpgradePower(init.Self, this); }
|
||||
public override object Create(ActorInitializer init) { return new GrantExternalConditionPower(init.Self, this); }
|
||||
}
|
||||
|
||||
class GrantUpgradePower : SupportPower
|
||||
class GrantExternalConditionPower : SupportPower
|
||||
{
|
||||
GrantUpgradePowerInfo info;
|
||||
readonly GrantExternalConditionPowerInfo info;
|
||||
|
||||
public GrantUpgradePower(Actor self, GrantUpgradePowerInfo info)
|
||||
public GrantExternalConditionPower(Actor self, GrantExternalConditionPowerInfo info)
|
||||
: base(self, info)
|
||||
{
|
||||
this.info = info;
|
||||
@@ -60,30 +62,18 @@ namespace OpenRA.Mods.Common.Traits
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
var wsb = self.TraitOrDefault<WithSpriteBody>();
|
||||
if (wsb != null && wsb.DefaultAnimation.HasSequence(info.GrantUpgradeSequence))
|
||||
wsb.PlayCustomAnimation(self, info.GrantUpgradeSequence, () => wsb.CancelCustomAnimation(self));
|
||||
if (wsb != null && wsb.DefaultAnimation.HasSequence(info.Sequence))
|
||||
wsb.PlayCustomAnimation(self, info.Sequence, () => wsb.CancelCustomAnimation(self));
|
||||
|
||||
Game.Sound.Play(info.GrantUpgradeSound, self.World.Map.CenterOfCell(order.TargetLocation));
|
||||
Game.Sound.Play(info.OnFireSound, self.World.Map.CenterOfCell(order.TargetLocation));
|
||||
|
||||
foreach (var a in UnitsInRange(order.TargetLocation))
|
||||
{
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
if (um == null)
|
||||
continue;
|
||||
|
||||
foreach (var u in info.Upgrades)
|
||||
{
|
||||
if (info.Duration > 0)
|
||||
{
|
||||
if (um.AcknowledgesUpgrade(a, u))
|
||||
um.GrantTimedUpgrade(a, u, info.Duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (um.AcceptsUpgrade(a, u))
|
||||
um.GrantUpgrade(a, u, this);
|
||||
}
|
||||
}
|
||||
// Condition token is ignored because we never revoke this condition.
|
||||
if (um != null)
|
||||
um.GrantCondition(a, info.Condition, true, info.Duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,20 +91,19 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return false;
|
||||
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
return um != null && (info.Duration > 0 ?
|
||||
info.Upgrades.Any(u => um.AcknowledgesUpgrade(a, u)) : info.Upgrades.Any(u => um.AcceptsUpgrade(a, u)));
|
||||
return um != null && um.AcceptsExternalCondition(a, info.Condition);
|
||||
});
|
||||
}
|
||||
|
||||
class SelectUpgradeTarget : IOrderGenerator
|
||||
{
|
||||
readonly GrantUpgradePower power;
|
||||
readonly GrantExternalConditionPower power;
|
||||
readonly int range;
|
||||
readonly Sprite tile;
|
||||
readonly SupportPowerManager manager;
|
||||
readonly string order;
|
||||
|
||||
public SelectUpgradeTarget(World world, string order, SupportPowerManager manager, GrantUpgradePower power)
|
||||
public SelectUpgradeTarget(World world, string order, SupportPowerManager manager, GrantExternalConditionPower power)
|
||||
{
|
||||
// Clear selection if using Left-Click Orders
|
||||
if (Game.Settings.Game.UseClassicMouseStyle)
|
||||
25
OpenRA.Mods.Common/Traits/Upgrades/ExternalConditions.cs
Normal file
25
OpenRA.Mods.Common/Traits/Upgrades/ExternalConditions.cs
Normal file
@@ -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<ExternalConditions>
|
||||
{
|
||||
[UpgradeGrantedReference]
|
||||
public readonly string[] Conditions = { };
|
||||
}
|
||||
|
||||
public class ExternalConditions { }
|
||||
}
|
||||
32
OpenRA.Mods.Common/Traits/Upgrades/StackedCondition.cs
Normal file
32
OpenRA.Mods.Common/Traits/Upgrades/StackedCondition.cs
Normal file
@@ -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<StackedCondition>
|
||||
{
|
||||
[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 { }
|
||||
}
|
||||
@@ -17,6 +17,13 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[RequireExplicitImplementation]
|
||||
public interface IConditionTimerWatcher
|
||||
{
|
||||
string Condition { get; }
|
||||
void Update(int duration, int remaining);
|
||||
}
|
||||
|
||||
[Desc("Attach this to a unit to enable dynamic upgrades by warheads, experience, crates, support powers, etc.")]
|
||||
public class UpgradeManagerInfo : TraitInfo<UpgradeManager>, Requires<IConditionConsumerInfo> { }
|
||||
|
||||
@@ -25,38 +32,16 @@ namespace OpenRA.Mods.Common.Traits
|
||||
/// <summary>Value used to represent an invalid token.</summary>
|
||||
public static readonly int InvalidConditionToken = -1;
|
||||
|
||||
class TimedCondition
|
||||
class ConditionTimer
|
||||
{
|
||||
public class ConditionSource
|
||||
{
|
||||
public readonly object Source;
|
||||
public int Remaining;
|
||||
|
||||
public ConditionSource(int duration, object source)
|
||||
{
|
||||
Remaining = duration;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string Condition;
|
||||
public readonly int Token;
|
||||
public readonly int Duration;
|
||||
public readonly HashSet<ConditionSource> Sources;
|
||||
public int Remaining; // Equal to maximum of all Sources.Remaining
|
||||
public int Remaining;
|
||||
|
||||
public TimedCondition(string condition, int duration, object source)
|
||||
public ConditionTimer(int token, int duration)
|
||||
{
|
||||
Condition = condition;
|
||||
Duration = duration;
|
||||
Remaining = duration;
|
||||
Sources = new HashSet<ConditionSource> { new ConditionSource(duration, source) };
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
Remaining--;
|
||||
foreach (var source in Sources)
|
||||
source.Remaining--;
|
||||
Token = token;
|
||||
Duration = Remaining = duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,23 +54,31 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public readonly HashSet<int> Tokens = new HashSet<int>();
|
||||
|
||||
/// <summary>External callbacks that are to be executed when a timed condition changes.</summary>
|
||||
public readonly List<Action<int, int>> Watchers = new List<Action<int, int>>();
|
||||
public readonly List<IConditionTimerWatcher> Watchers = new List<IConditionTimerWatcher>();
|
||||
}
|
||||
|
||||
readonly List<TimedCondition> timedConditions = new List<TimedCondition>();
|
||||
|
||||
Dictionary<string, ConditionState> state;
|
||||
readonly Dictionary<string, List<ConditionTimer>> timers = new Dictionary<string, List<ConditionTimer>>();
|
||||
|
||||
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
|
||||
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>
|
||||
readonly Dictionary<string, string[]> stackedConditions = new Dictionary<string, string[]>();
|
||||
|
||||
/// <summary>Tokens granted by the stacked condition bonuses defined in stackedConditions.</summary>
|
||||
readonly Dictionary<string, Stack<int>> stackedTokens = new Dictionary<string, Stack<int>>();
|
||||
|
||||
int nextToken = 1;
|
||||
|
||||
/// <summary>Temporary shim between the old and new upgrade/condition grant and revoke methods.</summary>
|
||||
Dictionary<Pair<object, string>, int> objectTokenShim = new Dictionary<Pair<object, string>, int>();
|
||||
readonly Dictionary<Pair<object, string>, int> objectTokenShim = new Dictionary<Pair<object, string>, int>();
|
||||
|
||||
/// <summary>Cache of condition -> enabled state for quick evaluation of boolean conditions.</summary>
|
||||
Dictionary<string, bool> conditionCache = new Dictionary<string, bool>();
|
||||
readonly Dictionary<string, bool> conditionCache = new Dictionary<string, bool>();
|
||||
|
||||
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
|
||||
IReadOnlyDictionary<string, bool> readOnlyConditionCache;
|
||||
@@ -96,12 +89,19 @@ namespace OpenRA.Mods.Common.Traits
|
||||
readOnlyConditionCache = new ReadOnlyDictionary<string, bool>(conditionCache);
|
||||
|
||||
var allConsumers = new HashSet<IConditionConsumer>();
|
||||
var allWatchers = self.TraitsImplementing<IConditionTimerWatcher>().ToList();
|
||||
|
||||
foreach (var consumer in self.TraitsImplementing<IConditionConsumer>())
|
||||
{
|
||||
allConsumers.Add(consumer);
|
||||
foreach (var condition in consumer.Conditions)
|
||||
{
|
||||
state.GetOrAdd(condition).Consumers.Add(consumer);
|
||||
var cs = state.GetOrAdd(condition);
|
||||
cs.Consumers.Add(consumer);
|
||||
foreach (var w in allWatchers)
|
||||
if (w.Condition == condition)
|
||||
cs.Watchers.Add(w);
|
||||
|
||||
conditionCache[condition] = false;
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,18 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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>())
|
||||
{
|
||||
stackedConditions[sc.Condition] = sc.StackedConditions;
|
||||
stackedTokens[sc.Condition] = new Stack<int>();
|
||||
}
|
||||
|
||||
// Update all traits with their initial condition state
|
||||
foreach (var consumer in allConsumers)
|
||||
consumer.ConditionsChanged(self, readOnlyConditionCache);
|
||||
@@ -137,15 +149,35 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Grants a specified condition.</summary>
|
||||
/// <returns>The token that is used to revoke this condition.</returns>
|
||||
public int GrantCondition(Actor self, string condition)
|
||||
/// <param name="external">Validate against the external condition whitelist.</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)
|
||||
{
|
||||
if (external && !externalConditions.Contains(condition))
|
||||
return InvalidConditionToken;
|
||||
|
||||
var token = nextToken++;
|
||||
tokens.Add(token, condition);
|
||||
|
||||
if (duration > 0)
|
||||
timers.GetOrAdd(condition).Add(new ConditionTimer(token, duration));
|
||||
|
||||
// Conditions may be granted before the state is initialized.
|
||||
// These conditions will be processed in INotifyCreated.Created.
|
||||
if (state != null)
|
||||
@@ -155,7 +187,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
/// <summary>Revokes a previously granted condition.</summary>
|
||||
/// <returns>The invalid token ID</returns>
|
||||
/// <returns>The invalid token ID.</returns>
|
||||
/// <param name="token">The token ID returned by GrantCondition.</param>
|
||||
public int RevokeCondition(Actor self, int token)
|
||||
{
|
||||
@@ -165,6 +197,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
tokens.Remove(token);
|
||||
|
||||
// Clean up timers
|
||||
List<ConditionTimer> ct;
|
||||
if (timers.TryGetValue(condition, out ct))
|
||||
{
|
||||
ct.RemoveAll(t => t.Token == token);
|
||||
if (!ct.Any())
|
||||
timers.Remove(condition);
|
||||
}
|
||||
|
||||
// Conditions may be granted and revoked before the state is initialized.
|
||||
if (state != null)
|
||||
UpdateConditionState(self, condition, token, true);
|
||||
@@ -172,6 +213,61 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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)
|
||||
{
|
||||
if (state == null)
|
||||
throw new InvalidOperationException("AcceptsExternalCondition cannot be queried before the actor has been fully created.");
|
||||
|
||||
if (!externalConditions.Contains(condition))
|
||||
return false;
|
||||
|
||||
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>
|
||||
public bool TokenValid(Actor self, int token)
|
||||
{
|
||||
return tokens.ContainsKey(token);
|
||||
}
|
||||
|
||||
readonly HashSet<int> timersToRemove = new HashSet<int>();
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
// 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 of the longest active instance.
|
||||
foreach (var kv in timers)
|
||||
{
|
||||
var duration = 0;
|
||||
var remaining = 0;
|
||||
foreach (var t in kv.Value)
|
||||
{
|
||||
if (--t.Remaining <= 0)
|
||||
timersToRemove.Add(t.Token);
|
||||
|
||||
// Track the duration and remaining time for the longest remaining timer
|
||||
if (t.Remaining > remaining)
|
||||
{
|
||||
duration = t.Duration;
|
||||
remaining = t.Remaining;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var w in state[kv.Key].Watchers)
|
||||
w.Update(duration, remaining);
|
||||
}
|
||||
|
||||
foreach (var t in timersToRemove)
|
||||
RevokeCondition(self, t);
|
||||
|
||||
timersToRemove.Clear();
|
||||
}
|
||||
|
||||
#region Shim methods for legacy upgrade granting code
|
||||
|
||||
void CheckCanManageConditions()
|
||||
@@ -180,36 +276,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
throw new InvalidOperationException("Conditions cannot be managed until the actor has been fully created.");
|
||||
}
|
||||
|
||||
/// <summary>Upgrade level increments are limited to dupesAllowed per source, i.e., if a single
|
||||
/// source attempts granting more upgrades than dupesAllowed, they will not accumulate. They will
|
||||
/// replace each other instead, leaving only the most recently granted upgrade active. Each new
|
||||
/// upgrade granting request will increment the upgrade's level until AcceptsUpgrade starts
|
||||
/// returning false. Then, when no new levels are accepted, the upgrade source with the shortest
|
||||
/// remaining upgrade duration will be replaced by the new source.</summary>
|
||||
public void GrantTimedUpgrade(Actor self, string upgrade, int duration, object source = null, int dupesAllowed = 1)
|
||||
{
|
||||
var timed = timedConditions.FirstOrDefault(u => u.Condition == upgrade);
|
||||
if (timed == null)
|
||||
{
|
||||
timed = new TimedCondition(upgrade, duration, source);
|
||||
timedConditions.Add(timed);
|
||||
GrantUpgrade(self, upgrade, timed);
|
||||
return;
|
||||
}
|
||||
|
||||
var srcs = timed.Sources.Where(s => s.Source == source);
|
||||
if (srcs.Count() < dupesAllowed)
|
||||
{
|
||||
timed.Sources.Add(new TimedCondition.ConditionSource(duration, source));
|
||||
if (AcceptsUpgrade(self, upgrade))
|
||||
GrantUpgrade(self, upgrade, timed);
|
||||
else
|
||||
timed.Sources.Remove(timed.Sources.MinBy(s => s.Remaining));
|
||||
}
|
||||
else
|
||||
srcs.MinBy(s => s.Remaining).Remaining = duration;
|
||||
|
||||
timed.Remaining = Math.Max(duration, timed.Remaining);
|
||||
CheckCanManageConditions();
|
||||
var token = GrantCondition(self, upgrade, false, duration);
|
||||
if (source != null)
|
||||
objectTokenShim[Pair.New(source, upgrade)] = token;
|
||||
}
|
||||
|
||||
public void GrantUpgrade(Actor self, string upgrade, object source)
|
||||
@@ -242,39 +314,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return !enabled;
|
||||
}
|
||||
|
||||
public void RegisterWatcher(string upgrade, Action<int, int> action)
|
||||
{
|
||||
CheckCanManageConditions();
|
||||
|
||||
ConditionState s;
|
||||
if (!state.TryGetValue(upgrade, out s))
|
||||
return;
|
||||
|
||||
s.Watchers.Add(action);
|
||||
}
|
||||
|
||||
/// <summary>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).</summary>
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
foreach (var u in timedConditions)
|
||||
{
|
||||
u.Tick();
|
||||
foreach (var source in u.Sources)
|
||||
if (source.Remaining <= 0)
|
||||
RevokeUpgrade(self, u.Condition, u);
|
||||
|
||||
u.Sources.RemoveWhere(source => source.Remaining <= 0);
|
||||
|
||||
foreach (var a in state[u.Condition].Watchers)
|
||||
a(u.Duration, u.Remaining);
|
||||
}
|
||||
|
||||
timedConditions.RemoveAll(u => u.Remaining <= 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,28 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
catch { }
|
||||
}
|
||||
|
||||
static void RenameNodeKey(MiniYamlNode node, string key)
|
||||
{
|
||||
var parts = node.Key.Split('@');
|
||||
node.Key = key;
|
||||
if (parts.Length > 1)
|
||||
node.Key += "@" + parts[1];
|
||||
}
|
||||
|
||||
static void ConvertUpgradesToCondition(MiniYamlNode parent, MiniYamlNode node, string upgradesKey, string conditionKey)
|
||||
{
|
||||
var upgradesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == upgradesKey);
|
||||
if (upgradesNode != null)
|
||||
{
|
||||
var conditions = FieldLoader.GetValue<string[]>("", upgradesNode.Value.Value);
|
||||
if (conditions.Length > 1)
|
||||
Console.WriteLine("Unable to automatically migrate {0}:{1} {2} to {3}. This must be corrected manually",
|
||||
parent.Key, node.Key, upgradesKey, conditionKey);
|
||||
else
|
||||
upgradesNode.Key = conditionKey;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpgradeActorRules(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
|
||||
{
|
||||
var addNodes = new List<MiniYamlNode>();
|
||||
@@ -118,10 +140,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
if (s != null)
|
||||
s.Key = "Image";
|
||||
|
||||
var parts = node.Key.Split('@');
|
||||
node.Key = "WithDamageOverlay";
|
||||
if (parts.Length > 1)
|
||||
node.Key += "@" + parts[1];
|
||||
RenameNodeKey(node, "WithDamageOverlay");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,13 +154,9 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
if (engineVersion < 20160611)
|
||||
{
|
||||
// Deprecated WithSpriteRotorOverlay
|
||||
if (depth == 1 && node.Key.StartsWith("WithSpriteRotorOverlay"))
|
||||
if (depth == 1 && node.Key.StartsWith("WithSpriteRotorOverlay", StringComparison.Ordinal))
|
||||
{
|
||||
var parts = node.Key.Split('@');
|
||||
node.Key = "WithIdleOverlay";
|
||||
if (parts.Length > 1)
|
||||
node.Key += "@" + parts[1];
|
||||
|
||||
RenameNodeKey(node, "WithIdleOverlay");
|
||||
Console.WriteLine("The 'WithSpriteRotorOverlay' trait has been removed.");
|
||||
Console.WriteLine("Its functionality can be fully replicated with 'WithIdleOverlay' + upgrades.");
|
||||
Console.WriteLine("Look at the helicopters in our RA / C&C1 mods for implementation details.");
|
||||
@@ -282,13 +297,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
|
||||
if (engineVersion < 20160818)
|
||||
{
|
||||
if (depth == 1 && node.Key.StartsWith("UpgradeOnDamage"))
|
||||
{
|
||||
var parts = node.Key.Split('@');
|
||||
node.Key = "UpgradeOnDamageState";
|
||||
if (parts.Length > 1)
|
||||
node.Key += "@" + parts[1];
|
||||
}
|
||||
if (depth == 1 && node.Key.StartsWith("UpgradeOnDamage", StringComparison.Ordinal))
|
||||
RenameNodeKey(node, "UpgradeOnDamageState");
|
||||
}
|
||||
|
||||
// DisplayTimer was replaced by DisplayTimerStances
|
||||
@@ -435,7 +445,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
}
|
||||
}
|
||||
|
||||
// Rename Replaced upgrade consumers with conditions
|
||||
// Replaced upgrade consumers with conditions
|
||||
if (engineVersion < 20161117)
|
||||
{
|
||||
var upgradeTypesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeTypes");
|
||||
@@ -480,16 +490,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
if (engineVersion < 20161119)
|
||||
{
|
||||
// Migrated carryalls over to new conditions system
|
||||
var carryableUpgradesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "CarryableUpgrades");
|
||||
if (carryableUpgradesNode != null)
|
||||
{
|
||||
var conditions = FieldLoader.GetValue<string[]>("", carryableUpgradesNode.Value.Value);
|
||||
if (conditions.Length > 1)
|
||||
Console.WriteLine("Unable to automatically migrate {0}:{1} CarryableUpgrades to CarriedCondition. This must be corrected manually",
|
||||
parent.Key, node.Key);
|
||||
else
|
||||
carryableUpgradesNode.Key = "CarriedCondition";
|
||||
}
|
||||
ConvertUpgradesToCondition(parent, node, "CarryableUpgrades", "CarriedCondition");
|
||||
|
||||
if (node.Key == "WithDecorationCarryable")
|
||||
{
|
||||
@@ -498,6 +499,39 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
}
|
||||
}
|
||||
|
||||
if (engineVersion < 20161120)
|
||||
{
|
||||
if (node.Key.StartsWith("TimedUpgradeBar", StringComparison.Ordinal))
|
||||
{
|
||||
RenameNodeKey(node, "TimedConditionBar");
|
||||
ConvertUpgradesToCondition(parent, node, "Upgrade", "Condition");
|
||||
}
|
||||
|
||||
if (node.Key.StartsWith("GrantUpgradePower", StringComparison.Ordinal))
|
||||
{
|
||||
Console.WriteLine("GrantUpgradePower Condition must be manually added to all target actor's ExternalConditions list.");
|
||||
RenameNodeKey(node, "GrantExternalConditionPower");
|
||||
ConvertUpgradesToCondition(parent, node, "Upgrades", "Condition");
|
||||
|
||||
var soundNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "GrantUpgradeSound");
|
||||
if (soundNode != null)
|
||||
soundNode.Key = "OnFireSound";
|
||||
else
|
||||
node.Value.Nodes.Add(new MiniYamlNode("OnFireSound", "ironcur9.aud"));
|
||||
|
||||
var sequenceNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "GrantUpgradeSequence");
|
||||
if (sequenceNode != null)
|
||||
sequenceNode.Key = "Sequence";
|
||||
}
|
||||
|
||||
if (node.Key.StartsWith("GrantUpgradeCrateAction", StringComparison.Ordinal))
|
||||
{
|
||||
Console.WriteLine("GrantUpgradeCrateAction Condition must be manually added to all target actor's ExternalConditions list.");
|
||||
RenameNodeKey(node, "GrantExternalConditionCrateAction");
|
||||
ConvertUpgradesToCondition(parent, node, "Upgrades", "Condition");
|
||||
}
|
||||
}
|
||||
|
||||
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||
}
|
||||
|
||||
@@ -567,6 +601,16 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
node.Key = "LaunchAngle";
|
||||
}
|
||||
|
||||
if (engineVersion < 20161120)
|
||||
{
|
||||
if (node.Key.StartsWith("Warhead", StringComparison.Ordinal) && node.Value.Value == "GrantUpgrade")
|
||||
{
|
||||
node.Value.Value = "GrantExternalCondition";
|
||||
Console.WriteLine("GrantExternalCondition Condition must be manually added to all target actor's ExternalConditions list.");
|
||||
ConvertUpgradesToCondition(parent, node, "Upgrades", "Condition");
|
||||
}
|
||||
}
|
||||
|
||||
UpgradeWeaponRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,18 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Warheads
|
||||
{
|
||||
public class GrantUpgradeWarhead : Warhead
|
||||
public class GrantExternalConditionWarhead : Warhead
|
||||
{
|
||||
[UpgradeGrantedReference]
|
||||
[Desc("The upgrades to apply.")]
|
||||
public readonly string[] Upgrades = { };
|
||||
[FieldLoader.Require]
|
||||
[Desc("The condition to apply. Must be included in the target actor's ExternalConditions list.")]
|
||||
public readonly string Condition = null;
|
||||
|
||||
[Desc("Duration of the upgrade (in ticks). Set to 0 for a permanent upgrade.")]
|
||||
[Desc("Duration of the condition (in ticks). Set to 0 for a permanent condition.")]
|
||||
public readonly int Duration = 0;
|
||||
|
||||
public readonly WDist Range = WDist.FromCells(1);
|
||||
@@ -38,22 +37,10 @@ namespace OpenRA.Mods.Common.Warheads
|
||||
continue;
|
||||
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
if (um == null)
|
||||
continue;
|
||||
|
||||
foreach (var u in Upgrades)
|
||||
{
|
||||
if (Duration > 0)
|
||||
{
|
||||
if (um.AcknowledgesUpgrade(a, u))
|
||||
um.GrantTimedUpgrade(a, u, Duration, firedBy, Upgrades.Count(upg => upg == u));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (um.AcceptsUpgrade(a, u))
|
||||
um.GrantUpgrade(a, u, this);
|
||||
}
|
||||
}
|
||||
// Condition token is ignored because we never revoke this condition.
|
||||
if (um != null && um.AcceptsExternalCondition(a, Condition))
|
||||
um.GrantCondition(a, Condition, true, Duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,8 @@
|
||||
CloakSound: trans1.aud
|
||||
UncloakSound: trans1.aud
|
||||
RequiresCondition: cloak
|
||||
ExternalConditions@CLOAK:
|
||||
Conditions: cloak
|
||||
MustBeDestroyed:
|
||||
Voiced:
|
||||
VoiceSet: VehicleVoice
|
||||
|
||||
@@ -16,10 +16,10 @@ CRATE:
|
||||
ExplodeCrateAction@fire:
|
||||
Weapon: Napalm.Crate
|
||||
SelectionShares: 5
|
||||
GrantUpgradeCrateAction@cloak:
|
||||
GrantExternalConditionCrateAction@cloak:
|
||||
SelectionShares: 5
|
||||
Effect: cloak
|
||||
Upgrades: cloak
|
||||
Condition: cloak
|
||||
GiveMcvCrateAction:
|
||||
SelectionShares: 0
|
||||
NoBaseSelectionShares: 120
|
||||
|
||||
@@ -119,7 +119,7 @@ FTUR:
|
||||
BeginChargeSound: chrochr1.aud
|
||||
EndChargeSound: chrordy1.aud
|
||||
Range: 3
|
||||
GrantUpgradePower@IRONCURTAIN:
|
||||
GrantExternalConditionPower@IRONCURTAIN:
|
||||
Icon: invuln
|
||||
ChargeTime: 30
|
||||
Description: Invulnerability
|
||||
@@ -129,8 +129,9 @@ FTUR:
|
||||
BeginChargeSound: ironchg1.aud
|
||||
EndChargeSound: ironrdy1.aud
|
||||
Range: 1
|
||||
Upgrades: invulnerability
|
||||
GrantUpgradeSequence: idle
|
||||
Condition: invulnerability
|
||||
Sequence: idle
|
||||
OnFireSound: ironcur9.aud
|
||||
Power:
|
||||
Amount: 0
|
||||
|
||||
@@ -186,7 +187,7 @@ T17:
|
||||
Duration: 999999
|
||||
KillCargo: yes
|
||||
Range: 3
|
||||
GrantUpgradePower@IRONCURTAIN:
|
||||
GrantExternalConditionPower@IRONCURTAIN:
|
||||
Icon: invuln
|
||||
ChargeTime: 30
|
||||
Description: Invulnerability
|
||||
@@ -196,5 +197,6 @@ T17:
|
||||
BeginChargeSound: ironchg1.aud
|
||||
EndChargeSound: ironrdy1.aud
|
||||
Range: 1
|
||||
Upgrades: invulnerability
|
||||
GrantUpgradeSequence: idle
|
||||
Condition: invulnerability
|
||||
Sequence: idle
|
||||
OnFireSound: ironcur9.aud
|
||||
|
||||
@@ -69,11 +69,11 @@ FORTCRATE:
|
||||
GiveUnitCrateAction@e7:
|
||||
Units: e7
|
||||
SelectionShares: 10
|
||||
GrantUpgradeCrateAction@ironcurtain:
|
||||
GrantExternalConditionCrateAction@ironcurtain:
|
||||
SelectionShares: 10
|
||||
Effect: invuln
|
||||
Notification: ironcur9.aud
|
||||
Upgrades: invulnerability
|
||||
Condition: invulnerability
|
||||
Duration: 1200
|
||||
ExplodeCrateAction@bigboom:
|
||||
Weapon: SCUD
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
^GainsExperience:
|
||||
GainsExperience:
|
||||
Upgrades:
|
||||
Upgrades:
|
||||
200: rank-veteran-1
|
||||
400: rank-veteran-2
|
||||
800: rank-veteran-3
|
||||
@@ -121,8 +121,10 @@
|
||||
DamageMultiplier@IRONCURTAIN:
|
||||
RequiresCondition: invulnerability
|
||||
Modifier: 0
|
||||
TimedUpgradeBar:
|
||||
Upgrade: invulnerability
|
||||
TimedConditionBar:
|
||||
Condition: invulnerability
|
||||
ExternalConditions@INVULNERABILITY:
|
||||
Conditions: invulnerability
|
||||
|
||||
^Vehicle:
|
||||
Inherits@1: ^ExistsInWorld
|
||||
|
||||
@@ -90,11 +90,11 @@ CRATE:
|
||||
Units: e1,e1,e4,e4,e3,e3,e3
|
||||
ValidFactions: soviet, russia, ukraine
|
||||
TimeDelay: 4500
|
||||
GrantUpgradeCrateAction@invuln:
|
||||
GrantExternalConditionCrateAction@invuln:
|
||||
SelectionShares: 5
|
||||
Effect: invuln
|
||||
Notification: ironcur9.aud
|
||||
Upgrades: invulnerability
|
||||
Condition: invulnerability
|
||||
Duration: 600
|
||||
|
||||
MONEYCRATE:
|
||||
|
||||
@@ -303,7 +303,7 @@ IRON:
|
||||
Range: 10c0
|
||||
Bib:
|
||||
HasMinibib: Yes
|
||||
GrantUpgradePower@IRONCURTAIN:
|
||||
GrantExternalConditionPower@IRONCURTAIN:
|
||||
Icon: invuln
|
||||
ChargeTime: 120
|
||||
Description: Invulnerability
|
||||
@@ -314,7 +314,8 @@ IRON:
|
||||
BeginChargeSpeechNotification: IronCurtainCharging
|
||||
EndChargeSpeechNotification: IronCurtainReady
|
||||
DisplayRadarPing: True
|
||||
Upgrades: invulnerability
|
||||
Condition: invulnerability
|
||||
OnFireSound: ironcur9.aud
|
||||
SupportPowerChargeBar:
|
||||
Power:
|
||||
Amount: -200
|
||||
|
||||
@@ -102,7 +102,7 @@ BUS:
|
||||
MaxWeight: 20
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
|
||||
PICK:
|
||||
@@ -126,7 +126,7 @@ PICK:
|
||||
MaxWeight: 2
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
|
||||
CAR:
|
||||
@@ -150,7 +150,7 @@ CAR:
|
||||
MaxWeight: 4
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
|
||||
WINI:
|
||||
@@ -174,7 +174,7 @@ WINI:
|
||||
MaxWeight: 5
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
|
||||
LOCOMOTIVE:
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
ReferencePoint: Bottom, Right
|
||||
RequiresCondition: rank-elite
|
||||
ZOffset: 256
|
||||
ExternalConditions@CRATES:
|
||||
Conditions: crate-firepower, crate-damage, crate-speed, crate-cloak
|
||||
|
||||
^EmpDisable:
|
||||
UpgradeOverlay@EMPDISABLE:
|
||||
@@ -71,8 +73,8 @@
|
||||
Palette: disabled
|
||||
DisableOnUpgrade@EMPDISABLE:
|
||||
RequiresCondition: empdisable
|
||||
TimedUpgradeBar@EMPDISABLE:
|
||||
Upgrade: empdisable
|
||||
TimedConditionBar@EMPDISABLE:
|
||||
Condition: empdisable
|
||||
Color: FFFFFF
|
||||
WithIdleOverlay@EMPDISABLE:
|
||||
Sequence: emp-overlay
|
||||
@@ -83,11 +85,13 @@
|
||||
PowerMultiplier@EMPDISABLE:
|
||||
RequiresCondition: empdisable
|
||||
Modifier: 0
|
||||
ExternalConditions@EMPDISABLE:
|
||||
Conditions: empdisable
|
||||
|
||||
^EmpDisableMobile:
|
||||
Inherits: ^EmpDisable
|
||||
Mobile:
|
||||
RequiresCondition: !notmobile
|
||||
RequiresCondition: !empdisable && !deployed && !loading
|
||||
|
||||
^Cloakable:
|
||||
Cloak@CLOAKGENERATOR:
|
||||
@@ -639,7 +643,6 @@
|
||||
Mobile:
|
||||
Speed: 113
|
||||
TurnSpeed: 16
|
||||
Crushes: crate
|
||||
SharesCell: no
|
||||
TerrainSpeeds:
|
||||
Clear: 90
|
||||
@@ -795,7 +798,7 @@
|
||||
Cargo:
|
||||
Types: Infantry
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
Health:
|
||||
HP: 100
|
||||
Armor:
|
||||
|
||||
@@ -28,7 +28,7 @@ APC:
|
||||
MaxWeight: 5
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
UpgradeOnTerrain:
|
||||
Upgrades: inwater
|
||||
|
||||
@@ -74,24 +74,24 @@ CRATE:
|
||||
SelectionShares: 0
|
||||
NoBaseSelectionShares: 100
|
||||
Units: mcv
|
||||
GrantUpgradeCrateAction@cloak:
|
||||
GrantExternalConditionCrateAction@cloak:
|
||||
SelectionShares: 5
|
||||
Effect: stealth
|
||||
Upgrades: crate-cloak
|
||||
Condition: crate-cloak
|
||||
Notification: cloak5.aud
|
||||
GrantUpgradeCrateAction@firepower:
|
||||
GrantExternalConditionCrateAction@firepower:
|
||||
SelectionShares: 5
|
||||
Effect: firepower
|
||||
Upgrades: crate-firepower
|
||||
Condition: crate-firepower
|
||||
Notification: 00-i070.aud
|
||||
GrantUpgradeCrateAction@armor:
|
||||
GrantExternalConditionCrateAction@armor:
|
||||
SelectionShares: 5
|
||||
Effect: armor
|
||||
Upgrades: crate-damage
|
||||
Condition: crate-damage
|
||||
Notification: 00-i068.aud
|
||||
GrantUpgradeCrateAction@speed:
|
||||
GrantExternalConditionCrateAction@speed:
|
||||
SelectionShares: 5
|
||||
Upgrades: crate-speed
|
||||
Condition: crate-speed
|
||||
Notification: 00-i080.aud
|
||||
|
||||
SROCK01:
|
||||
|
||||
@@ -106,7 +106,7 @@ TTNK:
|
||||
RenderSprites:
|
||||
Image: ttnk
|
||||
DeployToUpgrade:
|
||||
DeployedUpgrades: deployed, notmobile
|
||||
DeployedUpgrades: deployed
|
||||
UndeployedUpgrades: undeployed
|
||||
DeployAnimation: make
|
||||
Facing: 160
|
||||
@@ -285,7 +285,7 @@ SAPC:
|
||||
MaxWeight: 5
|
||||
PipCount: 5
|
||||
UnloadVoice: Unload
|
||||
LoadingUpgrades: notmobile
|
||||
LoadingUpgrades: loading
|
||||
EjectOnDeath: true
|
||||
|
||||
SUBTANK:
|
||||
|
||||
@@ -138,7 +138,7 @@ LPST:
|
||||
gdi: lpst.gdi
|
||||
nod: lpst.nod
|
||||
DeployToUpgrade:
|
||||
DeployedUpgrades: deployed, notmobile
|
||||
DeployedUpgrades: deployed
|
||||
UndeployedUpgrades: undeployed
|
||||
DeployAnimation: make
|
||||
Facing: 160
|
||||
|
||||
@@ -107,10 +107,10 @@ EMPulseCannon:
|
||||
Image: pulsball
|
||||
Warhead@1Eff: CreateEffect
|
||||
Explosions: pulse_explosion
|
||||
Warhead@emp: GrantUpgrade
|
||||
Warhead@emp: GrantExternalCondition
|
||||
Range: 4c0
|
||||
Duration: 250
|
||||
Upgrades: empdisable, notmobile
|
||||
Condition: empdisable
|
||||
|
||||
ClusterMissile:
|
||||
ValidTargets: Ground, Water, Air
|
||||
|
||||
Reference in New Issue
Block a user