Merge pull request #12381 from pchote/upgrade-conditions

Upgrades overhaul part 1: EnabledCondition
This commit is contained in:
reaperrr
2016-11-30 19:42:34 +01:00
committed by GitHub
79 changed files with 947 additions and 971 deletions

View File

@@ -15,12 +15,14 @@ using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA namespace OpenRA
{ {
@@ -396,6 +398,22 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
else if (fieldType == typeof(BooleanExpression))
{
if (value != null)
{
try
{
return new BooleanExpression(value);
}
catch (InvalidDataException e)
{
throw new YamlException(e.Message);
}
}
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType.IsEnum) else if (fieldType.IsEnum)
{ {
try try

View File

@@ -18,6 +18,7 @@ namespace OpenRA.Support
{ {
public class BooleanExpression public class BooleanExpression
{ {
public readonly string Expression;
readonly HashSet<string> variables = new HashSet<string>(); readonly HashSet<string> variables = new HashSet<string>();
public IEnumerable<string> Variables { get { return variables; } } public IEnumerable<string> Variables { get { return variables; } }
@@ -68,6 +69,7 @@ namespace OpenRA.Support
public BooleanExpression(string expression) public BooleanExpression(string expression)
{ {
Expression = expression;
var openParens = 0; var openParens = 0;
var closeParens = 0; var closeParens = 0;
var tokens = new List<Token>(); var tokens = new List<Token>();
@@ -220,7 +222,7 @@ namespace OpenRA.Support
return new VariableToken(start, expression.Substring(start)); return new VariableToken(start, expression.Substring(start));
} }
static bool ParseSymbol(VariableToken t, Dictionary<string, bool> symbols) static bool ParseSymbol(VariableToken t, IReadOnlyDictionary<string, bool> symbols)
{ {
bool value; bool value;
symbols.TryGetValue(t.Symbol, out value); symbols.TryGetValue(t.Symbol, out value);
@@ -269,7 +271,7 @@ namespace OpenRA.Support
yield return s.Pop(); yield return s.Pop();
} }
public bool Evaluate(Dictionary<string, bool> symbols) public bool Evaluate(IReadOnlyDictionary<string, bool> symbols)
{ {
var s = new Stack<bool>(); var s = new Stack<bool>();
foreach (var t in postfix) foreach (var t in postfix)

View File

@@ -47,7 +47,11 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly string OnFireSound = null; public readonly string OnFireSound = null;
public override object Create(ActorInitializer init) { return new IonCannonPower(init.Self, this); } public override object Create(ActorInitializer init) { return new IonCannonPower(init.Self, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; } public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()];
base.RulesetLoaded(rules, ai);
}
} }
class IonCannonPower : SupportPower class IonCannonPower : SupportPower

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.AI namespace OpenRA.Mods.Common.AI
@@ -193,7 +194,7 @@ namespace OpenRA.Mods.Common.AI
bool HasSufficientPowerForActor(ActorInfo actorInfo) bool HasSufficientPowerForActor(ActorInfo actorInfo)
{ {
return (actorInfo.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1) return (actorInfo.TraitInfos<PowerInfo>().Where(i => i.EnabledByDefault)
.Sum(p => p.Amount) + playerPower.ExcessPower) >= ai.Info.MinimumExcessPower; .Sum(p => p.Amount) + playerPower.ExcessPower) >= ai.Info.MinimumExcessPower;
} }
@@ -203,12 +204,12 @@ namespace OpenRA.Mods.Common.AI
// This gets used quite a bit, so let's cache it here // This gets used quite a bit, so let's cache it here
var power = GetProducibleBuilding(ai.Info.BuildingCommonNames.Power, buildableThings, var power = GetProducibleBuilding(ai.Info.BuildingCommonNames.Power, buildableThings,
a => a.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); a => a.TraitInfos<PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount));
// First priority is to get out of a low power situation // First priority is to get out of a low power situation
if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) if (playerPower.ExcessPower < ai.Info.MinimumExcessPower)
{ {
if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0)
{ {
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name);
return power; return power;
@@ -314,7 +315,7 @@ namespace OpenRA.Mods.Common.AI
if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor))
{ {
// Try building a power plant instead // Try building a power plant instead
if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0)
{ {
if (playerPower.PowerOutageRemainingTicks > 0) if (playerPower.PowerOutageRemainingTicks > 0)
HackyAI.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); HackyAI.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name);

View File

@@ -1,166 +0,0 @@
#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 System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
public class CheckUpgrades : ILintRulesPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)
{
CheckUpgradesValidity(emitError, rules);
CheckUpgradesUsage(emitError, emitWarning, rules);
}
static void CheckUpgradesValidity(Action<string> emitError, Ruleset rules)
{
var upgradesGranted = GetAllGrantedUpgrades(emitError, rules).ToHashSet();
foreach (var actorInfo in rules.Actors)
{
if (actorInfo.Key.StartsWith("^"))
continue;
foreach (var trait in actorInfo.Value.TraitInfos<ITraitInfo>())
{
var fields = trait.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeUsedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(trait, field, emitError);
foreach (var value in values)
{
if (!upgradesGranted.Contains(value))
emitError("Actor type `{0}` uses upgrade `{1}` that is not granted by anything!".F(actorInfo.Key, value));
if (actorInfo.Value.TraitInfoOrDefault<UpgradeManagerInfo>() == null)
emitError("Actor type `{0}` uses upgrade `{1}`, but doesn't have the UpgradeManager trait.".F(actorInfo.Key, value));
}
}
}
}
}
static void CheckUpgradesUsage(Action<string> emitError, Action<string> emitWarning, Ruleset rules)
{
var upgradesUsed = GetAllUsedUpgrades(emitError, rules).ToHashSet();
// Check all upgrades granted by traits.
foreach (var actorInfo in rules.Actors)
{
if (actorInfo.Key.StartsWith("^"))
continue;
foreach (var trait in actorInfo.Value.TraitInfos<ITraitInfo>())
{
var fields = trait.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeGrantedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(trait, field, emitError);
foreach (var value in values.Where(x => !upgradesUsed.Contains(x)))
emitWarning("Actor type `{0}` grants upgrade `{1}` that is not used by anything!".F(actorInfo.Key, value));
}
}
}
// Check all upgrades granted by warheads.
foreach (var weapon in rules.Weapons)
{
foreach (var warhead in weapon.Value.Warheads)
{
var fields = warhead.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeGrantedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(warhead, field, emitError);
foreach (var value in values.Where(x => !upgradesUsed.Contains(x)))
emitWarning("Weapon type `{0}` grants upgrade `{1}` that is not used by anything!".F(weapon.Key, value));
}
}
}
}
static IEnumerable<string> GetAllGrantedUpgrades(Action<string> emitError, Ruleset rules)
{
// Get all upgrades granted by traits.
foreach (var actorInfo in rules.Actors)
{
foreach (var trait in actorInfo.Value.TraitInfos<ITraitInfo>())
{
var fields = trait.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeGrantedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(trait, field, emitError);
foreach (var value in values)
yield return value;
}
}
}
// Get all upgrades granted by warheads.
foreach (var weapon in rules.Weapons)
{
foreach (var warhead in weapon.Value.Warheads)
{
var fields = warhead.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeGrantedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(warhead, field, emitError);
foreach (var value in values)
yield return value;
}
}
}
// TODO: HACK because GainsExperience grants upgrades differently to most other sources.
var gainsExperience = rules.Actors.SelectMany(x => x.Value.TraitInfos<GainsExperienceInfo>()
.SelectMany(y => y.Upgrades.SelectMany(z => z.Value)));
foreach (var upgrade in gainsExperience)
yield return upgrade;
// TODO: HACK because Pluggable grants upgrades differently to most other sources.
var pluggable = rules.Actors.SelectMany(x => x.Value.TraitInfos<PluggableInfo>()
.SelectMany(y => y.Upgrades.SelectMany(z => z.Value)));
foreach (var upgrade in pluggable)
yield return upgrade;
}
static IEnumerable<string> GetAllUsedUpgrades(Action<string> emitError, Ruleset rules)
{
foreach (var actorInfo in rules.Actors)
{
foreach (var trait in actorInfo.Value.TraitInfos<ITraitInfo>())
{
var fields = trait.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<UpgradeUsedReferenceAttribute>()))
{
var values = LintExts.GetFieldValues(trait, field, emitError);
foreach (var value in values)
yield return value;
}
}
}
// TODO: HACK because GainsExperience and GainsStatUpgrades do not play by the rules...
// We assume everything GainsExperience grants is used by GainsStatUpgrade
var gainsExperience = rules.Actors.SelectMany(x => x.Value.TraitInfos<GainsExperienceInfo>()
.SelectMany(y => y.Upgrades.SelectMany(z => z.Value)));
foreach (var upgrade in gainsExperience)
yield return upgrade;
}
}
}

View File

@@ -11,7 +11,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using OpenRA.Support;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
@@ -26,8 +28,13 @@ namespace OpenRA.Mods.Common.Lint
return (string[])fieldInfo.GetValue(ruleInfo); return (string[])fieldInfo.GetValue(ruleInfo);
if (type == typeof(HashSet<string>)) if (type == typeof(HashSet<string>))
return (HashSet<string>)fieldInfo.GetValue(ruleInfo); return (HashSet<string>)fieldInfo.GetValue(ruleInfo);
if (type == typeof(BooleanExpression))
{
var expr = (BooleanExpression)fieldInfo.GetValue(ruleInfo);
return expr != null ? expr.Variables : Enumerable.Empty<string>();
}
emitError("Bad type for reference on {0}.{1}. Supported types: string, string[], HashSet<string>" emitError("Bad type for reference on {0}.{1}. Supported types: string, string[], HashSet<string>, BooleanExpression"
.F(ruleInfo.GetType().Name, fieldInfo.Name)); .F(ruleInfo.GetType().Name, fieldInfo.Name));
return new string[] { }; return new string[] { };

View File

@@ -181,7 +181,6 @@
<Compile Include="Lint\CheckDeathTypes.cs" /> <Compile Include="Lint\CheckDeathTypes.cs" />
<Compile Include="Lint\CheckRangeLimit.cs" /> <Compile Include="Lint\CheckRangeLimit.cs" />
<Compile Include="Lint\CheckVoiceReferences.cs" /> <Compile Include="Lint\CheckVoiceReferences.cs" />
<Compile Include="Lint\CheckUpgrades.cs" />
<Compile Include="Lint\CheckTargetHealthRadius.cs" /> <Compile Include="Lint\CheckTargetHealthRadius.cs" />
<Compile Include="Lint\LintBuildablePrerequisites.cs" /> <Compile Include="Lint\LintBuildablePrerequisites.cs" />
<Compile Include="Lint\LintExts.cs" /> <Compile Include="Lint\LintExts.cs" />
@@ -357,7 +356,6 @@
<Compile Include="Traits\Parachutable.cs" /> <Compile Include="Traits\Parachutable.cs" />
<Compile Include="Traits\ParaDrop.cs" /> <Compile Include="Traits\ParaDrop.cs" />
<Compile Include="Traits\Passenger.cs" /> <Compile Include="Traits\Passenger.cs" />
<Compile Include="Traits\Multipliers\UpgradableMultiplierTrait.cs" />
<Compile Include="Traits\Multipliers\DamageMultiplier.cs" /> <Compile Include="Traits\Multipliers\DamageMultiplier.cs" />
<Compile Include="Traits\Multipliers\FirepowerMultiplier.cs" /> <Compile Include="Traits\Multipliers\FirepowerMultiplier.cs" />
<Compile Include="Traits\Multipliers\InaccuracyMultiplier.cs" /> <Compile Include="Traits\Multipliers\InaccuracyMultiplier.cs" />
@@ -443,7 +441,6 @@
<Compile Include="Traits\Render\WithMuzzleOverlay.cs" /> <Compile Include="Traits\Render\WithMuzzleOverlay.cs" />
<Compile Include="Traits\Render\WithParachute.cs" /> <Compile Include="Traits\Render\WithParachute.cs" />
<Compile Include="Traits\Render\WithRangeCircle.cs" /> <Compile Include="Traits\Render\WithRangeCircle.cs" />
<Compile Include="Traits\Render\WithRankDecoration.cs" />
<Compile Include="Traits\Render\WithRearmAnimation.cs" /> <Compile Include="Traits\Render\WithRearmAnimation.cs" />
<Compile Include="Traits\Render\WithRepairAnimation.cs" /> <Compile Include="Traits\Render\WithRepairAnimation.cs" />
<Compile Include="Traits\Render\WithRepairOverlay.cs" /> <Compile Include="Traits\Render\WithRepairOverlay.cs" />

View File

@@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits
} }
[Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")] [Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")]
public class ArmamentInfo : UpgradableTraitInfo, IRulesetLoaded, Requires<AttackBaseInfo> public class ArmamentInfo : UpgradableTraitInfo, Requires<AttackBaseInfo>
{ {
public readonly string Name = "primary"; public readonly string Name = "primary";
@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new Armament(init.Self, this); } public override object Create(ActorInitializer init) { return new Armament(init.Self, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{ {
WeaponInfo weaponInfo; WeaponInfo weaponInfo;
@@ -91,10 +91,12 @@ namespace OpenRA.Mods.Common.Traits
ModifiedRange = new WDist(Util.ApplyPercentageModifiers( ModifiedRange = new WDist(Util.ApplyPercentageModifiers(
WeaponInfo.Range.Length, WeaponInfo.Range.Length,
ai.TraitInfos<IRangeModifierInfo>().Select(m => m.GetRangeModifierDefault()))); ai.TraitInfos<IRangeModifierInfo>().Select(m => m.GetRangeModifierDefault())));
base.RulesetLoaded(rules, ai);
} }
} }
public class Armament : UpgradableTrait<ArmamentInfo>, INotifyCreated, ITick, IExplodeModifier public class Armament : UpgradableTrait<ArmamentInfo>, ITick, IExplodeModifier
{ {
public readonly WeaponInfo Weapon; public readonly WeaponInfo Weapon;
public readonly Barrel[] Barrels; public readonly Barrel[] Barrels;
@@ -139,12 +141,14 @@ namespace OpenRA.Mods.Common.Traits
return new WDist(Util.ApplyPercentageModifiers(Weapon.Range.Length, rangeModifiers)); return new WDist(Util.ApplyPercentageModifiers(Weapon.Range.Length, rangeModifiers));
} }
protected virtual void Created(Actor self) protected override void Created(Actor self)
{ {
turret = self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == Info.Turret); turret = self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == Info.Turret);
ammoPool = self.TraitsImplementing<AmmoPool>().FirstOrDefault(la => la.Info.Name == Info.AmmoPoolName); ammoPool = self.TraitsImplementing<AmmoPool>().FirstOrDefault(la => la.Info.Name == Info.AmmoPoolName);
coords = self.Trait<BodyOrientation>(); coords = self.Trait<BodyOrientation>();
rangeModifiers = self.TraitsImplementing<IRangeModifier>().ToArray().Select(m => m.GetRangeModifier()); rangeModifiers = self.TraitsImplementing<IRangeModifier>().ToArray().Select(m => m.GetRangeModifier());
base.Created(self);
} }
protected virtual void Tick(Actor self) protected virtual void Tick(Actor self)
@@ -168,12 +172,6 @@ namespace OpenRA.Mods.Common.Traits
delayedActions.RemoveAll(a => a.First <= 0); delayedActions.RemoveAll(a => a.First <= 0);
} }
void INotifyCreated.Created(Actor self)
{
// Split into a protected method to allow subclassing
Created(self);
}
void ITick.Tick(Actor self) void ITick.Tick(Actor self)
{ {
// Split into a protected method to allow subclassing // Split into a protected method to allow subclassing

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits
[PaletteReference] public readonly string MuzzlePalette = "effect"; [PaletteReference] public readonly string MuzzlePalette = "effect";
public override object Create(ActorInitializer init) { return new AttackGarrisoned(init.Self, this); } public override object Create(ActorInitializer init) { return new AttackGarrisoned(init.Self, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{ {
if (PortOffsets.Length == 0) if (PortOffsets.Length == 0)
throw new YamlException("PortOffsets must have at least one entry."); throw new YamlException("PortOffsets must have at least one entry.");
@@ -67,6 +67,8 @@ namespace OpenRA.Mods.Common.Traits
Cone = PortCones[i], Cone = PortCones[i],
}; };
} }
base.RulesetLoaded(rules, ai);
} }
} }

