diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 1e481b9144..25b9616c3c 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -872,6 +872,7 @@ + diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/DefineLocomotors.cs b/OpenRA.Mods.Common/UpdateRules/Rules/DefineLocomotors.cs new file mode 100644 index 0000000000..311f177d9a --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/DefineLocomotors.cs @@ -0,0 +1,163 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 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; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class DefineLocomotors : UpdateRule + { + public override string Name { get { return "Introduce global Locomotor definitions"; } } + public override string Description + { + get + { + return "A large number of properties have been moved from the actor-level Mobile trait\n" + + "to either world-level Locomotor traits or new actor-level GrantCondition* traits.\n" + + "Conditions for subterranean and jumpjet behaviours are migrated,\n" + + "and affected Mobile traits are listed for inspection."; + } + } + + readonly List> locations = new List>(); + bool subterraneanUsed; + bool jumpjetUsed; + + readonly string[] locomotorFields = + { + "TerrainSpeeds", "Crushes", "CrushDamageTypes", "SharesCell", "MoveIntoShroud" + }; + + readonly string[] subterraneanFields = + { + "SubterraneanTransitionCost", "SubterraneanTransitionTerrainTypes", + "SubterraneanTransitionOnRamps", "SubterraneanTransitionDepth", + "SubterraneanTransitionPalette", "SubterraneanTransitionSound", + }; + + readonly string[] jumpjetFields = + { + "JumpjetTransitionCost", "JumpjetTransitionTerrainTypes", "JumpjetTransitionOnRamps" + }; + + public override IEnumerable AfterUpdate(ModData modData) + { + var message = "You must define a set of Locomotor traits to the World actor for the different\n" + + "movement classes used in your mod (e.g. Infantry, Vehicles, Tanks, Ships, etc)\n" + + "and replace any definitions/overrides of the following properties on each\n" + + "actor with a Locomotor field referencing the appropriate locomotor type.\n\n" + + "The standard Locomotor definition contains the following fields:\n" + + UpdateUtils.FormatMessageList(locomotorFields) + "\n\n"; + + if (subterraneanUsed) + message += "Actors using the subterranean logic should reference a SubterraneanLocomotor\n" + + "instance that extends Locomotor with additional fields:\n" + + UpdateUtils.FormatMessageList(subterraneanFields) + "\n\n"; + + if (jumpjetUsed) + message += "Actors using the jump-jet logic should reference a JumpjetLocomotor\n" + + "instance that extends Locomotor with additional fields:\n" + + UpdateUtils.FormatMessageList(jumpjetFields) + "\n\n"; + + message += "Condition definitions have been automatically migrated.\n" + + "The following definitions reference fields that must be manually moved to Locomotors:\n" + + UpdateUtils.FormatMessageList(locations.Select(n => n.Item1 + " (" + n.Item2 + ")")); + + if (locations.Any()) + yield return message; + + locations.Clear(); + } + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) + { + var addNodes = new List(); + foreach (var mobileNode in actorNode.ChildrenMatching("Mobile")) + { + var checkFields = locomotorFields.Append(subterraneanFields).Append(jumpjetFields); + if (checkFields.Any(f => mobileNode.ChildrenMatching(f).Any())) + locations.Add(Tuple.Create(actorNode.Key, actorNode.Location.Filename)); + + var tunnelConditionNode = mobileNode.LastChildMatching("TunnelCondition"); + if (tunnelConditionNode != null) + { + var grantNode = new MiniYamlNode("GrantConditionOnTunnelLayer", ""); + grantNode.AddNode("Condition", tunnelConditionNode.Value.Value); + addNodes.Add(grantNode); + mobileNode.RemoveNodes("TunnelCondition"); + } + + var subterraneanNode = mobileNode.LastChildMatching("Subterranean"); + if (subterraneanNode != null) + { + subterraneanUsed = true; + + mobileNode.RemoveNodes("Subterranean"); + var conditionNode = mobileNode.LastChildMatching("SubterraneanCondition"); + if (conditionNode != null) + conditionNode.RenameKeyPreservingSuffix("Condition"); + + var transitionImageNode = mobileNode.LastChildMatching("SubterraneanTransitionImage"); + var transitionSequenceNode = mobileNode.LastChildMatching("SubterraneanTransitionSequence"); + var transitionPaletteNode = mobileNode.LastChildMatching("SubterraneanTransitionPalette"); + var transitionSoundNode = mobileNode.LastChildMatching("SubterraneanTransitionSound"); + + var nodes = new[] + { + conditionNode, + transitionImageNode, + transitionSequenceNode, + transitionPaletteNode, + transitionSoundNode + }; + + if (nodes.Any(n => n != null)) + { + var grantNode = new MiniYamlNode("GrantConditionOnSubterraneanLayer", ""); + foreach (var node in nodes) + { + if (node != null) + { + grantNode.Value.Nodes.Add(node); + mobileNode.Value.Nodes.Remove(node); + } + } + + addNodes.Add(grantNode); + } + } + + var jumpjetNode = mobileNode.LastChildMatching("Jumpjet"); + if (jumpjetNode != null) + { + jumpjetUsed = true; + + mobileNode.RemoveNodes("Jumpjet"); + var conditionNode = mobileNode.LastChildMatching("JumpjetCondition"); + if (conditionNode != null) + { + var grantNode = new MiniYamlNode("GrantConditionOnJumpjetLayer", ""); + grantNode.AddNode("Condition", conditionNode.Value.Value); + mobileNode.RemoveNodes("JumpjetCondition"); + addNodes.Add(grantNode); + } + } + } + + foreach (var node in addNodes) + actorNode.Value.Nodes.Add(node); + + yield break; + } + } +}