diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 4e4d071701..7c5c55b300 100755 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -146,7 +146,7 @@ namespace OpenRA return map; } - Dictionary FindMaps() + public Dictionary FindMaps() { var paths = Manifest.MapFolders.SelectMany(f => FindMapsIn(f)); var ret = new Dictionary(); diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index 8c9325c8f5..ff230b4430 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -324,7 +324,7 @@ namespace OpenRA.Utility } [Desc("MAPFILE", "MOD", "Upgrade a version 5 map to version 6.")] - public static void UpgradeMap(string[] args) + public static void UpgradeV5Map(string[] args) { var map = args[1]; var mod = args[2]; diff --git a/OpenRA.Utility/OpenRA.Utility.csproj b/OpenRA.Utility/OpenRA.Utility.csproj index eeb83d59c1..085267930e 100644 --- a/OpenRA.Utility/OpenRA.Utility.csproj +++ b/OpenRA.Utility/OpenRA.Utility.csproj @@ -76,6 +76,7 @@ + diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 9ad636e2e8..06914d0119 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -29,7 +29,9 @@ namespace OpenRA.Utility { "--docs", Command.ExtractTraitDocs }, { "--map-hash", Command.GetMapHash }, { "--map-preview", Command.GenerateMinimap }, - { "--map-upgrade", Command.UpgradeMap }, + { "--map-upgrade-v5", Command.UpgradeV5Map }, + { "--upgrade-map", UpgradeRules.UpgradeMap }, + { "--upgrade-mod", UpgradeRules.UpgradeMod }, }; static void Main(string[] args) diff --git a/OpenRA.Utility/UpgradeRules.cs b/OpenRA.Utility/UpgradeRules.cs new file mode 100644 index 0000000000..811f7577de --- /dev/null +++ b/OpenRA.Utility/UpgradeRules.cs @@ -0,0 +1,212 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 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.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Utility +{ + public static class UpgradeRules + { + 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); + } + + static void ConvertPxToRange(ref string input) + { + ConvertPxToRange(ref input, 1, 1); + } + + static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv) + { + var value = int.Parse(input); + var world = value * 1024 * scaleMult / (scaleDiv * Game.CellSize); + var cells = world / 1024; + var subcells = world - 1024 * cells; + + input = cells != 0 ? "{0}c{1}".F(cells, subcells) : subcells.ToString(); + } + + static void ConvertAngle(ref string input) + { + var value = float.Parse(input); + input = WAngle.ArcTan((int)(value * 4 * 1024), 1024).ToString(); + } + + static void ConvertInt2ToWVec(ref string input) + { + var offset = FieldLoader.GetValue("(value)", input); + var world = new WVec(offset.X * 1024 / Game.CellSize, offset.Y * 1024 / Game.CellSize, 0); + input = world.ToString(); + } + + static void UpgradeActorRules(int engineVersion, ref List 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); + } + + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); + } + } + + static void UpgradeWeaponRules(int engineVersion, ref List 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; + } + } + } + + UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1); + } + } + + [Desc("MAP", "CURRENTENGINE", "Upgrade map rules to the latest engine version.")] + public static void UpgradeMap(string[] args) + { + var map = new Map(args[1]); + var engineDate = int.Parse(args[2]); + + Game.modData = new ModData(map.RequiresMod); + UpgradeWeaponRules(engineDate, ref map.Weapons, null, 0); + UpgradeActorRules(engineDate, ref map.Rules, null, 0); + map.Save(args[1]); + } + + [Desc("MOD", "CURRENTENGINE", "Upgrade mod rules to the latest engine version.")] + public static void UpgradeMod(string[] args) + { + var mod = args[1]; + var engineDate = int.Parse(args[2]); + + Game.modData = new ModData(mod); + + Console.WriteLine("Processing Rules:"); + foreach (var filename in Game.modData.Manifest.Rules) + { + Console.WriteLine("\t" + filename); + var yaml = MiniYaml.FromFile(filename); + UpgradeActorRules(engineDate, ref yaml, null, 0); + + using (var file = new StreamWriter(filename)) + file.WriteLine(yaml.WriteToString()); + } + + Console.WriteLine("Processing Weapons:"); + foreach (var filename in Game.modData.Manifest.Weapons) + { + Console.WriteLine("\t" + filename); + var yaml = MiniYaml.FromFile(filename); + UpgradeWeaponRules(engineDate, ref yaml, null, 0); + + using (var file = new StreamWriter(filename)) + file.WriteLine(yaml.WriteToString()); + } + + Console.WriteLine("Processing Maps:"); + foreach (var map in Game.modData.FindMaps().Values) + { + Console.WriteLine("\t" + map.Path); + UpgradeActorRules(engineDate, ref map.Rules, null, 0); + UpgradeWeaponRules(engineDate, ref map.Weapons, null, 0); + map.Save(map.Path); + } + } + } +}