View File

@@ -20,7 +20,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("This actor can transport Passenger actors.")] [Desc("This actor can transport Passenger actors.")]
public class CargoInfo : ITraitInfo, Requires<IOccupySpaceInfo>, Requires<UpgradeManagerInfo> public class CargoInfo : ITraitInfo, Requires<IOccupySpaceInfo>
{ {
[Desc("The maximum sum of Passenger.Weight that this actor can support.")] [Desc("The maximum sum of Passenger.Weight that this actor can support.")]
public readonly int MaxWeight = 0; public readonly int MaxWeight = 0;
@@ -67,7 +67,6 @@ namespace OpenRA.Mods.Common.Traits
{ {
public readonly CargoInfo Info; public readonly CargoInfo Info;
readonly Actor self; readonly Actor self;
readonly UpgradeManager upgradeManager;
readonly Stack<Actor> cargo = new Stack<Actor>(); readonly Stack<Actor> cargo = new Stack<Actor>();
readonly HashSet<Actor> reserves = new HashSet<Actor>(); readonly HashSet<Actor> reserves = new HashSet<Actor>();
readonly Lazy<IFacing> facing; readonly Lazy<IFacing> facing;
@@ -76,6 +75,7 @@ namespace OpenRA.Mods.Common.Traits
int totalWeight = 0; int totalWeight = 0;
int reservedWeight = 0; int reservedWeight = 0;
Aircraft aircraft; Aircraft aircraft;
UpgradeManager upgradeManager;
CPos currentCell; CPos currentCell;
public IEnumerable<CPos> CurrentAdjacentCells { get; private set; } public IEnumerable<CPos> CurrentAdjacentCells { get; private set; }
@@ -89,7 +89,6 @@ namespace OpenRA.Mods.Common.Traits
Info = info; Info = info;
Unloading = false; Unloading = false;
checkTerrainType = info.UnloadTerrainTypes.Count > 0; checkTerrainType = info.UnloadTerrainTypes.Count > 0;
upgradeManager = self.Trait<UpgradeManager>();
if (init.Contains<RuntimeCargoInit>()) if (init.Contains<RuntimeCargoInit>())
{ {
@@ -127,6 +126,7 @@ namespace OpenRA.Mods.Common.Traits
void INotifyCreated.Created(Actor self) void INotifyCreated.Created(Actor self)
{ {
aircraft = self.TraitOrDefault<Aircraft>(); aircraft = self.TraitOrDefault<Aircraft>();
upgradeManager = self.Trait<UpgradeManager>();
} }
static int GetWeight(Actor a) { return a.Info.TraitInfo<PassengerInfo>().Weight; } static int GetWeight(Actor a) { return a.Info.TraitInfo<PassengerInfo>().Weight; }

View File

@@ -15,7 +15,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("Can be carried by actors with the `Carryall` trait.")] [Desc("Can be carried by actors with the `Carryall` trait.")]
public class CarryableInfo : ITraitInfo, Requires<UpgradeManagerInfo> public class CarryableInfo : ITraitInfo
{ {
[UpgradeGrantedReference] [UpgradeGrantedReference]
[Desc("The upgrades to grant to self while waiting or being carried.")] [Desc("The upgrades to grant to self while waiting or being carried.")]
@@ -27,10 +27,10 @@ namespace OpenRA.Mods.Common.Traits
public virtual object Create(ActorInitializer init) { return new Carryable(init.Self, this); } public virtual object Create(ActorInitializer init) { return new Carryable(init.Self, this); }
} }
public class Carryable public class Carryable : INotifyCreated
{ {
readonly CarryableInfo info; readonly CarryableInfo info;
readonly UpgradeManager upgradeManager; UpgradeManager upgradeManager;
public Actor Carrier { get; private set; } public Actor Carrier { get; private set; }
public bool Reserved { get { return state != State.Free; } } public bool Reserved { get { return state != State.Free; } }
@@ -44,6 +44,10 @@ namespace OpenRA.Mods.Common.Traits
public Carryable(Actor self, CarryableInfo info) public Carryable(Actor self, CarryableInfo info)
{ {
this.info = info; this.info = info;
}
void INotifyCreated.Created(Actor self)
{
upgradeManager = self.Trait<UpgradeManager>(); upgradeManager = self.Trait<UpgradeManager>();
} }

View File

@@ -29,10 +29,10 @@ namespace OpenRA.Mods.Common.Traits
public void AddedToWorld(Actor self) public void AddedToWorld(Actor self)
{ {
if (!IsTraitDisabled) if (!IsTraitDisabled)
UpgradeEnabled(self); TraitEnabled(self);
} }
protected override void UpgradeEnabled(Actor self) protected override void TraitEnabled(Actor self)
{ {
if (self.IsDead) if (self.IsDead)
return; return;

View File

@@ -13,19 +13,25 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("Damage taken by this actor is multiplied based on upgrade level.", [Desc("Modifies the damage applied to this actor.",
"Decrease to increase actor's apparent strength.",
"Use 0 to make actor invulnerable.")] "Use 0 to make actor invulnerable.")]
public class DamageMultiplierInfo : UpgradeMultiplierTraitInfo public class DamageMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new DamageMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new DamageMultiplier(this); }
} }
public class DamageMultiplier : UpgradeMultiplierTrait, IDamageModifier public class DamageMultiplier : UpgradableTrait<DamageMultiplierInfo>, IDamageModifier
{ {
public DamageMultiplier(DamageMultiplierInfo info, string actorType) public DamageMultiplier(DamageMultiplierInfo info)
: base(info, "DamageMultiplier", actorType) { } : base(info) { }
int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage) { return GetModifier(); } int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
{
return IsTraitDisabled ? 100 : Info.Modifier;
}
} }
} }

View File

@@ -11,17 +11,21 @@
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The firepower of this actor is multiplied based on upgrade level if specified.")] [Desc("Modifies the damage applied by this actor.")]
public class FirepowerMultiplierInfo : UpgradeMultiplierTraitInfo public class FirepowerMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new FirepowerMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new FirepowerMultiplier(this); }
} }
public class FirepowerMultiplier : UpgradeMultiplierTrait, IFirepowerModifier public class FirepowerMultiplier : UpgradableTrait<FirepowerMultiplierInfo>, IFirepowerModifier
{ {
public FirepowerMultiplier(FirepowerMultiplierInfo info, string actorType) public FirepowerMultiplier(FirepowerMultiplierInfo info)
: base(info, "FirepowerMultiplier", actorType) { } : base(info) { }
int IFirepowerModifier.GetFirepowerModifier() { return GetModifier(); } int IFirepowerModifier.GetFirepowerModifier() { return IsTraitDisabled ? 100 : Info.Modifier; }
} }
} }

View File

@@ -11,17 +11,21 @@
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The inaccuracy of this actor is multiplied based on upgrade level if specified.")] [Desc("Modifies the inaccuracy of weapons fired by this actor.")]
public class InaccuracyMultiplierInfo : UpgradeMultiplierTraitInfo public class InaccuracyMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new InaccuracyMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new InaccuracyMultiplier(this); }
} }
public class InaccuracyMultiplier : UpgradeMultiplierTrait, IInaccuracyModifier public class InaccuracyMultiplier : UpgradableTrait<InaccuracyMultiplierInfo>, IInaccuracyModifier
{ {
public InaccuracyMultiplier(InaccuracyMultiplierInfo info, string actorType) public InaccuracyMultiplier(InaccuracyMultiplierInfo info)
: base(info, "InaccuracyMultiplier", actorType) { } : base(info) { }
int IInaccuracyModifier.GetInaccuracyModifier() { return GetModifier(); } int IInaccuracyModifier.GetInaccuracyModifier() { return IsTraitDisabled ? 100 : Info.Modifier; }
} }
} }

View File

@@ -13,22 +13,30 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The power usage/output of this actor is multiplied based on upgrade level if specified.")] [Desc("Modifies the power usage/output of this actor.")]
public class PowerMultiplierInfo : UpgradeMultiplierTraitInfo public class PowerMultiplierInfo : UpgradableTraitInfo
{ {
[FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new PowerMultiplier(init.Self, this); } public override object Create(ActorInitializer init) { return new PowerMultiplier(init.Self, this); }
} }
public class PowerMultiplier : UpgradeMultiplierTrait, IPowerModifier, INotifyOwnerChanged public class PowerMultiplier : UpgradableTrait<PowerMultiplierInfo>, IPowerModifier, INotifyOwnerChanged
{ {
PowerManager power; PowerManager power;
public PowerMultiplier(Actor self, PowerMultiplierInfo info) public PowerMultiplier(Actor self, PowerMultiplierInfo info)
: base(info, "PowerMultiplier", self.Info.Name) { power = self.Owner.PlayerActor.Trait<PowerManager>(); } : base(info)
{
power = self.Owner.PlayerActor.Trait<PowerManager>();
}
int IPowerModifier.GetPowerModifier() { return GetModifier(); } protected override void TraitEnabled(Actor self) { power.UpdateActor(self); }
protected override void TraitDisabled(Actor self) { power.UpdateActor(self); }
protected override void Update(Actor self) { power.UpdateActor(self); } int IPowerModifier.GetPowerModifier() { return IsTraitDisabled ? 100 : Info.Modifier; }
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
{ {

View File

@@ -11,22 +11,21 @@
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("Range of this actor is multiplied based on upgrade level.")] [Desc("Modifies the range of weapons fired by this actor.")]
public class RangeMultiplierInfo : UpgradeMultiplierTraitInfo, IRangeModifierInfo public class RangeMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new RangeMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
int IRangeModifierInfo.GetRangeModifierDefault() public override object Create(ActorInitializer init) { return new RangeMultiplier(this); }
{
return BaseLevel > 0 || UpgradeTypes.Length == 0 ? 100 : Modifier[0];
}
} }
public class RangeMultiplier : UpgradeMultiplierTrait, IRangeModifier public class RangeMultiplier : UpgradableTrait<RangeMultiplierInfo>, IRangeModifierInfo
{ {
public RangeMultiplier(RangeMultiplierInfo info, string actorType) public RangeMultiplier(RangeMultiplierInfo info)
: base(info, "RangeMultiplier", actorType) { } : base(info) { }
int IRangeModifier.GetRangeModifier() { return GetModifier(); } int IRangeModifierInfo.GetRangeModifierDefault() { return IsTraitDisabled ? 100 : Info.Modifier; }
} }
} }

View File

@@ -11,17 +11,21 @@
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The reloading time of this actor is multiplied based on upgrade level if specified.")] [Desc("Modifies the reload time of weapons fired by this actor.")]
public class ReloadDelayMultiplierInfo : UpgradeMultiplierTraitInfo public class ReloadDelayMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new ReloadDelayMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new ReloadDelayMultiplier(this); }
} }
public class ReloadDelayMultiplier : UpgradeMultiplierTrait, IReloadModifier public class ReloadDelayMultiplier : UpgradableTrait<ReloadDelayMultiplierInfo>, IReloadModifier
{ {
public ReloadDelayMultiplier(ReloadDelayMultiplierInfo info, string actorType) public ReloadDelayMultiplier(ReloadDelayMultiplierInfo info)
: base(info, "ReloadDelayMultiplier", actorType) { } : base(info) { }
int IReloadModifier.GetReloadModifier() { return GetModifier(); } int IReloadModifier.GetReloadModifier() { return IsTraitDisabled ? 100 : Info.Modifier; }
} }
} }

View File

@@ -11,17 +11,21 @@
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The speed of this actor is multiplied based on upgrade level if specified.")] [Desc("Modifies the movement speed of this actor.")]
public class SpeedMultiplierInfo : UpgradeMultiplierTraitInfo public class SpeedMultiplierInfo : UpgradableTraitInfo
{ {
public override object Create(ActorInitializer init) { return new SpeedMultiplier(this, init.Self.Info.Name); } [FieldLoader.Require]
[Desc("Percentage modifier to apply.")]
public readonly int Modifier = 100;
public override object Create(ActorInitializer init) { return new SpeedMultiplier(this); }
} }
public class SpeedMultiplier : UpgradeMultiplierTrait, ISpeedModifier public class SpeedMultiplier : UpgradableTrait<SpeedMultiplierInfo>, ISpeedModifier
{ {
public SpeedMultiplier(SpeedMultiplierInfo info, string actorType) public SpeedMultiplier(SpeedMultiplierInfo info)
: base(info, "SpeedMultiplier", actorType) { } : base(info) { }
int ISpeedModifier.GetSpeedModifier() { return GetModifier(); } int ISpeedModifier.GetSpeedModifier() { return IsTraitDisabled ? 100 : Info.Modifier; }
} }
} }

View File

@@ -1,76 +0,0 @@
#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 System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public abstract class UpgradeMultiplierTraitInfo : ITraitInfo
{
[UpgradeUsedReference]
[Desc("Accepted upgrade types.")]
public readonly string[] UpgradeTypes = { };
[Desc("The lowest upgrade level using the scale.")]
public readonly int BaseLevel = 1;
[FieldLoader.Require]
[Desc("Percentages to apply with the first being applied at the base level.",
"Repeat last entry to accept time extensions.",
"If no upgrade types are specified, then the first/only modifier is always applied.")]
public readonly int[] Modifier = { };
public abstract object Create(ActorInitializer init);
}
public abstract class UpgradeMultiplierTrait : IUpgradable, IDisabledTrait, ISync
{
readonly UpgradeMultiplierTraitInfo info;
[Sync] int level = 0;
[Sync] public bool IsTraitDisabled { get; private set; }
public int AdjustedLevel { get { return level - info.BaseLevel; } }
public IEnumerable<string> UpgradeTypes { get { return info.UpgradeTypes; } }
protected UpgradeMultiplierTrait(UpgradeMultiplierTraitInfo info, string modifierType, string actorType)
{
if (info.Modifier.Length == 0)
throw new ArgumentException("No modifiers in " + modifierType + " for " + actorType);
this.info = info;
IsTraitDisabled = info.UpgradeTypes.Length > 0 && info.BaseLevel > 0;
level = IsTraitDisabled ? 0 : info.BaseLevel;
}
public bool AcceptsUpgradeLevel(Actor self, string type, int level)
{
return level < info.Modifier.Length + info.BaseLevel;
}
// Override to receive notice of level change.
protected virtual void Update(Actor self) { }
public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel)
{
if (!UpgradeTypes.Contains(type))
return;
level = newLevel.Clamp(0, Math.Max(info.Modifier.Length + info.BaseLevel - 1, 0));
IsTraitDisabled = level < info.BaseLevel;
Update(self);
}
public int GetModifier()
{
return IsTraitDisabled ? 100 : info.Modifier[level - info.BaseLevel];
}
}
}

View File

