Files
OpenRA/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
atlimit8 bbd54cb32f Added IDisabledTrait & rewrote upgrade code using a level-based approach.
Upgradeable traits are notified whenever an upgrade of their declared types are granted or revoked.  The traits maintain their own internal level counter, which is then used to enable or disable the trait functionality.  A trait can register for multiple upgrade types which then all affect the internal level counter.

	IDisabledTrait for identifying (and filtering) disabled traits
	UpgradableTrait provides an abstract base for traits to support upgrade levels
	Added IDisabledTrait support to GlobalButtonOrderGenerator

	Includes rework by pchote with alterations.
2014-11-26 05:45:26 -06:00

1039 lines
35 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.Mods.Common.UtilityCommands
{
static class UpgradeRules
{
internal static void ConvertFloatToRange(ref string input)
{
var value = float.Parse(input);
var cells = (int)value;
var subcells = (int)(1024 * value) - 1024 * cells;
input = "{0}c{1}".F(cells, subcells);
}
internal static void ConvertFloatArrayToPercentArray(ref string input)
{
input = string.Join(", ", input.Split(',')
.Select(s => ((int)Math.Round(FieldLoader.GetValue<float>("(float value)", s) * 100)).ToString()));
}
internal static void ConvertPxToRange(ref string input)
{
ConvertPxToRange(ref input, 1, 1);
}
internal static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv)
{
var value = Exts.ParseIntegerInvariant(input);
var ts = Game.modData.Manifest.TileSize;
var world = value * 1024 * scaleMult / (scaleDiv * ts.Height);
var cells = world / 1024;
var subcells = world - 1024 * cells;
input = cells != 0 ? "{0}c{1}".F(cells, subcells) : subcells.ToString();
}
internal static void ConvertAngle(ref string input)
{
var value = float.Parse(input);
input = WAngle.ArcTan((int)(value * 4 * 1024), 1024).ToString();
}
internal static void ConvertInt2ToWVec(ref string input)
{
var offset = FieldLoader.GetValue<int2>("(value)", input);
var ts = Game.modData.Manifest.TileSize;
var world = new WVec(offset.X * 1024 / ts.Width, offset.Y * 1024 / ts.Height, 0);
input = world.ToString();
}
internal static void UpgradeActorRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
{
var parentKey = parent != null ? parent.Key.Split('@').First() : null;
foreach (var node in nodes)
{
// Weapon definitions were converted to world coordinates
if (engineVersion < 20131226)
{
if (depth == 2 && parentKey == "Exit" && node.Key == "SpawnOffset")
ConvertInt2ToWVec(ref node.Value.Value);
if (depth == 2 && (parentKey == "Aircraft" || parentKey == "Helicopter" || parentKey == "Plane"))
{
if (node.Key == "CruiseAltitude")
ConvertPxToRange(ref node.Value.Value);
if (node.Key == "Speed")
ConvertPxToRange(ref node.Value.Value, 7, 32);
}
if (depth == 2 && parentKey == "Mobile" && node.Key == "Speed")
ConvertPxToRange(ref node.Value.Value, 1, 3);
if (depth == 2 && parentKey == "Health" && node.Key == "Radius")
ConvertPxToRange(ref node.Value.Value);
}
// CrateDrop was replaced with CrateSpawner
if (engineVersion < 20131231)
{
if (depth == 1 && parentKey == "World")
{
if (node.Key == "CrateDrop")
node.Key = "CrateSpawner";
if (node.Key == "-CrateDrop")
node.Key = "-CrateSpawner";
}
}
// AttackTesla was replaced with AttackCharge
if (engineVersion < 20140307)
{
if (depth == 1)
{
if (node.Key == "AttackTesla")
node.Key = "AttackCharge";
if (node.Key == "-AttackTesla")
node.Key = "-AttackCharge";
}
}
// AttackMove was generalized to support all moveable actor types
if (engineVersion < 20140116)
{
if (depth == 1 && node.Key == "AttackMove")
node.Value.Nodes.RemoveAll(n => n.Key == "JustMove");
}
// UnloadFacing was removed from Cargo
if (engineVersion < 20140212)
{
if (depth == 1 && node.Key == "Cargo")
node.Value.Nodes.RemoveAll(n => n.Key == "UnloadFacing");
}
// RevealShroud was updated to use world units.
if (engineVersion < 20140220)
{
if (depth == 2 && parentKey == "RevealsShroud" && node.Key == "Range")
ConvertFloatToRange(ref node.Value.Value);
if (depth == 2 && parentKey == "CreatesShroud" && node.Key == "Range")
ConvertFloatToRange(ref node.Value.Value);
}
// Waypoint was renamed to Immobile
if (engineVersion < 20140312)
{
if (depth == 1 && node.Key == "Waypoint")
node.Key = "Immobile";
}
// Spy was renamed to Disguise
if (engineVersion < 20140314)
{
if (depth == 1 && node.Key == "Spy")
node.Key = "Disguise";
if (depth == 1 && node.Key == "SpyToolTip")
node.Key = "DisguiseToolTip";
if (depth == 1 && node.Key == "RenderSpy")
node.Key = "RenderDisguise";
}
// IOccupySpace was removed from Mine
if (engineVersion < 20140320)
{
if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "Mine"))
node.Value.Nodes.Add(new MiniYamlNode("Immobile", new MiniYaml("", new List<MiniYamlNode>() { new MiniYamlNode("OccupiesSpace", "true") })));
else
foreach (var i in nodes.Where(n => n.Key == "Immobile"))
if (!i.Value.Nodes.Any(n => n.Key == "OccupiesSpace"))
i.Value.Nodes.Add(new MiniYamlNode("OccupiesSpace", "false"));
}
// Armaments and muzzleflashes were reworked to support garrisoning
if (engineVersion < 20140321)
{
if (depth == 0)
{
var muzzles = node.Value.Nodes.Where(n => n.Key.StartsWith("WithMuzzleFlash"));
var armaments = node.Value.Nodes.Where(n => n.Key.StartsWith("Armament"));
// Shift muzzle flash definitions to Armament
foreach (var m in muzzles)
{
var muzzleArmNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Armament");
var muzzleSequenceNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Sequence");
var muzzleSplitFacingsNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "SplitFacings");
var muzzleFacingsCountNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "FacingCount");
var muzzleArmName = muzzleArmNode != null ? muzzleArmNode.Value.Value.Trim() : "primary";
var muzzleSequence = muzzleSequenceNode != null ? muzzleSequenceNode.Value.Value.Trim() : "muzzle";
var muzzleSplitFacings = muzzleSplitFacingsNode != null ? FieldLoader.GetValue<bool>("SplitFacings", muzzleSplitFacingsNode.Value.Value) : false;
var muzzleFacingsCount = muzzleFacingsCountNode != null ? FieldLoader.GetValue<int>("FacingsCount", muzzleFacingsCountNode.Value.Value) : 8;
foreach (var a in armaments)
{
var armNameNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Name");
var armName = armNameNode != null ? armNameNode.Value.Value.Trim() : "primary";
if (muzzleArmName == armName)
{
a.Value.Nodes.Add(new MiniYamlNode("MuzzleSequence", muzzleSequence));
if (muzzleSplitFacings)
a.Value.Nodes.Add(new MiniYamlNode("MuzzleSplitFacings", muzzleFacingsCount.ToString()));
}
}
}
foreach (var m in muzzles.ToList().Skip(1))
node.Value.Nodes.Remove(m);
}
// Remove all but the first muzzle flash definition
if (depth == 1 && node.Key.StartsWith("WithMuzzleFlash"))
{
node.Key = "WithMuzzleFlash";
node.Value.Nodes.RemoveAll(n => n.Key == "Armament");
node.Value.Nodes.RemoveAll(n => n.Key == "Sequence");
}
}
// "disabled" palette overlay has been moved into it's own DisabledOverlay trait
if (engineVersion < 20140305)
{
if (node.Value.Nodes.Any(n => n.Key.StartsWith("RequiresPower"))
&& !node.Value.Nodes.Any(n => n.Key.StartsWith("DisabledOverlay")))
{
node.Value.Nodes.Add(new MiniYamlNode("DisabledOverlay", new MiniYaml("")));
}
}
// ChronoshiftDeploy was replaced with PortableChrono
if (engineVersion < 20140321)
{
if (depth == 1 && node.Key == "ChronoshiftDeploy")
node.Key = "PortableChrono";
if (depth == 2 && parentKey == "PortableChrono" && node.Key == "JumpDistance")
node.Key = "MaxDistance";
}
// Added new Lua API
if (engineVersion < 20140421)
{
if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "LuaScriptEvents"))
node.Value.Nodes.Add(new MiniYamlNode("ScriptTriggers", ""));
}
if (engineVersion < 20140517)
{
if (depth == 0)
node.Value.Nodes.RemoveAll(n => n.Key == "TeslaInstantKills");
}
if (engineVersion < 20140615)
{
if (depth == 1 && node.Key == "StoresOre")
node.Key = "StoresResources";
}
// make animation is now its own trait
if (engineVersion < 20140621)
{
if (depth == 1 && (node.Key.StartsWith("RenderBuilding")))
node.Value.Nodes.RemoveAll(n => n.Key == "HasMakeAnimation");
if (node.Value.Nodes.Any(n => n.Key.StartsWith("RenderBuilding"))
&& !node.Value.Nodes.Any(n => n.Key == "RenderBuildingWall")
&& !node.Value.Nodes.Any(n => n.Key == "WithMakeAnimation"))
{
node.Value.Nodes.Add(new MiniYamlNode("WithMakeAnimation", new MiniYaml("")));
}
}
// ParachuteAttachment was merged into Parachutable
if (engineVersion < 20140701)
{
if (depth == 1 && node.Key == "ParachuteAttachment")
{
node.Key = "Parachutable";
foreach (var subnode in node.Value.Nodes)
if (subnode.Key == "Offset")
subnode.Key = "ParachuteOffset";
}
if (depth == 2 && node.Key == "ParachuteSprite")
node.Key = "ParachuteSequence";
}
// SonarPulsePower was implemented as a generic SpawnActorPower
if (engineVersion < 20140703)
{
if (depth == 1 && node.Key == "SonarPulsePower")
node.Key = "SpawnActorPower";
}
if (engineVersion < 20140707)
{
// SpyPlanePower was removed (use AirstrikePower instead)
if (depth == 1 && node.Key == "SpyPlanePower")
{
node.Key = "AirstrikePower";
var revealTime = 6 * 25;
var revealNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "RevealTime");
if (revealNode != null)
{
revealTime = int.Parse(revealNode.Value.Value) * 25;
node.Value.Nodes.Remove(revealNode);
}
node.Value.Nodes.Add(new MiniYamlNode("CameraActor", new MiniYaml("camera")));
node.Value.Nodes.Add(new MiniYamlNode("CameraRemoveDelay", new MiniYaml(revealTime.ToString())));
node.Value.Nodes.Add(new MiniYamlNode("UnitType", new MiniYaml("u2")));
}
if (depth == 2 && node.Key == "LZRange" && parentKey == "ParaDrop")
{
node.Key = "DropRange";
ConvertFloatToRange(ref node.Value.Value);
}
}
// GiveUnitCrateAction and GiveMcvCrateAction were updated to allow multiple units
if (engineVersion < 20140723)
{
if (depth == 2 && parentKey.Contains("GiveMcvCrateAction"))
if (node.Key == "Unit")
node.Key = "Units";
if (depth == 2 && parentKey.Contains("GiveUnitCrateAction"))
if (node.Key == "Unit")
node.Key = "Units";
}
// Power from Building was moved out into Power and ScalePowerWithHealth traits
if (engineVersion < 20140823)
{
if (depth == 0)
{
var actorTraits = node.Value.Nodes;
var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
if (building != null)
{
var buildingFields = building.Value.Nodes;
var power = buildingFields.FirstOrDefault(n => n.Key == "Power");
if (power != null)
{
buildingFields.Remove(power);
var powerFields = new List<MiniYamlNode> { new MiniYamlNode("Amount", power.Value) };
actorTraits.Add(new MiniYamlNode("Power", new MiniYaml("", powerFields)));
if (FieldLoader.GetValue<int>("Power", power.Value.Value) > 0)
actorTraits.Add(new MiniYamlNode("ScaleWithHealth", ""));
}
}
}
}
if (engineVersion < 20140803)
{
// ContainsCrate was removed (use LeavesHusk instead)
if (depth == 1 && node.Key == "ContainsCrate")
{
node.Key = "LeavesHusk";
node.Value.Nodes.Add(new MiniYamlNode("HuskActor", new MiniYaml("crate")));
}
}
if (engineVersion < 20140806)
{
// remove ConquestVictoryConditions when StrategicVictoryConditions is set
if (depth == 0 && node.Key == "Player" && node.Value.Nodes.Any(n => n.Key == "StrategicVictoryConditions"))
node.Value.Nodes.Add(new MiniYamlNode("-ConquestVictoryConditions", ""));
// the objectives panel trait and its properties have been renamed
if (depth == 1 && node.Key == "ConquestObjectivesPanel")
{
node.Key = "ObjectivesPanel";
node.Value.Nodes.RemoveAll(_ => true);
node.Value.Nodes.Add(new MiniYamlNode("PanelName", new MiniYaml("SKIRMISH_STATS")));
}
}
// Veterancy was changed to use the upgrades system
if (engineVersion < 20140807)
{
if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsExperience")))
node.Value.Nodes.Add(new MiniYamlNode("GainsStatUpgrades", new MiniYaml("")));
if (depth == 1 && node.Key == "-CloakCrateAction")
node.Key = "-UnitUpgradeCrateAction@cloak";
if (depth == 1 && node.Key == "CloakCrateAction")
{
node.Key = "UnitUpgradeCrateAction@cloak";
node.Value.Nodes.Add(new MiniYamlNode("Upgrades", new MiniYaml("cloak")));
}
if (depth == 2 && node.Key == "RequiresCrate" && parentKey == "Cloak")
{
node.Key = "RequiresUpgrade";
node.Value.Value = "cloak";
}
}
// Modifiers were changed to integer percentages
if (engineVersion < 20140812)
{
if (depth == 2 && node.Key == "ClosedDamageMultiplier" && parentKey == "AttackPopupTurreted")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
if (depth == 2 && node.Key == "FullyLoadedSpeed" && parentKey == "Harvester")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
if (depth == 2 && node.Key == "PanicSpeedModifier" && parentKey == "ScaredyCat")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
if (depth == 2 && node.Key == "ProneSpeed" && parentKey == "TakeCover")
{
node.Key = "SpeedModifier";
ConvertFloatArrayToPercentArray(ref node.Value.Value);
}
if (depth == 2 && node.Key == "SpeedModifier" && parentKey == "GainsStatUpgrades")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
if (depth == 2 && node.Key == "FirepowerModifier" && parentKey == "GainsStatUpgrades")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
}
// RemoveImmediately was replaced with RemoveOnConditions
if (engineVersion < 20140821)
{
if (depth == 1)
{
if (node.Key == "RemoveImmediately")
node.Key = "RemoveOnConditions";
if (node.Key == "-RemoveImmediately")
node.Key = "-RemoveOnConditions";
}
}
if (engineVersion < 20140823)
{
if (depth == 2 && node.Key == "ArmorUpgrade" && parentKey == "GainsStatUpgrades")
node.Key = "DamageUpgrade";
if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
{
node.Key = "DamageModifier";
node.Value.Value = string.Join(", ", node.Value.Value.Split(',')
.Select(s => ((int)(100 * 100 / float.Parse(s))).ToString()));
}
if (depth == 3 && parentKey == "Upgrades")
node.Value.Value = node.Value.Value.Replace("armor", "damage");
}
// RenderInfantryProne and RenderInfantryPanic was merged into RenderInfantry
if (engineVersion < 20140824)
{
var renderInfantryRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderInfantry");
if (depth == 0 && renderInfantryRemoval != null && !node.Value.Nodes.Any(n => n.Key == "RenderDisguise"))
node.Value.Nodes.Remove(renderInfantryRemoval);
if (depth == 1 && (node.Key == "RenderInfantryProne" || node.Key == "RenderInfantryPanic"))
node.Key = "RenderInfantry";
}
// InfDeath was renamed to DeathType
if (engineVersion < 20140830)
{
if (depth == 2 && parentKey.StartsWith("DeathSounds") && node.Key == "InfDeaths")
node.Key = "DeathTypes";
if (depth == 2 && parentKey == "SpawnsViceroid" && node.Key == "InfDeath")
node.Key = "DeathType";
if (depth == 2 && parentKey == "Explodes" && node.Key == "InfDeath")
node.Key = "DeathType";
}
// SellSounds from Building was moved into Sellable
if (engineVersion < 20140904)
{
if (depth == 0)
{
var actorTraits = node.Value.Nodes;
var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
if (building != null)
{
var buildingFields = building.Value.Nodes;
var sellSounds = buildingFields.FirstOrDefault(n => n.Key == "SellSounds");
if (sellSounds != null)
{
buildingFields.Remove(sellSounds);
var sellable = actorTraits.FirstOrDefault(t => t.Key == "Sellable");
if (sellable != null)
sellable.Value.Nodes.Add(sellSounds);
else
{
Console.WriteLine("Warning: Adding Sellable trait to {0} in {1}".F(node.Key, node.Location.Filename));
actorTraits.Add(new MiniYamlNode("Sellable", new MiniYaml("", new List<MiniYamlNode> { sellSounds })));
}
}
}
}
}
// DuplicateUnitCrateAction was tidied up
if (engineVersion < 20140912)
{
if (depth == 2 && node.Key == "MaxDuplicatesWorth" && parentKey == "DuplicateUnitCrateAction")
node.Key = "MaxDuplicateValue";
if (depth == 2 && node.Key == "ValidDuplicateTypes" && parentKey == "DuplicateUnitCrateAction")
node.Key = "ValidTargets";
}
// Added WithDeathAnimation
if (engineVersion < 20140913)
{
var spawnsCorpseRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "SpawnsCorpse");
if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("RenderInfantry")) && spawnsCorpseRemoval == null)
node.Value.Nodes.Add(new MiniYamlNode("WithDeathAnimation", new MiniYaml("")));
if (depth == 2 && node.Key == "SpawnsCorpse" && parentKey == "RenderInfantry")
node.Value.Nodes.Remove(spawnsCorpseRemoval);
// CrushableInfantry renamed to Crushable
if (depth == 1)
{
if (node.Key == "CrushableInfantry")
node.Key = "Crushable";
if (node.Key == "-CrushableInfantry")
node.Key = "-Crushable";
}
}
// Replaced Wall with Crushable + BlocksBullets
if (engineVersion < 20140914)
{
if (depth == 0)
{
var actorTraits = node.Value.Nodes;
var wall = actorTraits.FirstOrDefault(t => t.Key == "Wall");
if (wall != null)
node.Value.Nodes.Add(new MiniYamlNode("BlocksBullets", new MiniYaml("")));
var blocksBullets = actorTraits.FirstOrDefault(t => t.Key == "BlocksBullets");
if (depth == 1 && node.Key == "Wall" && blocksBullets != null)
node.Key = "Crushable";
}
}
if (engineVersion < 20140927)
{
if (depth == 0)
node.Value.Nodes.RemoveAll(n => n.Key == "SelfHealingTech");
if (depth == 2 && node.Key == "RequiresTech" && parentKey.StartsWith("SelfHealing"))
{
node.Key = "RequiresUpgrade";
node.Value.Value = "selfhealing-needs-reconfiguration";
}
}
if (engineVersion < 20141001)
{
// Routed unit upgrades via the UnitUpgradeManager trait
if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsStatUpgrades")))
node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));
// Replaced IronCurtainPower -> GrantUpgradePower
if (depth == 1 && node.Key == "IronCurtainPower")
{
node.Key = "GrantUpgradePower@IRONCURTAIN";
node.Value.Nodes.Add(new MiniYamlNode("Upgrades", "invulnerability"));
var durationNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Duration");
if (durationNode != null)
durationNode.Value.Value = (int.Parse(durationNode.Value.Value) * 25).ToString();
else
node.Value.Nodes.Add(new MiniYamlNode("Duration", "600"));
var soundNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "IronCurtainSound");
if (soundNode != null)
soundNode.Key = "GrantUpgradeSound";
}
if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("IronCurtainable")))
{
node.Value.Nodes.RemoveAll(n => n.Key.StartsWith("IronCurtainable"));
var overlayKeys = new List<MiniYamlNode>();
overlayKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
node.Value.Nodes.Add(new MiniYamlNode("UpgradeOverlay@IRONCURTAIN", new MiniYaml("", overlayKeys)));
var invulnKeys = new List<MiniYamlNode>();
invulnKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
node.Value.Nodes.Add(new MiniYamlNode("InvulnerabilityUpgrade@IRONCURTAIN", new MiniYaml("", invulnKeys)));
var barKeys = new List<MiniYamlNode>();
barKeys.Add(new MiniYamlNode("Upgrade", "invulnerability"));
node.Value.Nodes.Add(new MiniYamlNode("TimedUpgradeBar", new MiniYaml("", barKeys)));
if (!node.Value.Nodes.Any(n => n.Key.StartsWith("UnitUpgradeManager")))
node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));
}
if (depth == 1 && node.Key == "-IronCurtainable")
node.Key = "-InvulnerabilityUpgrade@IRONCURTAIN";
// Replaced RemoveOnConditions with KillsSelf
if (depth == 1 && node.Key == "RemoveOnConditions")
{
node.Key = "KillsSelf";
node.Value.Nodes.Add(new MiniYamlNode("RemoveInstead", new MiniYaml("true")));
}
if (depth == 1 && node.Key.StartsWith("UnitUpgradeCrateAction"))
{
var parts = node.Key.Split('@');
node.Key = "GrantUpgradeCrateAction";
if (parts.Length > 1)
node.Key += "@" + parts[1];
}
if (depth == 1 && node.Key.StartsWith("-UnitUpgradeCrateAction"))
{
var parts = node.Key.Split('@');
node.Key = "-GrantUpgradeCrateAction";
if (parts.Length > 1)
node.Key += "@" + parts[1];
}
}
if (engineVersion < 20141002)
{
if (node.Key == "AlignWhenIdle" && parentKey == "Turreted")
{
node.Key = "RealignDelay";
node.Value.Value = "0";
}
}
// Replaced BelowUnits with per sequence ZOffsets
if (engineVersion < 20141030)
{
if (depth == 0)
{
node.Value.Nodes.RemoveAll(n => n.Key == "BelowUnits");
node.Value.Nodes.RemoveAll(n => n.Key == "-BelowUnits");
}
}
if (engineVersion < 20141121)
{
if (depth == 1)
{
if (node.Value.Nodes.Exists(n => n.Key == "RestrictedByUpgrade"))
{
node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxEnabledLevel", "0"));
node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxAcceptedLevel", "1"));
}
else if (node.Value.Nodes.Exists(n => n.Key == "RequiresUpgrade"))
node.Value.Nodes.Add(new MiniYamlNode("UpgradeMinEnabledLevel", "1"));
if (node.Key.StartsWith("DisableUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes"))
node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "disable"));
if (node.Key.StartsWith("InvulnerabilityUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes"))
node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "invulnerability"));
}
else if (depth == 2)
{
if (node.Key == "RequiresUpgrade" || node.Key == "RestrictedByUpgrade")
node.Key = "UpgradeTypes";
else if (node.Key == "-RequiresUpgrade" || node.Key == "-RestrictedByUpgrade")
node.Key = "-UpgradeTypes";
}
}
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}
internal static void UpgradeWeaponRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
{
var parentKey = parent != null ? parent.Key.Split('@').First() : null;
foreach (var node in nodes)
{
// Weapon definitions were converted to world coordinates
if (engineVersion < 20131226)
{
if (depth == 1)
{
switch (node.Key)
{
case "Range":
case "MinRange":
ConvertFloatToRange(ref node.Value.Value);
break;
default:
break;
}
}
if (depth == 2 && parentKey == "Projectile")
{
switch (node.Key)
{
case "Inaccuracy":
ConvertPxToRange(ref node.Value.Value);
break;
case "Angle":
ConvertAngle(ref node.Value.Value);
break;
case "Speed":
{
if (parent.Value.Value == "Missile")
ConvertPxToRange(ref node.Value.Value, 1, 5);
if (parent.Value.Value == "Bullet")
ConvertPxToRange(ref node.Value.Value, 2, 5);
break;
}
default:
break;
}
}
if (depth == 2 && parentKey == "Warhead")
{
switch (node.Key)
{
case "Spread":
ConvertPxToRange(ref node.Value.Value);
break;
default:
break;
}
}
}
if (engineVersion < 20140615)
{
if (depth == 2 && parentKey == "Warhead" && node.Key == "Ore")
node.Key = "DestroyResources";
}
if (engineVersion < 20140720)
{
// Split out the warheads to individual warhead types.
if (depth == 0)
{
var validTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "ValidTargets"); // Weapon's ValidTargets need to be copied to the warheads, so find it
var invalidTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "InvalidTargets"); // Weapon's InvalidTargets need to be copied to the warheads, so find it
var warheadCounter = 0;
foreach(var curNode in node.Value.Nodes.ToArray())
{
if (curNode.Key.Contains("Warhead") && curNode.Value.Value == null)
{
var newNodes = new List<MiniYamlNode>();
var oldNodeAtName = "";
if (curNode.Key.Contains('@'))
oldNodeAtName = "_" + curNode.Key.Split('@')[1];
// Per Cell Damage Model
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("PerCell")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New PerCell warhead allows 2 sizes, as opposed to 1 size
if (temp != null)
{
var newValue = temp.Value.Value.Split(',').First();
newYaml.Add(new MiniYamlNode("Size", newValue));
}
var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };
foreach(var keyword in keywords)
{
var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp2 != null)
newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
}
if (validTargets != null)
newYaml.Add(validTargets);
if (invalidTargets != null)
newYaml.Add(invalidTargets);
var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
if (tempVersus != null)
newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "PerCellDamage", newYaml));
}
// HealthPercentage damage model
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("HealthPercentage")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New HealthPercentage warhead allows 2 spreads, as opposed to 1 size
if (temp != null)
{
var newValue = temp.Value.Value.Split(',').First() + "c0";
newYaml.Add(new MiniYamlNode("Spread", newValue));
}
var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };
foreach(var keyword in keywords)
{
var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp2 != null)
newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
}
if (validTargets != null)
newYaml.Add(validTargets);
if (invalidTargets != null)
newYaml.Add(invalidTargets);
var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
if (tempVersus != null)
newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "HealthPercentageDamage", newYaml));
}
// SpreadDamage
{ // Always occurs, since by definition all warheads were SpreadDamage warheads before
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "Spread", "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
}
if (validTargets != null)
newYaml.Add(validTargets);
if (invalidTargets != null)
newYaml.Add(invalidTargets);
var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
if (tempVersus != null)
newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "SpreadDamage", newYaml));
}
// DestroyResource
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DestroyResources") ||
n.Key.Contains("Ore")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "Size", "Delay", "ValidTargets", "InvalidTargets" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
}
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "DestroyResource", newYaml));
}
// CreateResource
if (curNode.Value.Nodes.Where(n => n.Key.Contains("AddsResourceType")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "AddsResourceType", "Size", "Delay", "ValidTargets", "InvalidTargets" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
}
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "CreateResource", newYaml));
}
// LeaveSmudge
if (curNode.Value.Nodes.Where(n => n.Key.Contains("SmudgeType")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "SmudgeType", "Size", "Delay", "ValidTargets", "InvalidTargets" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
}
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Smu" + oldNodeAtName, "LeaveSmudge", newYaml));
}
// CreateEffect - Explosion
if (curNode.Value.Nodes.Where(n => n.Key.Contains("Explosion") ||
n.Key.Contains("ImpactSound")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "Explosion", "ImpactSound", "Delay", "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
}
newYaml.Add(new MiniYamlNode("InvalidImpactTypes", "Water"));
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
}
// CreateEffect - Water Explosion
if (curNode.Value.Nodes.Where(n => n.Key.Contains("WaterExplosion") ||
n.Key.Contains("WaterImpactSound")).Any())
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var keywords = new List<string> { "WaterExplosion", "WaterImpactSound", "Delay", "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };
foreach(var keyword in keywords)
{
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
if (temp != null)
{
if (temp.Key == "WaterExplosion")
temp.Key = "Explosion";
if (temp.Key == "WaterImpactSound")
temp.Key = "ImpactSound";
newYaml.Add(new MiniYamlNode(temp.Key, temp.Value.Value));
}
}
newYaml.Add(new MiniYamlNode("ValidImpactTypes", "Water"));
newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
}
node.Value.Nodes.InsertRange(node.Value.Nodes.IndexOf(curNode),newNodes);
node.Value.Nodes.Remove(curNode);
}
}
}
}
if (engineVersion < 20140818)
{
if (depth == 1)
{
if (node.Key == "ROF")
node.Key = "ReloadDelay";
}
}
if (engineVersion < 20140821)
{
// Converted versus definitions to integers
if (depth == 3 && parentKey == "Versus")
ConvertFloatArrayToPercentArray(ref node.Value.Value);
}
if (engineVersion < 20140830)
{
if (depth == 2)
{
if (node.Key == "InfDeath")
node.Key = "DeathType";
}
}
UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}
internal static void UpgradeTileset(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
{
var parentKey = parent != null ? parent.Key.Split('@').First() : null;
var addNodes = new List<MiniYamlNode>();
foreach (var node in nodes)
{
if (engineVersion < 20140104)
{
if (depth == 2 && parentKey == "TerrainType" && node.Key.Split('@').First() == "Type")
addNodes.Add(new MiniYamlNode("TargetTypes", node.Value.Value == "Water" ? "Water" : "Ground"));
}
UpgradeTileset(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
nodes.AddRange(addNodes);
}
}
}