@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits
power = newOwner.PlayerActor.Trait<PowerManager>(); power = newOwner.PlayerActor.Trait<PowerManager>();
} }
protected override void UpgradeDisabled(Actor self) protected override void TraitDisabled(Actor self)
{ {
if (!disabled || !Info.CancelWhenDisabled) if (!disabled || !Info.CancelWhenDisabled)
return; return;

View File

@@ -41,8 +41,8 @@ namespace OpenRA.Mods.Common.Traits
powerModifiers = Exts.Lazy(() => self.TraitsImplementing<IPowerModifier>().ToArray()); powerModifiers = Exts.Lazy(() => self.TraitsImplementing<IPowerModifier>().ToArray());
} }
protected override void UpgradeEnabled(Actor self) { PlayerPower.UpdateActor(self); } protected override void TraitEnabled(Actor self) { PlayerPower.UpdateActor(self); }
protected override void UpgradeDisabled(Actor self) { PlayerPower.UpdateActor(self); } protected override void TraitDisabled(Actor self) { PlayerPower.UpdateActor(self); }
public void AddedToWorld(Actor self) { PlayerPower.UpdateActor(self); } public void AddedToWorld(Actor self) { PlayerPower.UpdateActor(self); }
public void RemovedFromWorld(Actor self) { PlayerPower.RemoveActor(self); } public void RemovedFromWorld(Actor self) { PlayerPower.RemoveActor(self); }
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public override object Create(ActorInitializer init) { return new LeavesTrails(init.Self, this); } public override object Create(ActorInitializer init) { return new LeavesTrails(init.Self, this); }
} }
public class LeavesTrails : UpgradableTrait<LeavesTrailsInfo>, ITick, INotifyCreated public class LeavesTrails : UpgradableTrait<LeavesTrailsInfo>, ITick
{ {
BodyOrientation body; BodyOrientation body;
IFacing facing; IFacing facing;
@@ -75,12 +75,14 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
WPos cachedPosition; WPos cachedPosition;
public void Created(Actor self) protected override void Created(Actor self)
{ {
body = self.Trait<BodyOrientation>(); body = self.Trait<BodyOrientation>();
facing = self.TraitOrDefault<IFacing>(); facing = self.TraitOrDefault<IFacing>();
cachedFacing = facing != null ? facing.Facing : 0; cachedFacing = facing != null ? facing.Facing : 0;
cachedPosition = self.CenterPosition; cachedPosition = self.CenterPosition;
base.Created(self);
} }
int ticks; int ticks;
@@ -132,7 +134,7 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
} }
protected override void UpgradeEnabled(Actor self) protected override void TraitEnabled(Actor self)
{ {
cachedPosition = self.CenterPosition; cachedPosition = self.CenterPosition;
} }

View File

@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits.Render
// Defer this lookup until we really need it to ensure we get the correct value. // Defer this lookup until we really need it to ensure we get the correct value.
range = Exts.Lazy(() => range = Exts.Lazy(() =>
{ {
var armaments = ai.TraitInfos<ArmamentInfo>().Where(a => a.UpgradeMinEnabledLevel == 0); var armaments = ai.TraitInfos<ArmamentInfo>().Where(a => a.EnabledByDefault);
if (!armaments.Any()) if (!armaments.Any())
return FallbackRange; return FallbackRange;

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
if (Palette != null) if (Palette != null)

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
} }
public class WithInfantryBody : UpgradableTrait<WithInfantryBodyInfo>, ITick, INotifyAttack, INotifyIdle, INotifyCreated public class WithInfantryBody : UpgradableTrait<WithInfantryBodyInfo>, ITick, INotifyAttack, INotifyIdle
{ {
readonly IMove move; readonly IMove move;
protected readonly Animation DefaultAnimation; protected readonly Animation DefaultAnimation;
@@ -90,9 +90,11 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
} }
public void Created(Actor self) protected override void Created(Actor self)
{ {
rsm = self.TraitOrDefault<IRenderInfantrySequenceModifier>(); rsm = self.TraitOrDefault<IRenderInfantrySequenceModifier>();
base.Created(self);
} }
protected virtual string NormalizeInfantrySequence(Actor self, string baseSequence) protected virtual string NormalizeInfantrySequence(Actor self, string baseSequence)

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
if (image == null) if (image == null)
@@ -128,7 +128,7 @@ namespace OpenRA.Mods.Common.Traits.Render
rs.Add(anim, info.Palette, info.IsPlayerPalette); rs.Add(anim, info.Palette, info.IsPlayerPalette);
} }
protected override void UpgradeEnabled(Actor self) protected override void TraitEnabled(Actor self)
{ {
if (info.Image == null) if (info.Image == null)
return; return;
@@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Traits.Render
anim.Animation.PlayThen(info.OpeningSequence, () => anim.Animation.PlayRepeating(info.Sequence)); anim.Animation.PlayThen(info.OpeningSequence, () => anim.Animation.PlayRepeating(info.Sequence));
} }
protected override void UpgradeDisabled(Actor self) protected override void TraitDisabled(Actor self)
{ {
if (info.Image == null) if (info.Image == null)
return; return;

View File

@@ -1,28 +0,0 @@
#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
namespace OpenRA.Mods.Common.Traits.Render
{
public class WithRankDecorationInfo : WithDecorationInfo
{
public override object Create(ActorInitializer init) { return new WithRankDecoration(init.Self, this); }
}
public class WithRankDecoration : WithDecoration
{
public WithRankDecoration(Actor self, WithRankDecorationInfo info) : base(self, info) { }
protected override void UpgradeLevelChanged(Actor self, int oldLevel, int newLevel)
{
Anim.PlayFetchIndex(Info.Sequence, () => newLevel - 1);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
var body = init.Actor.TraitInfo<BodyOrientationInfo>(); var body = init.Actor.TraitInfo<BodyOrientationInfo>();

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public virtual IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) public virtual IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
var anim = new Animation(init.World, image); var anim = new Animation(init.World, image);

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
var body = init.Actor.TraitInfo<BodyOrientationInfo>(); var body = init.Actor.TraitInfo<BodyOrientationInfo>();

View File

@@ -20,7 +20,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render namespace OpenRA.Mods.Common.Traits.Render
{ {
[Desc("Displays a text overlay relative to the selection box.")] [Desc("Displays a text overlay relative to the selection box.")]
public class WithTextDecorationInfo : UpgradableTraitInfo, IRulesetLoaded public class WithTextDecorationInfo : UpgradableTraitInfo
{ {
[FieldLoader.Require] [Translate] public readonly string Text = null; [FieldLoader.Require] [Translate] public readonly string Text = null;
@@ -47,10 +47,12 @@ namespace OpenRA.Mods.Common.Traits.Render
public override object Create(ActorInitializer init) { return new WithTextDecoration(init.Self, this); } public override object Create(ActorInitializer init) { return new WithTextDecoration(init.Self, this); }
void IRulesetLoaded<ActorInfo>.RulesetLoaded(Ruleset rules, ActorInfo info) public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{ {
if (!Game.ModData.Manifest.Fonts.ContainsKey(Font)) if (!Game.ModData.Manifest.Fonts.ContainsKey(Font))
throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font)); throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font));
base.RulesetLoaded(rules, ai);
} }
} }

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<VoxelAnimation> RenderPreviewVoxels( public IEnumerable<VoxelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p) ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
var body = init.Actor.TraitInfo<BodyOrientationInfo>(); var body = init.Actor.TraitInfo<BodyOrientationInfo>();

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<VoxelAnimation> RenderPreviewVoxels( public IEnumerable<VoxelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p) ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
{ {
if (UpgradeMinEnabledLevel > 0) if (!EnabledByDefault)
yield break; yield break;
var body = init.Actor.TraitInfo<BodyOrientationInfo>(); var body = init.Actor.TraitInfo<BodyOrientationInfo>();

View File

@@ -74,7 +74,11 @@ namespace OpenRA.Mods.Common.Traits
public WeaponInfo WeaponInfo { get; private set; } public WeaponInfo WeaponInfo { get; private set; }
public override object Create(ActorInitializer init) { return new NukePower(init.Self, this); } public override object Create(ActorInitializer init) { return new NukePower(init.Self, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[MissileWeapon.ToLowerInvariant()]; } public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
WeaponInfo = rules.Weapons[MissileWeapon.ToLowerInvariant()];
base.RulesetLoaded(rules, ai);
}
} }
class NukePower : SupportPower class NukePower : SupportPower

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new Targetable(init.Self, this); } public override object Create(ActorInitializer init) { return new Targetable(init.Self, this); }
} }
public class Targetable : UpgradableTrait<TargetableInfo>, ITargetable, INotifyCreated public class Targetable : UpgradableTrait<TargetableInfo>, ITargetable
{ {
protected static readonly string[] None = new string[] { }; protected static readonly string[] None = new string[] { };
protected Cloak[] cloaks; protected Cloak[] cloaks;
@@ -35,9 +35,11 @@ namespace OpenRA.Mods.Common.Traits
public Targetable(Actor self, TargetableInfo info) public Targetable(Actor self, TargetableInfo info)
: base(info) { } : base(info) { }
void INotifyCreated.Created(Actor self) protected override void Created(Actor self)
{ {
cloaks = self.TraitsImplementing<Cloak>().ToArray(); cloaks = self.TraitsImplementing<Cloak>().ToArray();
base.Created(self);
} }
public virtual bool TargetableBy(Actor self, Actor viewer) public virtual bool TargetableBy(Actor self, Actor viewer)

View File

@@ -10,30 +10,32 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
/// <summary>Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.)</summary> /// <summary>Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.)</summary>
public abstract class UpgradableTraitInfo : IUpgradableInfo public abstract class UpgradableTraitInfo : IConditionConsumerInfo, IRulesetLoaded
{ {
static readonly IReadOnlyDictionary<string, bool> NoConditions = new ReadOnlyDictionary<string, bool>(new Dictionary<string, bool>());
[UpgradeUsedReference] [UpgradeUsedReference]
[Desc("The upgrade types which can enable or disable this trait.")] [Desc("Boolean expression defining the condition to enable this trait.")]
public readonly HashSet<string> UpgradeTypes = new HashSet<string>(); public readonly BooleanExpression RequiresCondition = null;
[Desc("The minimum upgrade level at which this trait is enabled.", "Defaults to 0 (enabled by default).")]
public readonly int UpgradeMinEnabledLevel = 0;
[Desc("The maximum upgrade level at which the trait is enabled.",
"Defaults to UpgradeMaxAcceptedLevel (enabled for all levels greater than UpgradeMinEnabledLevel).",
"Set this to a value smaller than UpgradeMaxAcceptedLevel to disable the trait at higher levels.",
"Use UpgradeMaxAcceptedLevel: 2 (1 more) to be able to extend upgrade time.")]
public readonly int UpgradeMaxEnabledLevel = int.MaxValue;
[Desc("The maximum upgrade level that this trait will accept.")]
public readonly int UpgradeMaxAcceptedLevel = 1;
public abstract object Create(ActorInitializer init); public abstract object Create(ActorInitializer init);
// HACK: A shim for all the ActorPreview code that used to query UpgradeMinEnabledLevel directly
// This can go away after we introduce an InitialUpgrades ActorInit and have the traits query the
// condition directly
public bool EnabledByDefault { get; private set; }
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
EnabledByDefault = RequiresCondition != null ? RequiresCondition.Evaluate(NoConditions) : true;
}
} }
/// <summary> /// <summary>
@@ -41,50 +43,59 @@ namespace OpenRA.Mods.Common.Traits
/// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor. /// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor.
/// Note that EnabledByUpgrade is not called at creation even if this starts as enabled. /// Note that EnabledByUpgrade is not called at creation even if this starts as enabled.
/// </summary> /// </summary>
public abstract class UpgradableTrait<InfoType> : IUpgradable, IDisabledTrait, ISync where InfoType : UpgradableTraitInfo public abstract class UpgradableTrait<InfoType> : IConditionConsumer, IDisabledTrait, INotifyCreated, ISync where InfoType : UpgradableTraitInfo
{ {
public readonly InfoType Info; public readonly InfoType Info;
public IEnumerable<string> UpgradeTypes { get { return Info.UpgradeTypes; } }
IEnumerable<string> IConditionConsumer.Conditions
{
get
{
if (Info.RequiresCondition != null)
return Info.RequiresCondition.Variables;
return Enumerable.Empty<string>();
}
}
[Sync] public bool IsTraitDisabled { get; private set; } [Sync] public bool IsTraitDisabled { get; private set; }
public UpgradableTrait(InfoType info) public UpgradableTrait(InfoType info)
{ {
Info = info; Info = info;
IsTraitDisabled = info.UpgradeTypes != null && info.UpgradeTypes.Count > 0 && info.UpgradeMinEnabledLevel > 0;
// Conditional traits will be enabled (if appropriate) by the UpgradeManager
// calling IConditionConsumer.ConditionsChanged at the end of INotifyCreated.
IsTraitDisabled = Info.RequiresCondition != null;
} }
public bool AcceptsUpgradeLevel(Actor self, string type, int level) protected virtual void Created(Actor self)
{ {
return level > 0 && level <= Info.UpgradeMaxAcceptedLevel; if (Info.RequiresCondition == null)
TraitEnabled(self);
} }
public void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel) void INotifyCreated.Created(Actor self) { Created(self); }
{
if (!Info.UpgradeTypes.Contains(type))
return;
// Restrict the levels to the allowed range void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary<string, bool> conditions)
oldLevel = oldLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); {
newLevel = newLevel.Clamp(0, Info.UpgradeMaxAcceptedLevel); if (Info.RequiresCondition == null)
if (oldLevel == newLevel)
return; return;
var wasDisabled = IsTraitDisabled; var wasDisabled = IsTraitDisabled;
IsTraitDisabled = newLevel < Info.UpgradeMinEnabledLevel || newLevel > Info.UpgradeMaxEnabledLevel; IsTraitDisabled = !Info.RequiresCondition.Evaluate(conditions);
UpgradeLevelChanged(self, oldLevel, newLevel);
if (IsTraitDisabled != wasDisabled) if (IsTraitDisabled != wasDisabled)
{ {
if (wasDisabled) if (wasDisabled)
UpgradeEnabled(self); TraitEnabled(self);
else else
UpgradeDisabled(self); TraitDisabled(self);
} }
} }
// Subclasses can add upgrade support by querying IsTraitDisabled and/or overriding these methods. // Subclasses can add condition support by querying IsTraitDisabled and/or overriding these methods.
protected virtual void UpgradeLevelChanged(Actor self, int oldLevel, int newLevel) { } protected virtual void TraitEnabled(Actor self) { }
protected virtual void UpgradeEnabled(Actor self) { } protected virtual void TraitDisabled(Actor self) { }
protected virtual void UpgradeDisabled(Actor self) { }
} }
} }

View File

@@ -12,49 +12,44 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Primitives;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("Attach this to a unit to enable dynamic upgrades by warheads, experience, crates, support powers, etc.")] [Desc("Attach this to a unit to enable dynamic upgrades by warheads, experience, crates, support powers, etc.")]
public class UpgradeManagerInfo : TraitInfo<UpgradeManager>, IRulesetLoaded public class UpgradeManagerInfo : TraitInfo<UpgradeManager>, Requires<IConditionConsumerInfo> { }
{
public void RulesetLoaded(Ruleset rules, ActorInfo info)
{
if (!info.Name.StartsWith("^") && !info.TraitInfos<IUpgradableInfo>().Any())
throw new YamlException(
"There are no upgrades to be managed for actor '{0}'. You are either missing some upgradeable traits, or this UpgradeManager trait is not required.".F(
info.Name));
}
}
public class UpgradeManager : INotifyCreated, ITick public class UpgradeManager : INotifyCreated, ITick
{ {
class TimedUpgrade /// <summary>Value used to represent an invalid token.</summary>
public static readonly int InvalidConditionToken = -1;
class TimedCondition
{ {
public class UpgradeSource public class ConditionSource
{ {
public readonly object Source; public readonly object Source;
public int Remaining; public int Remaining;
public UpgradeSource(int duration, object source) public ConditionSource(int duration, object source)
{ {
Remaining = duration; Remaining = duration;
Source = source; Source = source;
} }
} }
public readonly string Upgrade; public readonly string Condition;
public readonly int Duration; public readonly int Duration;
public readonly HashSet<UpgradeSource> Sources; public readonly HashSet<ConditionSource> Sources;
public int Remaining; // Equal to maximum of all Sources.Remaining public int Remaining; // Equal to maximum of all Sources.Remaining
public TimedUpgrade(string upgrade, int duration, object source) public TimedCondition(string condition, int duration, object source)
{ {
Upgrade = upgrade; Condition = condition;
Duration = duration; Duration = duration;
Remaining = duration; Remaining = duration;
Sources = new HashSet<UpgradeSource> { new UpgradeSource(duration, source) }; Sources = new HashSet<ConditionSource> { new ConditionSource(duration, source) };
} }
public void Tick() public void Tick()
@@ -65,29 +60,124 @@ namespace OpenRA.Mods.Common.Traits
} }
} }
class UpgradeState class ConditionState
{ {
public readonly List<IUpgradable> Traits = new List<IUpgradable>(); /// <summary>Traits that have registered to be notified when this condition changes.</summary>
public readonly List<object> Sources = new List<object>(); public readonly List<IConditionConsumer> Consumers = new List<IConditionConsumer>();
/// <summary>Unique integers identifying granted instances of the condition.</summary>
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<Action<int, int>> Watchers = new List<Action<int, int>>();
} }
readonly List<TimedUpgrade> timedUpgrades = new List<TimedUpgrade>(); readonly List<TimedCondition> timedConditions = new List<TimedCondition>();
readonly Dictionary<IUpgradable, int> levels = new Dictionary<IUpgradable, int>();
Dictionary<string, UpgradeState> upgrades; Dictionary<string, ConditionState> state;
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
Dictionary<int, string> tokens = new Dictionary<int, string>();
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>();
/// <summary>Cache of condition -> enabled state for quick evaluation of boolean conditions.</summary>
Dictionary<string, bool> conditionCache = new Dictionary<string, bool>();
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
IReadOnlyDictionary<string, bool> readOnlyConditionCache;
void INotifyCreated.Created(Actor self) void INotifyCreated.Created(Actor self)
{ {
upgrades = new Dictionary<string, UpgradeState>(); state = new Dictionary<string, ConditionState>();
foreach (var up in self.TraitsImplementing<IUpgradable>()) readOnlyConditionCache = new ReadOnlyDictionary<string, bool>(conditionCache);
foreach (var t in up.UpgradeTypes)
upgrades.GetOrAdd(t).Traits.Add(up); var allConsumers = new HashSet<IConditionConsumer>();
foreach (var consumer in self.TraitsImplementing<IConditionConsumer>())
{
allConsumers.Add(consumer);
foreach (var condition in consumer.Conditions)
{
state.GetOrAdd(condition).Consumers.Add(consumer);
conditionCache[condition] = false;
}
}
// Enable any conditions granted during trait setup
foreach (var kv in tokens)
{
ConditionState conditionState;
if (!state.TryGetValue(kv.Value, out conditionState))
continue;
conditionState.Tokens.Add(kv.Key);
conditionCache[kv.Value] = conditionState.Tokens.Count > 0;
}
// Update all traits with their initial condition state
foreach (var consumer in allConsumers)
consumer.ConditionsChanged(self, readOnlyConditionCache);
} }
void CheckCanManageUpgrades() void UpdateConditionState(Actor self, string condition, int token, bool isRevoke)
{ {
if (upgrades == null) ConditionState conditionState;
throw new InvalidOperationException("Upgrades cannot be managed until the actor has been fully created."); if (!state.TryGetValue(condition, out conditionState))
return;
if (isRevoke)
conditionState.Tokens.Remove(token);
else
conditionState.Tokens.Add(token);
conditionCache[condition] = conditionState.Tokens.Count > 0;
foreach (var t in conditionState.Consumers)
t.ConditionsChanged(self, readOnlyConditionCache);
}
/// <summary>Grants a specified condition.</summary>
/// <returns>The token that is used to revoke this condition.</returns>
public int GrantCondition(Actor self, string condition)
{
var token = nextToken++;
tokens.Add(token, condition);
// Conditions may be granted before the state is initialized.
// These conditions will be processed in INotifyCreated.Created.
if (state != null)
UpdateConditionState(self, condition, token, false);
return token;
}
/// <summary>Revokes a previously granted condition.</summary>
/// <returns>The invalid token ID</returns>
/// <param name="token">The token ID returned by GrantCondition.</param>
public int RevokeCondition(Actor self, int token)
{
string condition;
if (!tokens.TryGetValue(token, out condition))
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, self));
tokens.Remove(token);
// Conditions may be granted and revoked before the state is initialized.
if (state != null)
UpdateConditionState(self, condition, token, true);
return InvalidConditionToken;
}
#region Shim methods for legacy upgrade granting code
void CheckCanManageConditions()
{
if (state == null)
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 /// <summary>Upgrade level increments are limited to dupesAllowed per source, i.e., if a single
@@ -98,11 +188,11 @@ namespace OpenRA.Mods.Common.Traits
/// remaining upgrade duration will be replaced by the new source.</summary> /// 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) public void GrantTimedUpgrade(Actor self, string upgrade, int duration, object source = null, int dupesAllowed = 1)
{ {
var timed = timedUpgrades.FirstOrDefault(u => u.Upgrade == upgrade); var timed = timedConditions.FirstOrDefault(u => u.Condition == upgrade);
if (timed == null) if (timed == null)
{ {
timed = new TimedUpgrade(upgrade, duration, source); timed = new TimedCondition(upgrade, duration, source);
timedUpgrades.Add(timed); timedConditions.Add(timed);
GrantUpgrade(self, upgrade, timed); GrantUpgrade(self, upgrade, timed);
return; return;
} }
@@ -110,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits
var srcs = timed.Sources.Where(s => s.Source == source); var srcs = timed.Sources.Where(s => s.Source == source);
if (srcs.Count() < dupesAllowed) if (srcs.Count() < dupesAllowed)
{ {
timed.Sources.Add(new TimedUpgrade.UpgradeSource(duration, source)); timed.Sources.Add(new TimedCondition.ConditionSource(duration, source));
if (AcceptsUpgrade(self, upgrade)) if (AcceptsUpgrade(self, upgrade))
GrantUpgrade(self, upgrade, timed); GrantUpgrade(self, upgrade, timed);
else else
@@ -122,108 +212,69 @@ namespace OpenRA.Mods.Common.Traits
timed.Remaining = Math.Max(duration, timed.Remaining); timed.Remaining = Math.Max(duration, timed.Remaining);
} }
// Different upgradeable traits may define (a) different level ranges for the same upgrade type,
// and (b) multiple upgrade types for the same trait. The unrestricted level for each trait is
// tracked independently so that we can correctly revoke levels without adding the burden of
// tracking both the overall (unclamped) and effective (clamped) levels on each individual trait.
void NotifyUpgradeLevelChanged(IEnumerable<IUpgradable> traits, Actor self, string upgrade, int levelAdjust)
{
foreach (var up in traits)
{
var oldLevel = levels.GetOrAdd(up);
var newLevel = levels[up] = oldLevel + levelAdjust;
// This will internally clamp the levels to its own restricted range
up.UpgradeLevelChanged(self, upgrade, oldLevel, newLevel);
}
}
int GetOverallLevel(IUpgradable upgradable)
{
int level;
return levels.TryGetValue(upgradable, out level) ? level : 0;
}
public void GrantUpgrade(Actor self, string upgrade, object source) public void GrantUpgrade(Actor self, string upgrade, object source)
{ {
CheckCanManageUpgrades(); CheckCanManageConditions();
objectTokenShim[Pair.New(source, upgrade)] = GrantCondition(self, upgrade);
UpgradeState s;
if (!upgrades.TryGetValue(upgrade, out s))
return;
// Track the upgrade source so that the upgrade can be removed without conflicts
s.Sources.Add(source);
NotifyUpgradeLevelChanged(s.Traits, self, upgrade, 1);
} }
public void RevokeUpgrade(Actor self, string upgrade, object source) public void RevokeUpgrade(Actor self, string upgrade, object source)
{ {
CheckCanManageUpgrades(); CheckCanManageConditions();
RevokeCondition(self, objectTokenShim[Pair.New(source, upgrade)]);
UpgradeState s;
if (!upgrades.TryGetValue(upgrade, out s))
return;
if (!s.Sources.Remove(source))
throw new InvalidOperationException("Object <{0}> revoked more levels of upgrade {1} than it granted for {2}.".F(source, upgrade, self));
NotifyUpgradeLevelChanged(s.Traits, self, upgrade, -1);
} }
/// <summary>Returns true if the actor uses the given upgrade. Does not check the actual level of the upgrade.</summary> /// <summary>Returns true if the actor uses the given upgrade. Does not check the actual level of the upgrade.</summary>
public bool AcknowledgesUpgrade(Actor self, string upgrade) public bool AcknowledgesUpgrade(Actor self, string upgrade)
{ {
CheckCanManageUpgrades(); CheckCanManageConditions();
return upgrades.ContainsKey(upgrade); return state.ContainsKey(upgrade);
} }
/// <summary>Returns true only if the actor can accept another level of the upgrade.</summary> /// <summary>Returns true only if the actor can accept another level of the upgrade.</summary>
public bool AcceptsUpgrade(Actor self, string upgrade) public bool AcceptsUpgrade(Actor self, string upgrade)
{ {
CheckCanManageUpgrades(); CheckCanManageConditions();
bool enabled;
UpgradeState s; if (!conditionCache.TryGetValue(upgrade, out enabled))
if (!upgrades.TryGetValue(upgrade, out s))
return false; return false;
return s.Traits.Any(up => up.AcceptsUpgradeLevel(self, upgrade, GetOverallLevel(up) + 1)); return !enabled;
} }
public void RegisterWatcher(string upgrade, Action<int, int> action) public void RegisterWatcher(string upgrade, Action<int, int> action)
{ {
CheckCanManageUpgrades(); CheckCanManageConditions();
UpgradeState s; ConditionState s;
if (!upgrades.TryGetValue(upgrade, out s)) if (!state.TryGetValue(upgrade, out s))
return; return;
s.Watchers.Add(action); s.Watchers.Add(action);
} }
/// <summary>Watchers will be receiving notifications while the upgrade's level is nonzero. /// <summary>Watchers will be receiving notifications while the condition is enabled.
/// They will also be provided with the number of ticks before the level returns to zero, /// 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 /// as well as the duration in ticks of the timed upgrade (provided in the first call to
/// GrantTimedUpgrade).</summary> /// GrantTimedUpgrade).</summary>
public void Tick(Actor self) void ITick.Tick(Actor self)
{ {
CheckCanManageUpgrades(); foreach (var u in timedConditions)
foreach (var u in timedUpgrades)
{ {
u.Tick(); u.Tick();
foreach (var source in u.Sources) foreach (var source in u.Sources)
if (source.Remaining <= 0) if (source.Remaining <= 0)
RevokeUpgrade(self, u.Upgrade, u); RevokeUpgrade(self, u.Condition, u);
u.Sources.RemoveWhere(source => source.Remaining <= 0); u.Sources.RemoveWhere(source => source.Remaining <= 0);
foreach (var a in upgrades[u.Upgrade].Watchers) foreach (var a in state[u.Condition].Watchers)
a(u.Duration, u.Remaining); a(u.Duration, u.Remaining);
} }
timedUpgrades.RemoveAll(u => u.Remaining <= 0); timedConditions.RemoveAll(u => u.Remaining <= 0);
} }
#endregion
} }
} }

View File

@@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new Wanders(init.Self, this); } public override object Create(ActorInitializer init) { return new Wanders(init.Self, this); }
} }
public class Wanders : UpgradableTrait<WandersInfo>, INotifyCreated, INotifyIdle, INotifyBecomingIdle public class Wanders : UpgradableTrait<WandersInfo>, INotifyIdle, INotifyBecomingIdle
{ {
readonly Actor self; readonly Actor self;
readonly WandersInfo info; readonly WandersInfo info;
@@ -50,9 +50,11 @@ namespace OpenRA.Mods.Common.Traits
effectiveMoveRadius = info.WanderMoveRadius; effectiveMoveRadius = info.WanderMoveRadius;
} }
void INotifyCreated.Created(Actor self) protected override void Created(Actor self)
{ {
move = self.Trait<IMove>() as IResolveOrder; move = self.Trait<IMove>() as IResolveOrder;
base.Created(self);
} }
public virtual void OnBecomingIdle(Actor self) public virtual void OnBecomingIdle(Actor self)

View File

@@ -103,15 +103,14 @@ namespace OpenRA.Mods.Common.Traits
[RequireExplicitImplementation] [RequireExplicitImplementation]
public interface INotifyPassengerExited { void OnPassengerExited(Actor self, Actor passenger); } public interface INotifyPassengerExited { void OnPassengerExited(Actor self, Actor passenger); }
public interface IUpgradable [RequireExplicitImplementation]
{ public interface IConditionConsumerInfo : ITraitInfo { }
IEnumerable<string> UpgradeTypes { get; }
bool AcceptsUpgradeLevel(Actor self, string type, int level);
void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel);
}
// Implement to construct before UpgradeManager public interface IConditionConsumer
public interface IUpgradableInfo : ITraitInfo { } {
IEnumerable<string> Conditions { get; }
void ConditionsChanged(Actor self, IReadOnlyDictionary<string, bool> conditions);
}
public interface INotifyHarvesterAction public interface INotifyHarvesterAction
{ {

View File

@@ -435,6 +435,48 @@ namespace OpenRA.Mods.Common.UtilityCommands
} }
} }
// Rename Replaced upgrade consumers with conditions
if (engineVersion < 20161117)
{
var upgradeTypesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeTypes");
if (upgradeTypesNode != null)
{
var upgradeMinEnabledLevel = 0;
var upgradeMaxEnabledLevel = int.MaxValue;
var upgradeMaxAcceptedLevel = 1;
var upgradeTypes = FieldLoader.GetValue<string[]>("", upgradeTypesNode.Value.Value);
var minEnabledNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel");
if (minEnabledNode != null)
upgradeMinEnabledLevel = FieldLoader.GetValue<int>("", minEnabledNode.Value.Value);
var maxEnabledNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMaxEnabledLevel");
if (maxEnabledNode != null)
upgradeMaxEnabledLevel = FieldLoader.GetValue<int>("", maxEnabledNode.Value.Value);
var maxAcceptedNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMaxAcceptedLevel");
if (maxAcceptedNode != null)
upgradeMaxAcceptedLevel = FieldLoader.GetValue<int>("", maxAcceptedNode.Value.Value);
var processed = false;
if (upgradeTypes.Length == 1 && upgradeMinEnabledLevel == 0 && upgradeMaxEnabledLevel == 0 && upgradeMaxAcceptedLevel == 1)
{
node.Value.Nodes.Add(new MiniYamlNode("RequiresCondition", "!" + upgradeTypes.First()));
processed = true;
}
else if (upgradeTypes.Length == 1 && upgradeMinEnabledLevel == 1 && upgradeMaxEnabledLevel == int.MaxValue && upgradeMaxAcceptedLevel == 1)
{
node.Value.Nodes.Add(new MiniYamlNode("RequiresCondition", upgradeTypes.First()));
processed = true;
}
if (processed)
node.Value.Nodes.RemoveAll(n => n.Key == "UpgradeTypes" || n.Key == "UpgradeMinEnabledLevel" ||
n.Key == "UpgradeMaxEnabledLevel" || n.Key == "UpgradeMaxAcceptedLevel");
else
Console.WriteLine("Unable to automatically migrate {0}:{1} UpgradeTypes to RequiresCondition. This must be corrected manually", parent.Key, node.Key);
}
}
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
} }

View File

@@ -13,6 +13,7 @@ using System;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
using OpenRA.Widgets; using OpenRA.Widgets;
@@ -74,7 +75,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : ""; var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : "";
requiresLabel.GetText = () => requiresString; requiresLabel.GetText = () => requiresString;
var power = actor.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(i => i.Amount); var power = actor.TraitInfos<PowerInfo>().Where(i => i.EnabledByDefault).Sum(i => i.Amount);
var powerString = power.ToString(); var powerString = power.ToString();
powerLabel.GetText = () => powerString; powerLabel.GetText = () => powerString;
powerLabel.GetColor = () => ((pm.PowerProvided - pm.PowerDrained) >= -power || power > 0) powerLabel.GetColor = () => ((pm.PowerProvided - pm.PowerDrained) >= -power || power > 0)

View File

@@ -21,11 +21,11 @@ namespace OpenRA.Test
[TestFixture] [TestFixture]
public class BooleanExpressionTest public class BooleanExpressionTest
{ {
Dictionary<string, bool> testValues = new Dictionary<string, bool>() IReadOnlyDictionary<string, bool> testValues = new ReadOnlyDictionary<string, bool>(new Dictionary<string, bool>()
{ {
{ "true", true }, { "true", true },
{ "false", false } { "false", false }
}; });
void AssertFalse(string expression) void AssertFalse(string expression)
{ {

View File

@@ -26,23 +26,19 @@ TRAN:
WithIdleOverlay@ROTOR1AIR: WithIdleOverlay@ROTOR1AIR:
Offset: 597,0,85 Offset: 597,0,85
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTOR1GROUND: WithIdleOverlay@ROTOR1GROUND:
Offset: 597,0,85 Offset: 597,0,85
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
WithIdleOverlay@ROTOR2AIR: WithIdleOverlay@ROTOR2AIR:
Offset: -597,0,171 Offset: -597,0,171
Sequence: rotor2 Sequence: rotor2
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTOR2GROUND: WithIdleOverlay@ROTOR2GROUND:
Offset: -597,0,171 Offset: -597,0,171
Sequence: slow-rotor2 Sequence: slow-rotor2
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Cargo: Cargo:
Types: Infantry Types: Infantry
MaxWeight: 10 MaxWeight: 10
@@ -98,13 +94,11 @@ HELI:
WithIdleOverlay@ROTORAIR: WithIdleOverlay@ROTORAIR:
Offset: 0,0,85 Offset: 0,0,85
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTORGROUND: WithIdleOverlay@ROTORGROUND:
Offset: 0,0,85 Offset: 0,0,85
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
WithMuzzleOverlay: WithMuzzleOverlay:
SpawnActorOnDeath: SpawnActorOnDeath:
Actor: HELI.Husk Actor: HELI.Husk

View File

@@ -17,41 +17,104 @@
^GainsExperience: ^GainsExperience:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
200: firepower, damage, speed, reload, inaccuracy, rank 200: rank-veteran-1
400: firepower, damage, speed, reload, inaccuracy, rank 400: rank-veteran-2
800: firepower, damage, speed, reload, inaccuracy, rank 800: rank-veteran-3
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal 1600: rank-elite
FirepowerMultiplier@EXPERIENCE: DamageMultiplier@RANK-1:
UpgradeTypes: firepower RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 105, 110, 120, 130 Modifier: 95
DamageMultiplier@EXPERIENCE: DamageMultiplier@RANK-2:
UpgradeTypes: damage RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 95, 90, 85, 75 Modifier: 90
SpeedMultiplier@EXPERIENCE: DamageMultiplier@RANK-3:
UpgradeTypes: speed RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 105, 110, 120, 140 Modifier: 85
ReloadDelayMultiplier@EXPERIENCE: DamageMultiplier@RANK-ELITE:
UpgradeTypes: reload RequiresCondition: rank-elite
Modifier: 95, 90, 85, 75 Modifier: 75
InaccuracyMultiplier@EXPERIENCE: FirepowerMultiplier@RANK-1:
UpgradeTypes: inaccuracy RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90, 80, 70, 50 Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 120
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 130
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 120
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 140
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 95
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 90
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 85
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 50
SelfHealing@ELITE: SelfHealing@ELITE:
Step: 2 Step: 2
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: selfheal RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1 WithDecoration@RANK-1:
WithRankDecoration:
Image: rank Image: rank
Sequence: rank Sequence: rank-veteran-1
Palette: effect Palette: effect
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: rank RequiresCondition: rank-veteran-1 && !rank-veteran-2
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank
Sequence: rank-elite
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-elite
ZOffset: 256 ZOffset: 256
UpgradeMinEnabledLevel: 1
UpgradeMaxAcceptedLevel: 4
^Vehicle: ^Vehicle:
Inherits@1: ^ExistsInWorld Inherits@1: ^ExistsInWorld
@@ -91,12 +154,11 @@
Tooltip: Tooltip:
GenericName: Vehicle GenericName: Vehicle
Cloak: Cloak:
UpgradeTypes: cloak
UpgradeMinEnabledLevel: 1
InitialDelay: 15 InitialDelay: 15
CloakDelay: 90 CloakDelay: 90
CloakSound: trans1.aud CloakSound: trans1.aud
UncloakSound: trans1.aud UncloakSound: trans1.aud
RequiresCondition: cloak
MustBeDestroyed: MustBeDestroyed:
Voiced: Voiced:
VoiceSet: VehicleVoice VoiceSet: VehicleVoice
@@ -127,12 +189,10 @@
UseLocation: yes UseLocation: yes
Targetable@GROUND: Targetable@GROUND:
TargetTypes: Ground, Vehicle TargetTypes: Ground, Vehicle
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Targetable@AIRBORNE: Targetable@AIRBORNE:
TargetTypes: Air TargetTypes: Air
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
SelectionDecorations: SelectionDecorations:
WithSpriteControlGroupDecoration: WithSpriteControlGroupDecoration:
Selectable: Selectable:
@@ -160,8 +220,7 @@
Offset: 43, 128, 0 Offset: 43, 128, 0
ZOffset: -129 ZOffset: -129
Hovers@CRUISING: Hovers@CRUISING:
UpgradeTypes: cruising RequiresCondition: cruising
UpgradeMinEnabledLevel: 1
MustBeDestroyed: MustBeDestroyed:
Voiced: Voiced:
VoiceSet: VehicleVoice VoiceSet: VehicleVoice
@@ -217,12 +276,11 @@
CargoType: Infantry CargoType: Infantry
HiddenUnderFog: HiddenUnderFog:
DamagedByTerrain: DamagedByTerrain:
UpgradeTypes: hazmatsuits
UpgradeMaxEnabledLevel: 0
Terrain: Tiberium, BlueTiberium Terrain: Tiberium, BlueTiberium
Damage: 2 Damage: 2
DamageInterval: 16 DamageInterval: 16
DamageTypes: TiberiumDeath DamageTypes: TiberiumDeath
RequiresCondition: !hazmatsuits
GlobalUpgradable@BIO: GlobalUpgradable@BIO:
Upgrades: hazmatsuits Upgrades: hazmatsuits
Prerequisites: bio Prerequisites: bio
@@ -230,8 +288,7 @@
Image: pips Image: pips
Sequence: pip-hazmat Sequence: pip-hazmat
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: hazmatsuits RequiresCondition: hazmatsuits
UpgradeMinEnabledLevel: 1
ActorLostNotification: ActorLostNotification:
SpawnActorOnDeath: SpawnActorOnDeath:
Probability: 10 Probability: 10
@@ -249,8 +306,7 @@
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: hospitalheal RequiresCondition: hospitalheal
UpgradeMinEnabledLevel: 1
GlobalUpgradable@HOSPITAL: GlobalUpgradable@HOSPITAL:
Upgrades: hospitalheal Upgrades: hospitalheal
Prerequisites: hosp Prerequisites: hosp
@@ -258,8 +314,7 @@
Image: pips Image: pips
Sequence: pip-heal Sequence: pip-heal
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: hospitalheal RequiresCondition: hospitalheal
UpgradeMinEnabledLevel: 1
DetectCloaked: DetectCloaked:
Range: 2c0 Range: 2c0
DeathSounds@NORMAL: DeathSounds@NORMAL:

View File

@@ -46,8 +46,6 @@ LST:
BuildPaletteOrder: 1000 BuildPaletteOrder: 1000
Prerequisites: ~disabled Prerequisites: ~disabled
Mobile: Mobile:
UpgradeTypes: notmobile
UpgradeMaxEnabledLevel: 0
Crushes: crate Crushes: crate
TerrainSpeeds: TerrainSpeeds:
Clear: 100 Clear: 100
@@ -61,6 +59,7 @@ LST:
InitialFacing: 0 InitialFacing: 0
TurnSpeed: 4 TurnSpeed: 4
Speed: 142 Speed: 142
RequiresCondition: !notmobile
Health: Health:
HP: 400 HP: 400
Armor: Armor:

View File

@@ -88,10 +88,9 @@ APC:
Queue: Vehicle.GDI Queue: Vehicle.GDI
Description: Armed infantry transport.\nCan attack Aircraft.\n Strong vs Vehicles\n Weak vs Infantry Description: Armed infantry transport.\nCan attack Aircraft.\n Strong vs Vehicles\n Weak vs Infantry
Mobile: Mobile:
UpgradeTypes: notmobile
UpgradeMaxEnabledLevel: 0
TurnSpeed: 8 TurnSpeed: 8
Speed: 128 Speed: 128
RequiresCondition: !notmobile
Health: Health:
HP: 210 HP: 210
Armor: Armor:
@@ -517,7 +516,7 @@ STNK:
RevealsShroud: RevealsShroud:
Range: 7c0 Range: 7c0
Cloak: Cloak:
-UpgradeTypes: -RequiresCondition:
InitialDelay: 90 InitialDelay: 90
CloakDelay: 90 CloakDelay: 90
CloakSound: trans1.aud CloakSound: trans1.aud

View File

@@ -124,8 +124,13 @@ explosion:
FlipX: true FlipX: true
rank: rank:
rank: rank-veteran-1:
Length: * rank-veteran-2:
Start: 1
rank-veteran-3:
Start: 2
rank-elite:
Start: 3
rallypoint: rallypoint:
flag: flagfly flag: flagfly

View File

@@ -21,11 +21,11 @@ World:
^Building: ^Building:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
Modifier: 0, 0 Modifier: 0
wall: wall:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
Modifier: 0, 0 Modifier: 0
upgrade.conyard: upgrade.conyard:
Valued: Valued:

View File

@@ -20,12 +20,10 @@ carryall.reinforce:
CanHover: True CanHover: True
Targetable@GROUND: Targetable@GROUND:
TargetTypes: Ground, Vehicle TargetTypes: Ground, Vehicle
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Targetable@AIRBORNE: Targetable@AIRBORNE:
TargetTypes: Air TargetTypes: Air
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
SpawnActorOnDeath: SpawnActorOnDeath:
Actor: carryall.husk Actor: carryall.husk
Carryall: Carryall:

View File

@@ -72,13 +72,11 @@ sandworm:
WithIdleAnimation: WithIdleAnimation:
Interval: 160 Interval: 160
Sequences: lightninga, lightningb, lightningc, lightningd, lightninge, lightningf Sequences: lightninga, lightningb, lightningc, lightningd, lightninge, lightningf
UpgradeTypes: attacking RequiresCondition: !attacking
UpgradeMaxEnabledLevel: 0
AmbientSound: AmbientSound:
SoundFile: WRMSIGN1.WAV SoundFile: WRMSIGN1.WAV
Interval: 160 Interval: 160
UpgradeTypes: attacking RequiresCondition: !attacking
UpgradeMaxEnabledLevel: 0
WithAttackOverlay@mouth: WithAttackOverlay@mouth:
Sequence: mouth Sequence: mouth
WithAttackOverlay@sand: WithAttackOverlay@sand:
@@ -106,8 +104,7 @@ sandworm:
Type: CenterPosition Type: CenterPosition
TerrainTypes: Sand, Dune, SpiceSand, Spice TerrainTypes: Sand, Dune, SpiceSand, Spice
MovingInterval: 3 MovingInterval: 3
UpgradeTypes: attacking RequiresCondition: !attacking
UpgradeMaxEnabledLevel: 0
UpgradeManager: UpgradeManager:
Buildable: Buildable:
Description: Attracted by vibrations in the sand.\nWill eat units whole and has a large appetite. Description: Attracted by vibrations in the sand.\nWill eat units whole and has a large appetite.

View File

@@ -17,42 +17,105 @@
^GainsExperience: ^GainsExperience:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
200: firepower, damage, speed, reload, inaccuracy, rank 200: rank-veteran-1
400: firepower, damage, speed, reload, inaccuracy, rank 400: rank-veteran-2
800: firepower, damage, speed, reload, inaccuracy, rank 800: rank-veteran-3
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal 1600: rank-elite
FirepowerMultiplier@EXPERIENCE: DamageMultiplier@RANK-1:
UpgradeTypes: firepower RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 105, 110, 115, 125 Modifier: 96
DamageMultiplier@EXPERIENCE: DamageMultiplier@RANK-2:
UpgradeTypes: damage RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 96, 92, 88, 80 Modifier: 92
SpeedMultiplier@EXPERIENCE: DamageMultiplier@RANK-3:
UpgradeTypes: speed RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 105, 110, 115, 125 Modifier: 88
ReloadDelayMultiplier@EXPERIENCE: DamageMultiplier@RANK-ELITE:
UpgradeTypes: reload RequiresCondition: rank-elite
Modifier: 96, 92, 88, 80 Modifier: 80
InaccuracyMultiplier@EXPERIENCE: FirepowerMultiplier@RANK-1:
UpgradeTypes: inaccuracy RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90, 80, 70, 50 Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 115
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 125
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 115
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 125
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 96
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 92
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 88
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 80
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 50
SelfHealing@ELITE: SelfHealing@ELITE:
Step: 0 Step: 0
PercentageStep: 4 PercentageStep: 4
Delay: 125 Delay: 125
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: selfheal RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1 WithDecoration@RANK-1:
WithRankDecoration:
Image: rank Image: rank
Sequence: rank Sequence: rank-veteran-1
Palette: effect Palette: effect
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: rank RequiresCondition: rank-veteran-1 && !rank-veteran-2
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank
Sequence: rank-elite
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-elite
ZOffset: 256 ZOffset: 256
UpgradeMinEnabledLevel: 1
UpgradeMaxAcceptedLevel: 4
^Vehicle: ^Vehicle:
Inherits@1: ^ExistsInWorld Inherits@1: ^ExistsInWorld
@@ -60,8 +123,6 @@
Inherits@3: ^SpriteActor Inherits@3: ^SpriteActor
Huntable: Huntable:
Mobile: Mobile:
UpgradeTypes: notmobile
UpgradeMaxEnabledLevel: 0
Crushes: crate, spicebloom Crushes: crate, spicebloom
TerrainSpeeds: TerrainSpeeds:
Sand: 100 Sand: 100
@@ -73,6 +134,7 @@
SpiceBlobs: 100 SpiceBlobs: 100
Dune: 50 Dune: 50
TurnSpeed: 5 TurnSpeed: 5
RequiresCondition: !notmobile
SelectionDecorations: SelectionDecorations:
WithSpriteControlGroupDecoration: WithSpriteControlGroupDecoration:
Selectable: Selectable:

View File

@@ -94,34 +94,28 @@ thumper:
RevealsShroud: RevealsShroud:
Range: 2c768 Range: 2c768
Mobile: Mobile:
UpgradeTypes: deployed
UpgradeMaxEnabledLevel: 0
Speed: 43 Speed: 43
RequiresCondition: !deployed
DeployToUpgrade: DeployToUpgrade:
DeployedUpgrades: deployed DeployedUpgrades: deployed
Facing: 128 Facing: 128
AllowedTerrainTypes: Sand, Spice, Dune, SpiceSand AllowedTerrainTypes: Sand, Spice, Dune, SpiceSand
WithInfantryBody: WithInfantryBody:
UpgradeTypes: deployed RequiresCondition: !deployed
UpgradeMaxEnabledLevel: 0
WithSpriteBody@DEPLOYED: WithSpriteBody@DEPLOYED:
Sequence: thump Sequence: thump
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
WithIdleOverlay@DEPLOYED: WithIdleOverlay@DEPLOYED:
Sequence: thump-sand Sequence: thump-sand
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
AmbientSound: AmbientSound:
SoundFile: THUMPER1.WAV SoundFile: THUMPER1.WAV
Interval: 60 Interval: 60
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
AttractsWorms: AttractsWorms:
Intensity: 1000 Intensity: 1000
Falloff: 0, 0, 0, 100, 100, 100, 25, 11, 6, 4, 3, 2, 1, 0 Falloff: 0, 0, 0, 100, 100, 100, 25, 11, 6, 4, 3, 2, 1, 0
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
Passenger: Passenger:
PipType: Blue PipType: Blue
Voiced: Voiced:

View File

@@ -98,15 +98,13 @@ construction_yard:
Sequence: tag-upgraded Sequence: tag-upgraded
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: stardecoration RequiresCondition: stardecoration
UpgradeMinEnabledLevel: 1
WithTextDecoration@primary: WithTextDecoration@primary:
RequiresSelection: true RequiresSelection: true
Text: PRIMARY Text: PRIMARY
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
wind_trap: wind_trap:
Inherits: ^Building Inherits: ^Building
@@ -212,15 +210,13 @@ barracks:
Sequence: tag-upgraded Sequence: tag-upgraded
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: stardecoration RequiresCondition: stardecoration
UpgradeMinEnabledLevel: 1
WithTextDecoration@primary: WithTextDecoration@primary:
RequiresSelection: true RequiresSelection: true
Text: PRIMARY Text: PRIMARY
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
refinery: refinery:
Inherits: ^Building Inherits: ^Building
@@ -393,15 +389,13 @@ light_factory:
Sequence: tag-upgraded Sequence: tag-upgraded
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: stardecoration RequiresCondition: stardecoration
UpgradeMinEnabledLevel: 1
WithTextDecoration@primary: WithTextDecoration@primary:
RequiresSelection: true RequiresSelection: true
Text: PRIMARY Text: PRIMARY
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
heavy_factory: heavy_factory:
Inherits: ^Building Inherits: ^Building
@@ -474,15 +468,13 @@ heavy_factory:
Sequence: tag-upgraded Sequence: tag-upgraded
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: stardecoration RequiresCondition: stardecoration
UpgradeMinEnabledLevel: 1
WithTextDecoration@primary: WithTextDecoration@primary:
RequiresSelection: true RequiresSelection: true
Text: PRIMARY Text: PRIMARY
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
outpost: outpost:
Inherits: ^Building Inherits: ^Building
@@ -597,8 +589,7 @@ starport:
Text: PRIMARY Text: PRIMARY
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
wall: wall:
Inherits@1: ^SpriteActor Inherits@1: ^SpriteActor
@@ -854,8 +845,7 @@ high_tech_factory:
Sequence: tag-upgraded Sequence: tag-upgraded
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: stardecoration RequiresCondition: stardecoration
UpgradeMinEnabledLevel: 1
research_centre: research_centre:
Inherits: ^Building Inherits: ^Building

View File

@@ -151,8 +151,13 @@ poweroff:
ZOffset: 2047 ZOffset: 2047
rank: rank:
rank: rank.shp rank-veteran-1: rank.shp
Length: * rank-veteran-2: rank.shp
Start: 1
rank-veteran-3: rank.shp
Start: 2
rank-elite: rank.shp
Start: 3
overlay: overlay:
Defaults: DATA.R8 Defaults: DATA.R8

View File

@@ -33,8 +33,7 @@ HACKE6:
Captures: Captures:
CaptureTypes: building CaptureTypes: building
Targetable: Targetable:
UpgradeTypes: jail RequiresCondition: !jail
UpgradeMaxEnabledLevel: 0
Targetable@PRISONER: Targetable@PRISONER:
TargetTypes: Prisoner TargetTypes: Prisoner
RenderSprites: RenderSprites:
@@ -42,8 +41,7 @@ HACKE6:
MEDI: MEDI:
Targetable: Targetable:
UpgradeTypes: jail RequiresCondition: !jail
UpgradeMaxEnabledLevel: 0
Targetable@PRISONER: Targetable@PRISONER:
TargetTypes: Prisoner TargetTypes: Prisoner

View File

@@ -34,8 +34,7 @@ HACKE6:
CaptureTypes: building CaptureTypes: building
WithInfantryBody: WithInfantryBody:
Targetable: Targetable:
UpgradeTypes: jail RequiresCondition: !jail
UpgradeMaxEnabledLevel: 0
Targetable@PRISONER: Targetable@PRISONER:
TargetTypes: Prisoner TargetTypes: Prisoner
RenderSprites: RenderSprites:
@@ -43,8 +42,7 @@ HACKE6:
MEDI: MEDI:
Targetable: Targetable:
UpgradeTypes: jail RequiresCondition: !jail
UpgradeMaxEnabledLevel: 0
Targetable@PRISONER: Targetable@PRISONER:
TargetTypes: Prisoner TargetTypes: Prisoner

View File

@@ -21,8 +21,8 @@ World:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
^Tank: ^Tank:
GivesBounty: GivesBounty:
@@ -30,8 +30,8 @@ World:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
^Infantry: ^Infantry:
GivesBounty: GivesBounty:
@@ -45,8 +45,8 @@ World:
DeathSounds@ZAPPED: DeathSounds@ZAPPED:
VolumeMultiplier: 0.1 VolumeMultiplier: 0.1
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
^Ship: ^Ship:
GivesBounty: GivesBounty:
@@ -54,22 +54,22 @@ World:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
^Plane: ^Plane:
GivesBounty: GivesBounty:
Percentage: 0 Percentage: 0
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
^Building: ^Building:
GivesBounty: GivesBounty:
Percentage: 0 Percentage: 0
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
OILB: OILB:
CashTrickler: CashTrickler:

View File

@@ -71,8 +71,7 @@ TRUK.Hijackable:
Buildable: Buildable:
Prerequisites: ~disabled Prerequisites: ~disabled
Mobile: Mobile:
UpgradeTypes: mobile RequiresCondition: mobile
UpgradeMinEnabledLevel: 1
Cargo: Cargo:
Types: Infantry Types: Infantry
MaxWeight: 5 MaxWeight: 5

View File

@@ -223,23 +223,19 @@ TRAN:
WithIdleOverlay@ROTOR1AIR: WithIdleOverlay@ROTOR1AIR:
Offset: 597,0,213 Offset: 597,0,213
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTOR1GROUND: WithIdleOverlay@ROTOR1GROUND:
Offset: 597,0,213 Offset: 597,0,213
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
WithIdleOverlay@ROTOR2AIR: WithIdleOverlay@ROTOR2AIR:
Offset: -597,0,341 Offset: -597,0,341
Sequence: rotor2 Sequence: rotor2
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTOR2GROUND: WithIdleOverlay@ROTOR2GROUND:
Offset: -597,0,341 Offset: -597,0,341
Sequence: slow-rotor2 Sequence: slow-rotor2
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Cargo: Cargo:
Types: Infantry Types: Infantry
MaxWeight: 8 MaxWeight: 8
@@ -287,13 +283,11 @@ HELI:
WithIdleOverlay@ROTORAIR: WithIdleOverlay@ROTORAIR:
Offset: 0,0,85 Offset: 0,0,85
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTORGROUND: WithIdleOverlay@ROTORGROUND:
Offset: 0,0,85 Offset: 0,0,85
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
AmmoPool: AmmoPool:
Ammo: 8 Ammo: 8
SelectionDecorations: SelectionDecorations:
@@ -345,12 +339,10 @@ HIND:
InitialStanceAI: HoldFire InitialStanceAI: HoldFire
WithIdleOverlay@ROTORAIR: WithIdleOverlay@ROTORAIR:
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTORGROUND: WithIdleOverlay@ROTORGROUND:
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
AmmoPool: AmmoPool:
Ammo: 24 Ammo: 24
PipCount: 6 PipCount: 6

View File

@@ -16,48 +16,110 @@
^GainsExperience: ^GainsExperience:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
200: firepower, damage, speed, reload, inaccuracy, rank 200: rank-veteran-1
400: firepower, damage, speed, reload, inaccuracy, rank 400: rank-veteran-2
800: firepower, damage, speed, reload, inaccuracy, rank 800: rank-veteran-3
1600: firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal 1600: rank-elite
DamageMultiplier@EXPERIENCE: DamageMultiplier@RANK-1:
UpgradeTypes: damage RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 95, 90, 85, 75 Modifier: 95
FirepowerMultiplier@EXPERIENCE: DamageMultiplier@RANK-2:
UpgradeTypes: firepower RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 105, 110, 120, 130 Modifier: 90
SpeedMultiplier@EXPERIENCE: DamageMultiplier@RANK-3:
UpgradeTypes: speed RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 105, 110, 120, 140 Modifier: 85
ReloadDelayMultiplier@EXPERIENCE: DamageMultiplier@RANK-ELITE:
UpgradeTypes: reload RequiresCondition: rank-elite
Modifier: 95, 90, 85, 75 Modifier: 75
InaccuracyMultiplier@EXPERIENCE: FirepowerMultiplier@RANK-1:
UpgradeTypes: inaccuracy RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90, 80, 70, 50 Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 120
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 130
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 120
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 140
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 95
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 90
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 85
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 50
SelfHealing@ELITE: SelfHealing@ELITE:
Step: 2 Step: 2
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: selfheal RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1 WithDecoration@RANK-1:
WithRankDecoration:
Image: rank Image: rank
Sequence: rank Sequence: rank-veteran-1
Palette: effect Palette: effect
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: rank RequiresCondition: rank-veteran-1 && !rank-veteran-2
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank
Sequence: rank-elite
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-elite
ZOffset: 256 ZOffset: 256
UpgradeMinEnabledLevel: 1
UpgradeMaxAcceptedLevel: 4
^IronCurtainable: ^IronCurtainable:
UpgradeOverlay@IRONCURTAIN: UpgradeOverlay@IRONCURTAIN:
UpgradeTypes: invulnerability RequiresCondition: invulnerability
UpgradeMinEnabledLevel: 1
DamageMultiplier@IRONCURTAIN: DamageMultiplier@IRONCURTAIN:
UpgradeTypes: invulnerability RequiresCondition: invulnerability
Modifier: 0 Modifier: 0
TimedUpgradeBar: TimedUpgradeBar:
Upgrade: invulnerability Upgrade: invulnerability
@@ -87,8 +149,7 @@
Bounds: 24, 24 Bounds: 24, 24
Targetable: Targetable:
TargetTypes: Ground, Repair, Vehicle TargetTypes: Ground, Repair, Vehicle
UpgradeTypes: parachute RequiresCondition: !parachute
UpgradeMaxEnabledLevel: 0
Repairable: Repairable:
Chronoshiftable: Chronoshiftable:
Passenger: Passenger:
@@ -133,14 +194,13 @@
EmptyWeapon: UnitExplodeSmall EmptyWeapon: UnitExplodeSmall
WithFacingSpriteBody: WithFacingSpriteBody:
WithParachute: WithParachute:
UpgradeTypes: parachute
UpgradeMinEnabledLevel: 1
ShadowImage: parach-shadow ShadowImage: parach-shadow
ShadowSequence: idle ShadowSequence: idle
Image: parach Image: parach
Sequence: idle Sequence: idle
OpeningSequence: open OpeningSequence: open
Offset: 0,0,200 Offset: 0,0,200
RequiresCondition: parachute
BodyOrientation: BodyOrientation:
UseClassicFacingFudge: True UseClassicFacingFudge: True
@@ -195,8 +255,7 @@
Bounds: 12,18,0,-8 Bounds: 12,18,0,-8
Targetable: Targetable:
TargetTypes: Ground, Infantry, Disguise TargetTypes: Ground, Infantry, Disguise
UpgradeTypes: parachute RequiresCondition: !parachute
UpgradeMaxEnabledLevel: 0
QuantizeFacingsFromSequence: QuantizeFacingsFromSequence:
Sequence: stand Sequence: stand
WithInfantryBody: WithInfantryBody:
@@ -228,8 +287,7 @@
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: hospitalheal RequiresCondition: hospitalheal
UpgradeMinEnabledLevel: 1
GlobalUpgradable: GlobalUpgradable:
Upgrades: hospitalheal Upgrades: hospitalheal
Prerequisites: hosp Prerequisites: hosp
@@ -252,14 +310,13 @@
Voiced: Voiced:
VoiceSet: GenericVoice VoiceSet: GenericVoice
WithParachute: WithParachute:
UpgradeTypes: parachute
UpgradeMinEnabledLevel: 1
ShadowImage: parach-shadow ShadowImage: parach-shadow
ShadowSequence: idle ShadowSequence: idle
Image: parach Image: parach
Sequence: idle Sequence: idle
OpeningSequence: open OpeningSequence: open
Offset: 0,0,427 Offset: 0,0,427
RequiresCondition: parachute
^Soldier: ^Soldier:
Inherits: ^Infantry Inherits: ^Infantry
@@ -370,12 +427,10 @@
CruisingUpgrades: cruising CruisingUpgrades: cruising
Targetable@GROUND: Targetable@GROUND:
TargetTypes: Ground, Repair, Vehicle TargetTypes: Ground, Repair, Vehicle
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Targetable@AIRBORNE: Targetable@AIRBORNE:
TargetTypes: Air TargetTypes: Air
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
HiddenUnderFog: HiddenUnderFog:
Type: CenterPosition Type: CenterPosition
AttackMove: AttackMove:
@@ -414,8 +469,7 @@
GpsDot: GpsDot:
String: Helicopter String: Helicopter
Hovers@CRUISING: Hovers@CRUISING:
UpgradeTypes: cruising RequiresCondition: cruising
UpgradeMinEnabledLevel: 1
BodyOrientation: BodyOrientation:
UseClassicFacingFudge: True UseClassicFacingFudge: True
@@ -825,13 +879,12 @@
KilledOnImpassableTerrain: false KilledOnImpassableTerrain: false
Passenger: Passenger:
WithParachute: WithParachute:
UpgradeTypes: parachute
UpgradeMinEnabledLevel: 1
Image: parach Image: parach
Sequence: idle Sequence: idle
OpeningSequence: open OpeningSequence: open
ShadowImage: parach-shadow ShadowImage: parach-shadow
ShadowSequence: idle ShadowSequence: idle
RequiresCondition: parachute
UpgradeManager: UpgradeManager:
^Mine: ^Mine:

View File

@@ -226,8 +226,7 @@ SPY:
Palette: effect Palette: effect
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: disguise RequiresCondition: disguise
UpgradeMinEnabledLevel: 1
IgnoresDisguise: IgnoresDisguise:
DetectCloaked: DetectCloaked:
CloakTypes: Cloak, Hijacker CloakTypes: Cloak, Hijacker

View File

@@ -21,12 +21,10 @@ SS:
Range: 6c0 Range: 6c0
Targetable: Targetable:
TargetTypes: Ground, Water, Repair TargetTypes: Ground, Water, Repair
UpgradeTypes: underwater RequiresCondition: !underwater
UpgradeMaxEnabledLevel: 0
Targetable@UNDERWATER: Targetable@UNDERWATER:
TargetTypes: Underwater, Repair TargetTypes: Underwater, Repair
UpgradeTypes: underwater RequiresCondition: underwater
UpgradeMinEnabledLevel: 1
Cloak: Cloak:
CloakTypes: Underwater CloakTypes: Underwater
InitialDelay: 0 InitialDelay: 0
@@ -77,12 +75,10 @@ MSUB:
Range: 6c0 Range: 6c0
Targetable: Targetable:
TargetTypes: Ground, Water, Repair TargetTypes: Ground, Water, Repair
UpgradeTypes: underwater RequiresCondition: !underwater
UpgradeMaxEnabledLevel: 0
Targetable@UNDERWATER: Targetable@UNDERWATER:
TargetTypes: Underwater, Repair TargetTypes: Underwater, Repair
UpgradeTypes: underwater RequiresCondition: underwater
UpgradeMinEnabledLevel: 1
Cloak: Cloak:
CloakTypes: Underwater CloakTypes: Underwater
InitialDelay: 0 InitialDelay: 0
@@ -230,12 +226,11 @@ LST:
Armor: Armor:
Type: Heavy Type: Heavy
Mobile: Mobile:
UpgradeTypes: notmobile
UpgradeMaxEnabledLevel: 0
TurnSpeed: 10 TurnSpeed: 10
Speed: 113 Speed: 113
TerrainSpeeds: TerrainSpeeds:
Beach: 70 Beach: 70
RequiresCondition: !notmobile
RevealsShroud: RevealsShroud:
Range: 6c0 Range: 6c0
SelectionDecorations: SelectionDecorations:

View File

@@ -174,8 +174,7 @@ SPEN:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
SYRD: SYRD:
Inherits: ^Building Inherits: ^Building
@@ -270,8 +269,7 @@ SYRD:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
IRON: IRON:
Inherits: ^ScienceBuilding Inherits: ^ScienceBuilding
@@ -877,8 +875,7 @@ WEAP:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
FACT: FACT:
Inherits: ^Building Inherits: ^Building
@@ -1116,8 +1113,7 @@ HPAD:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
WithRearmAnimation: WithRearmAnimation:
AFLD: AFLD:
@@ -1233,8 +1229,7 @@ AFLD:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
WithRearmAnimation: WithRearmAnimation:
POWR: POWR:
@@ -1413,8 +1408,7 @@ BARR:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
KENN: KENN:
Inherits: ^Building Inherits: ^Building
@@ -1461,8 +1455,7 @@ KENN:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
TENT: TENT:
Inherits: ^Building Inherits: ^Building
@@ -1541,8 +1534,7 @@ TENT:
Sequence: tag-primary Sequence: tag-primary
ReferencePoint: Top ReferencePoint: Top
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
FIX: FIX:
Inherits: ^Building Inherits: ^Building

View File

@@ -338,8 +338,7 @@ JEEP:
Mobile: Mobile:
TurnSpeed: 10 TurnSpeed: 10
Speed: 170 Speed: 170
UpgradeTypes: notmobile RequiresCondition: !notmobile
UpgradeMaxEnabledLevel: 0
RevealsShroud: RevealsShroud:
Range: 8c0 Range: 8c0
Turreted: Turreted:
@@ -378,8 +377,7 @@ APC:
Mobile: Mobile:
Speed: 142 Speed: 142
Crushes: wall, mine, crate, infantry Crushes: wall, mine, crate, infantry
UpgradeTypes: notmobile RequiresCondition: !notmobile
UpgradeMaxEnabledLevel: 0
RevealsShroud: RevealsShroud:
Range: 5c0 Range: 5c0
Armament: Armament:
@@ -651,8 +649,7 @@ DTRK:
AttackSuicides: AttackSuicides:
-DamageMultiplier@IRONCURTAIN: -DamageMultiplier@IRONCURTAIN:
KillsSelf: KillsSelf:
UpgradeTypes: invulnerability RequiresCondition: invulnerability
UpgradeMinEnabledLevel: 1
Chronoshiftable: Chronoshiftable:
ExplodeInstead: yes ExplodeInstead: yes
@@ -738,8 +735,7 @@ STNK:
Mobile: Mobile:
Speed: 142 Speed: 142
Crushes: wall, mine, crate, infantry Crushes: wall, mine, crate, infantry
UpgradeTypes: notmobile RequiresCondition: !notmobile
UpgradeMaxEnabledLevel: 0
RevealsShroud: RevealsShroud:
Range: 7c0 Range: 7c0
AutoTarget: AutoTarget:

View File

@@ -424,8 +424,13 @@ fire:
ZOffset: 1023 ZOffset: 1023
rank: rank:
rank: rank-veteran-1:
Length: * rank-veteran-2:
Start: 1
rank-veteran-3:
Start: 2
rank-elite:
Start: 3
iconchevrons: iconchevrons:
veteran: veteran:

View File

@@ -1447,13 +1447,13 @@ Rules:
InitialUnits: e1, e1, e2, e2, medic InitialUnits: e1, e1, e2, e2, medic
GACTWR: GACTWR:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
NAOBEL: NAOBEL:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0
NALASR: NALASR:
DamageMultiplier@UNKILLABLE: DamageMultiplier@UNKILLABLE:
UpgradeTypes: unkillable RequiresCondition: unkillable
Modifier: 0, 0 Modifier: 0

View File

@@ -147,8 +147,7 @@ ORCAB:
AutoTarget: AutoTarget:
RenderSprites: RenderSprites:
Hovers@CRUISING: Hovers@CRUISING:
UpgradeTypes: cruising RequiresCondition: cruising
UpgradeMinEnabledLevel: 1
SpawnActorOnDeath: SpawnActorOnDeath:
Actor: ORCAB.Husk Actor: ORCAB.Husk
@@ -310,13 +309,11 @@ APACHE:
WithIdleOverlay@ROTORAIR: WithIdleOverlay@ROTORAIR:
Offset: 85,0,384 Offset: 85,0,384
Sequence: rotor Sequence: rotor
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
WithIdleOverlay@ROTORGROUND: WithIdleOverlay@ROTORGROUND:
Offset: 85,0,384 Offset: 85,0,384
Sequence: slow-rotor Sequence: slow-rotor
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
RenderSprites: RenderSprites:
SpawnActorOnDeath: SpawnActorOnDeath:
Actor: APACHE.Husk Actor: APACHE.Husk
@@ -351,8 +348,7 @@ HUNTER:
Image: GGHUNT Image: GGHUNT
WithFacingSpriteBody: WithFacingSpriteBody:
Hovers@CRUISING: Hovers@CRUISING:
UpgradeTypes: cruising RequiresCondition: cruising
UpgradeMinEnabledLevel: 1
QuantizeFacingsFromSequence: QuantizeFacingsFromSequence:
AutoSelectionSize: AutoSelectionSize:
DrawLineToTarget: DrawLineToTarget:

View File

@@ -70,8 +70,7 @@ CHAMSPY:
Palette: pips Palette: pips
ReferencePoint: Top, Right ReferencePoint: Top, Right
ZOffset: 256 ZOffset: 256
UpgradeTypes: disguise RequiresCondition: disguise
UpgradeMinEnabledLevel: 1
Infiltrates: Infiltrates:
Types: SpyInfiltrate Types: SpyInfiltrate
-AutoTarget: -AutoTarget:

View File

@@ -18,69 +18,80 @@
^GainsExperience: ^GainsExperience:
GainsExperience: GainsExperience:
Upgrades: Upgrades:
500: rank, firepower, damage, speed, reload 500: rank-veteran
1000: rank, firepower, damage, speed, reload, selfheal, eliteweapon 1000: rank-elite
FirepowerMultiplier@EXPERIENCE: FirepowerMultiplier@VETERAN:
UpgradeTypes: firepower RequiresCondition: (rank-veteran && !rank-elite) || crate-firepower
Modifier: 110, 130 Modifier: 110
DamageMultiplier@EXPERIENCE: FirepowerMultiplier@ELITE:
UpgradeTypes: damage RequiresCondition: rank-elite || (rank-veteran && crate-firepower)
Modifier: 90, 75 Modifier: 130
SpeedMultiplier@EXPERIENCE: DamageMultiplier@VETERAN:
UpgradeTypes: speed RequiresCondition: (rank-veteran && !rank-elite) || crate-damage
Modifier: 120, 140 Modifier: 90
ReloadDelayMultiplier@EXPERIENCE: DamageMultiplier@ELITE:
UpgradeTypes: reload RequiresCondition: rank-elite || (rank-veteran && crate-damage)
Modifier: 90, 75 Modifier: 75
SpeedMultiplier@VETERAN:
RequiresCondition: (rank-veteran && !rank-elite) || crate-speed
Modifier: 120
SpeedMultiplier@ELITE:
RequiresCondition: rank-elite || (rank-veteran && crate-speed)
Modifier: 140
ReloadDelayMultiplier@VETERAN:
RequiresCondition: rank-veteran && !rank-elite
Modifier: 90
ReloadDelayMultiplier@ELITE:
RequiresCondition: rank-elite
Modifier: 75
SelfHealing@ELITE: SelfHealing@ELITE:
Step: 2 Step: 2
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: selfheal RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1 WithDecoration@VETERAN:
WithRankDecoration:
Image: rank Image: rank
Sequence: rank Sequence: veteran
Palette: ra Palette: ra
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: rank RequiresCondition: rank-veteran && !rank-elite
ZOffset: 256
WithDecoration@ELITE:
Image: rank
Sequence: elite
Palette: ra
ReferencePoint: Bottom, Right
RequiresCondition: rank-elite
ZOffset: 256 ZOffset: 256
UpgradeMinEnabledLevel: 1
UpgradeMaxAcceptedLevel: 2
^EmpDisable: ^EmpDisable:
UpgradeOverlay@EMPDISABLE: UpgradeOverlay@EMPDISABLE:
UpgradeTypes: empdisable RequiresCondition: empdisable
UpgradeMinEnabledLevel: 1
Palette: disabled Palette: disabled
DisableOnUpgrade@EMPDISABLE: DisableOnUpgrade@EMPDISABLE:
UpgradeTypes: empdisable RequiresCondition: empdisable
UpgradeMinEnabledLevel: 1
TimedUpgradeBar@EMPDISABLE: TimedUpgradeBar@EMPDISABLE:
Upgrade: empdisable Upgrade: empdisable
Color: FFFFFF Color: FFFFFF
WithIdleOverlay@EMPDISABLE: WithIdleOverlay@EMPDISABLE:
Sequence: emp-overlay Sequence: emp-overlay
Palette: effect Palette: effect
UpgradeTypes: empdisable RequiresCondition: empdisable
ShowToEnemies: true ShowToEnemies: true
ZOffset: 512 ZOffset: 512
UpgradeMinEnabledLevel: 1
PowerMultiplier@EMPDISABLE: PowerMultiplier@EMPDISABLE:
UpgradeTypes: empdisable RequiresCondition: empdisable
Modifier: 0 Modifier: 0
^EmpDisableMobile: ^EmpDisableMobile:
Inherits: ^EmpDisable Inherits: ^EmpDisable
Mobile: Mobile:
UpgradeTypes: notmobile RequiresCondition: !notmobile
UpgradeMaxEnabledLevel: 0
^Cloakable: ^Cloakable:
Cloak@CLOAKGENERATOR: Cloak@CLOAKGENERATOR:
UpgradeTypes: cloakgenerator RequiresCondition: cloakgenerator || crate-cloak
UpgradeMinEnabledLevel: 1
InitialDelay: 0 InitialDelay: 0
CloakDelay: 90 CloakDelay: 90
IsPlayerPalette: true IsPlayerPalette: true
@@ -306,8 +317,7 @@
Delay: 100 Delay: 100
HealIfBelow: 100 HealIfBelow: 100
DamageCooldown: 125 DamageCooldown: 125
UpgradeTypes: hospitalheal RequiresCondition: hospitalheal
UpgradeMinEnabledLevel: 1
GlobalUpgradable@HOSPITAL: GlobalUpgradable@HOSPITAL:
Upgrades: hospitalheal Upgrades: hospitalheal
Prerequisites: cahosp Prerequisites: cahosp
@@ -316,8 +326,7 @@
Sequence: medic Sequence: medic
Palette: pips Palette: pips
ReferencePoint: Bottom, Right ReferencePoint: Bottom, Right
UpgradeTypes: hospitalheal RequiresCondition: hospitalheal
UpgradeMinEnabledLevel: 1
^RegularInfantryDeath: ^RegularInfantryDeath:
WithDeathAnimation@normal: WithDeathAnimation@normal:
@@ -406,7 +415,7 @@
ValidDamageStates: Critical ValidDamageStates: Critical
GrantPermanently: true GrantPermanently: true
SpeedMultiplier@CRITICAL: SpeedMultiplier@CRITICAL:
UpgradeTypes: criticalspeed RequiresCondition: criticalspeed
Modifier: 50 Modifier: 50
^CivilianInfantry: ^CivilianInfantry:
@@ -488,10 +497,10 @@
Upgrades: criticalspeed Upgrades: criticalspeed
ValidDamageStates: Critical ValidDamageStates: Critical
SpeedMultiplier@DAMAGED: SpeedMultiplier@DAMAGED:
UpgradeTypes: damagedspeed RequiresCondition: damagedspeed
Modifier: 80 Modifier: 80
SpeedMultiplier@CRITICAL: SpeedMultiplier@CRITICAL:
UpgradeTypes: criticalspeed RequiresCondition: criticalspeed
Modifier: 60 Modifier: 60
Carryable: Carryable:
@@ -543,12 +552,10 @@
UseLocation: true UseLocation: true
Targetable@GROUND: Targetable@GROUND:
TargetTypes: Ground, Vehicle TargetTypes: Ground, Vehicle
UpgradeTypes: airborne RequiresCondition: !airborne
UpgradeMaxEnabledLevel: 0
Targetable@AIRBORNE: Targetable@AIRBORNE:
TargetTypes: Air TargetTypes: Air
UpgradeTypes: airborne RequiresCondition: airborne
UpgradeMinEnabledLevel: 1
Selectable: Selectable:
WithTextControlGroupDecoration: WithTextControlGroupDecoration:
SelectionDecorations: SelectionDecorations:
@@ -584,8 +591,7 @@
CruiseAltitude: 2048 CruiseAltitude: 2048
CanHover: True CanHover: True
Hovers@CRUISING: Hovers@CRUISING:
UpgradeTypes: cruising RequiresCondition: cruising
UpgradeMinEnabledLevel: 1
^Plane: ^Plane:
Inherits: ^Aircraft Inherits: ^Aircraft
@@ -939,5 +945,4 @@
Upgrades: veins Upgrades: veins
WithIdleOverlay@VEINS: WithIdleOverlay@VEINS:
Sequence: veins Sequence: veins
UpgradeTypes: veins RequiresCondition: veins
UpgradeMinEnabledLevel: 1

View File

@@ -41,24 +41,20 @@ GAPOWR:
Upgrades: Upgrades:
powrup: powrup.a powrup: powrup.a
Power@pluga: Power@pluga:
UpgradeTypes: powrup.a RequiresCondition: powrup.a
UpgradeMinEnabledLevel: 1
Amount: 50 Amount: 50
WithIdleOverlay@pluga: WithIdleOverlay@pluga:
UpgradeTypes: powrup.a RequiresCondition: powrup.a
UpgradeMinEnabledLevel: 1
Sequence: idle-powrupa Sequence: idle-powrupa
Pluggable@plugb: Pluggable@plugb:
Offset: 1,1 Offset: 1,1
Upgrades: Upgrades:
powrup: powrup.b powrup: powrup.b
WithIdleOverlay@plugb: WithIdleOverlay@plugb:
UpgradeTypes: powrup.b RequiresCondition: powrup.b
UpgradeMinEnabledLevel: 1
Sequence: idle-powrupb Sequence: idle-powrupb
Power@plugb: Power@plugb:
UpgradeTypes: powrup.b RequiresCondition: powrup.b
UpgradeMinEnabledLevel: 1
Amount: 50 Amount: 50
ProvidesPrerequisite@buildingname: ProvidesPrerequisite@buildingname:
SelectionDecorations: SelectionDecorations:
@@ -118,8 +114,7 @@ GAPILE:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
GAWEAP: GAWEAP:
Inherits: ^Building Inherits: ^Building
@@ -181,8 +176,7 @@ GAWEAP:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
GAHPAD: GAHPAD:
Inherits: ^Building Inherits: ^Building
@@ -236,8 +230,7 @@ GAHPAD:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
GADEPT: GADEPT:
Inherits: ^Building Inherits: ^Building
@@ -406,8 +399,7 @@ GAPLUG:
MaxHeightDelta: 3 MaxHeightDelta: 3
IonCannonPower: IonCannonPower:
Cursor: ioncannon Cursor: ioncannon
UpgradeTypes: plug.ioncannon RequiresCondition: plug.ioncannona || plug.ioncannonb
UpgradeMinEnabledLevel: 1
Icon: ioncannon Icon: ioncannon
Effect: explosion Effect: explosion
EffectSequence: ionring EffectSequence: ionring
@@ -421,8 +413,7 @@ GAPLUG:
DisplayRadarPing: True DisplayRadarPing: True
CameraActor: camera CameraActor: camera
ProduceActorPower: ProduceActorPower:
UpgradeTypes: plug.hunterseeker RequiresCondition: plug.hunterseekera || plug.hunterseekerb
UpgradeMinEnabledLevel: 1
Description: Hunter Seeker Description: Hunter Seeker
LongDesc: Releases a drone that will acquire and destroy an enemy target. LongDesc: Releases a drone that will acquire and destroy an enemy target.
Icon: hunterseeker Icon: hunterseeker
@@ -437,38 +428,32 @@ GAPLUG:
Power: Power:
Amount: -150 Amount: -150
Power@ioncannon: Power@ioncannon:
UpgradeTypes: plug.ioncannon RequiresCondition: plug.ioncannona || plug.ioncannonb
UpgradeMinEnabledLevel: 1
Amount: -100 Amount: -100
Power@hunterseeker: Power@hunterseeker:
UpgradeTypes: plug.hunterseeker RequiresCondition: plug.hunterseekera || plug.hunterseekerb
UpgradeMinEnabledLevel: 1
Amount: -50 Amount: -50
Pluggable@pluga: Pluggable@pluga:
Offset: 0,2 Offset: 0,2
Upgrades: Upgrades:
plug.ioncannon: plug.ioncannon, plug.ioncannona plug.ioncannon: plug.ioncannona
plug.hunterseeker: plug.hunterseeker, plug.hunterseekera plug.hunterseeker: plug.hunterseekera
WithIdleOverlay@ioncannona: WithIdleOverlay@ioncannona:
UpgradeTypes: plug.ioncannona RequiresCondition: plug.ioncannona
UpgradeMinEnabledLevel: 1
Sequence: idle-ioncannona Sequence: idle-ioncannona
WithIdleOverlay@hunterseekera: WithIdleOverlay@hunterseekera:
UpgradeTypes: plug.hunterseekera RequiresCondition: plug.hunterseekera
UpgradeMinEnabledLevel: 1
Sequence: idle-hunterseekera Sequence: idle-hunterseekera
Pluggable@plugb: Pluggable@plugb:
Offset: 1,2 Offset: 1,2
Upgrades: Upgrades:
plug.ioncannon: plug.ioncannon, plug.ioncannonb plug.ioncannon: plug.ioncannonb
plug.hunterseeker: plug.hunterseeker, plug.hunterseekerb plug.hunterseeker: plug.hunterseekerb
WithIdleOverlay@ioncannonb: WithIdleOverlay@ioncannonb:
UpgradeTypes: plug.ioncannonb RequiresCondition: plug.ioncannonb
UpgradeMinEnabledLevel: 1
Sequence: idle-ioncannonb Sequence: idle-ioncannonb
WithIdleOverlay@hunterseekerb: WithIdleOverlay@hunterseekerb:
UpgradeTypes: plug.hunterseekerb RequiresCondition: plug.hunterseekerb
UpgradeMinEnabledLevel: 1
Sequence: idle-hunterseekerb Sequence: idle-hunterseekerb
ProvidesPrerequisite@buildingname: ProvidesPrerequisite@buildingname:
SelectionDecorations: SelectionDecorations:

View File

@@ -68,61 +68,51 @@ GACTWR:
BodyOrientation: BodyOrientation:
QuantizedFacings: 32 QuantizedFacings: 32
DetectCloaked: DetectCloaked:
UpgradeTypes: tower RequiresCondition: tower.vulcan || tower.rocket || tower.sam
UpgradeMinEnabledLevel: 1
Turreted: Turreted:
TurnSpeed: 10 TurnSpeed: 10
InitialFacing: 224 InitialFacing: 224
AttackTurreted: AttackTurreted:
UpgradeTypes: tower RequiresCondition: tower.vulcan || tower.rocket || tower.sam
UpgradeMinEnabledLevel: 1
CanPowerDown: CanPowerDown:
IndicatorPalette: mouse IndicatorPalette: mouse
PowerupSpeech: EnablePower PowerupSpeech: EnablePower
PowerdownSpeech: DisablePower PowerdownSpeech: DisablePower
WithSpriteTurret@VULC: WithSpriteTurret@VULC:
UpgradeTypes: tower.vulcan RequiresCondition: tower.vulcan
UpgradeMinEnabledLevel: 1
Recoils: false Recoils: false
Sequence: turret-vulcan Sequence: turret-vulcan
WithSpriteTurret@ROCKET: WithSpriteTurret@ROCKET:
UpgradeTypes: tower.rocket RequiresCondition: tower.rocket
UpgradeMinEnabledLevel: 1
Recoils: false Recoils: false
Sequence: turret-rocket Sequence: turret-rocket
WithSpriteTurret@SAM: WithSpriteTurret@SAM:
UpgradeTypes: tower.sam RequiresCondition: tower.sam
UpgradeMinEnabledLevel: 1
Recoils: false Recoils: false
Sequence: turret-sam Sequence: turret-sam
Armament@VULCPRIMARY: Armament@VULCPRIMARY:
UpgradeTypes: tower.vulcan RequiresCondition: tower.vulcan
UpgradeMinEnabledLevel: 1
Weapon: VulcanTower Weapon: VulcanTower
LocalOffset: 416,85,960 LocalOffset: 416,85,960
MuzzleSequence: muzzle MuzzleSequence: muzzle
MuzzleSplitFacings: 8 MuzzleSplitFacings: 8
Armament@VULCSECONDARY: Armament@VULCSECONDARY:
UpgradeTypes: tower.vulcan RequiresCondition: tower.vulcan
UpgradeMinEnabledLevel: 1
Name: secondary Name: secondary
Weapon: VulcanTower Weapon: VulcanTower
LocalOffset: 416,-85,960 LocalOffset: 416,-85,960
MuzzleSequence: muzzle MuzzleSequence: muzzle
MuzzleSplitFacings: 8 MuzzleSplitFacings: 8
Armament@ROCKET: Armament@ROCKET:
UpgradeTypes: tower.rocket RequiresCondition: tower.rocket
UpgradeMinEnabledLevel: 1
Weapon: RPGTower Weapon: RPGTower
LocalOffset: 192,-65,1056 LocalOffset: 192,-65,1056
Armament@SAM: Armament@SAM:
UpgradeTypes: tower.sam RequiresCondition: tower.sam
UpgradeMinEnabledLevel: 1
Weapon: RedEye2 Weapon: RedEye2
LocalOffset: 384,0,1200 LocalOffset: 384,0,1200
WithMuzzleOverlay: WithMuzzleOverlay:
UpgradeTypes: tower.vulcan RequiresCondition: tower.vulcan
UpgradeMinEnabledLevel: 1
WithIdleOverlay@LIGHTS: WithIdleOverlay@LIGHTS:
Sequence: idle-lights Sequence: idle-lights
LineBuildNode: LineBuildNode:
@@ -130,18 +120,16 @@ GACTWR:
Power@base: Power@base:
Amount: -10 Amount: -10
Power@turrets: Power@turrets:
UpgradeTypes: tower RequiresCondition: tower.vulcan || tower.rocket || tower.sam
UpgradeMinEnabledLevel: 1
Amount: -20 Amount: -20
Power@samextra: Power@samextra:
UpgradeTypes: tower.sam RequiresCondition: tower.sam
UpgradeMinEnabledLevel: 1
Amount: -10 Amount: -10
Pluggable: Pluggable:
Upgrades: Upgrades:
tower.vulcan: tower, tower.vulcan tower.vulcan: tower.vulcan
tower.rocket: tower, tower.rocket tower.rocket: tower.rocket
tower.sam: tower, tower.sam tower.sam: tower.sam
ProvidesPrerequisite@buildingname: ProvidesPrerequisite@buildingname:
SelectionDecorations: SelectionDecorations:
VisualBounds: 48, 48, 0, -12 VisualBounds: 48, 48, 0, -12

View File

@@ -34,12 +34,10 @@ APC:
Upgrades: inwater Upgrades: inwater
TerrainTypes: Water TerrainTypes: Water
WithVoxelBody: WithVoxelBody:
UpgradeTypes: inwater RequiresCondition: !inwater
UpgradeMaxEnabledLevel: 0
WithVoxelBody@water: WithVoxelBody@water:
Sequence: water Sequence: water
UpgradeTypes: inwater RequiresCondition: inwater
UpgradeMinEnabledLevel: 1
LeavesTrails: LeavesTrails:
Image: wake Image: wake
Palette: effect Palette: effect

View File

@@ -77,21 +77,21 @@ CRATE:
GrantUpgradeCrateAction@cloak: GrantUpgradeCrateAction@cloak:
SelectionShares: 5 SelectionShares: 5
Effect: stealth Effect: stealth
Upgrades: cloakgenerator Upgrades: crate-cloak
Notification: cloak5.aud Notification: cloak5.aud
GrantUpgradeCrateAction@firepower: GrantUpgradeCrateAction@firepower:
SelectionShares: 5 SelectionShares: 5
Effect: firepower Effect: firepower
Upgrades: firepower Upgrades: crate-firepower
Notification: 00-i070.aud Notification: 00-i070.aud
GrantUpgradeCrateAction@armor: GrantUpgradeCrateAction@armor:
SelectionShares: 5 SelectionShares: 5
Effect: armor Effect: armor
Upgrades: damage Upgrades: crate-damage
Notification: 00-i068.aud Notification: 00-i068.aud
GrantUpgradeCrateAction@speed: GrantUpgradeCrateAction@speed:
SelectionShares: 5 SelectionShares: 5
Upgrades: speed Upgrades: crate-speed
Notification: 00-i080.aud Notification: 00-i080.aud
SROCK01: SROCK01:

View File

@@ -128,8 +128,7 @@ NAHAND:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
NAWEAP: NAWEAP:
Inherits: ^Building Inherits: ^Building
@@ -187,8 +186,7 @@ NAWEAP:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
NAHPAD: NAHPAD:
Inherits: ^Building Inherits: ^Building
@@ -242,8 +240,7 @@ NAHPAD:
ReferencePoint: Top ReferencePoint: Top
Color: E0D048 Color: E0D048
ZOffset: 256 ZOffset: 256
UpgradeTypes: primary RequiresCondition: primary
UpgradeMinEnabledLevel: 1
NARADR: NARADR:
Inherits: ^Building Inherits: ^Building

View File

@@ -55,14 +55,11 @@ BIKE:
MaxHeightDelta: 3 MaxHeightDelta: 3
Armament@PRIMARY: Armament@PRIMARY:
Weapon: BikeMissile Weapon: BikeMissile
UpgradeTypes: eliteweapon RequiresCondition: !rank-elite
UpgradeMaxEnabledLevel: 0
UpgradeMaxAcceptedLevel: 1
LocalOffset: -108,-144,360, -108,144,360 LocalOffset: -108,-144,360, -108,144,360
Armament@ELITE: Armament@ELITE:
Weapon: HoverMissile Weapon: HoverMissile
UpgradeTypes: eliteweapon RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1
LocalOffset: -108,-144,360, -108,144,360 LocalOffset: -108,-144,360, -108,144,360
AttackFrontal: AttackFrontal:
Voice: Attack Voice: Attack
@@ -74,12 +71,10 @@ TTNK:
Cost: 800 Cost: 800
Tooltip: Tooltip:
Name: Tick Tank Name: Tick Tank
UpgradeTypes: deployed RequiresCondition: !deployed
UpgradeMaxEnabledLevel: 0
Tooltip@DEPLOYED: Tooltip@DEPLOYED:
Name: Tick Tank (deployed) Name: Tick Tank (deployed)
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
Buildable: Buildable:
Queue: Vehicle Queue: Vehicle
BuildPaletteOrder: 60 BuildPaletteOrder: 60
@@ -93,20 +88,16 @@ TTNK:
HP: 350 HP: 350
Armor: Armor:
Type: Light Type: Light
UpgradeTypes: undeployed RequiresCondition: undeployed
UpgradeMinEnabledLevel: 1
Armament@PRIMARY: Armament@PRIMARY:
Weapon: 90mm Weapon: 90mm
LocalOffset: 288,0,256 LocalOffset: 288,0,256
UpgradeTypes: eliteweapon RequiresCondition: !rank-elite
UpgradeMaxEnabledLevel: 0
UpgradeMaxAcceptedLevel: 1
MuzzleSequence: muzzle MuzzleSequence: muzzle
Armament@ELITE: Armament@ELITE:
Weapon: 120mmx Weapon: 120mmx
LocalOffset: 288,0,256 LocalOffset: 288,0,256
UpgradeTypes: eliteweapon RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1
MuzzleSequence: muzzle MuzzleSequence: muzzle
WithMuzzleOverlay: WithMuzzleOverlay:
RevealsShroud: RevealsShroud:
@@ -123,16 +114,12 @@ TTNK:
DeploySound: place2.aud DeploySound: place2.aud
UndeploySound: clicky1.aud UndeploySound: clicky1.aud
WithVoxelBody: WithVoxelBody:
UpgradeTypes: undeployed RequiresCondition: undeployed
UpgradeMinEnabledLevel: 1
WithSpriteBody@deployed: WithSpriteBody@deployed:
UpgradeTypes: undeployed RequiresCondition: !undeployed
UpgradeMaxEnabledLevel: 0
AttackFrontal: AttackFrontal:
Voice: Attack Voice: Attack
UpgradeTypes: undeployed RequiresCondition: undeployed
UpgradeMinEnabledLevel: 1
UpgradeMaxAcceptedLevel: 1
Turreted: Turreted:
TurnSpeed: 6 TurnSpeed: 6
Turret: deployed Turret: deployed
@@ -141,38 +128,31 @@ TTNK:
WithVoxelBarrel: WithVoxelBarrel:
Armament: deployed Armament: deployed
LocalOffset: 128, 0, 256 LocalOffset: 128, 0, 256
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
WithVoxelTurret@deployed: WithVoxelTurret@deployed:
Turret: deployed Turret: deployed
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
AttackTurreted@deployed: AttackTurreted@deployed:
Voice: Attack Voice: Attack
Armaments: deployed Armaments: deployed
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
Armament@deployed: Armament@deployed:
Name: deployed Name: deployed
Turret: deployed Turret: deployed
Weapon: 90mm Weapon: 90mm
LocalOffset: 384,0,256 LocalOffset: 384,0,256
UpgradeTypes: eliteweapon RequiresCondition: !rank-elite
UpgradeMaxEnabledLevel: 0
UpgradeMaxAcceptedLevel: 1
MuzzleSequence: muzzle MuzzleSequence: muzzle
Armament@deployedElite: Armament@deployedElite:
Name: deployed Name: deployed
Turret: deployed Turret: deployed
Weapon: 120mmx Weapon: 120mmx
LocalOffset: 384,0,256 LocalOffset: 384,0,256
UpgradeTypes: eliteweapon RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1
MuzzleSequence: muzzle MuzzleSequence: muzzle
Armor@deployed: Armor@deployed:
Type: Concrete Type: Concrete
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
AutoTarget: AutoTarget:
ART2: ART2:

View File

@@ -15,13 +15,10 @@ E1:
Speed: 71 Speed: 71
Armament@PRIMARY: Armament@PRIMARY:
Weapon: Minigun Weapon: Minigun
UpgradeTypes: eliteweapon RequiresCondition: !rank-elite
UpgradeMaxEnabledLevel: 0
UpgradeMaxAcceptedLevel: 1
Armament@ELITE: Armament@ELITE:
Weapon: M1Carbine Weapon: M1Carbine
UpgradeTypes: eliteweapon RequiresCondition: rank-elite
UpgradeMinEnabledLevel: 1
AttackFrontal: AttackFrontal:
Voice: Attack Voice: Attack
WithInfantryBody: WithInfantryBody:

View File

@@ -116,12 +116,10 @@ LPST:
Cost: 950 Cost: 950
Tooltip: Tooltip:
Name: Mobile Sensor Array Name: Mobile Sensor Array
UpgradeTypes: deployed RequiresCondition: !deployed
UpgradeMaxEnabledLevel: 0
Tooltip@DEPLOYED: Tooltip@DEPLOYED:
Name: Mobile Sensor Array (deployed) Name: Mobile Sensor Array (deployed)
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
Health: Health:
HP: 600 HP: 600
Armor: Armor:
@@ -147,14 +145,11 @@ LPST:
UndeploySound: clicky1.aud UndeploySound: clicky1.aud
WithVoxelBody: WithVoxelBody:
Image: lpst Image: lpst
UpgradeTypes: undeployed RequiresCondition: undeployed
UpgradeMinEnabledLevel: 1
WithSpriteBody@deployed: WithSpriteBody@deployed:
UpgradeTypes: undeployed RequiresCondition: !undeployed
UpgradeMaxEnabledLevel: 0
DetectCloaked: DetectCloaked:
UpgradeTypes: deployed RequiresCondition: deployed
UpgradeMinEnabledLevel: 1
Range: 18c0 Range: 18c0
RenderDetectionCircle: RenderDetectionCircle:
TrailCount: 3 TrailCount: 3

View File

@@ -62,9 +62,10 @@ crate-effects:
levelup: veteran levelup: veteran
rank: rank:
rank: pips veteran: pips
Start: 7 Start: 7
Length: 2 elite: pips
Start: 8
mpspawn: mpspawn:
idle: idle: