diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs index c6a39e9856..84ae36a5d5 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs @@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules } } + readonly List locations = new List(); bool messageShown; readonly string[] harvesterFields = @@ -40,13 +41,19 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules public override IEnumerable AfterUpdate(ModData modData) { - var message = "You may want to check your AI yamls for possible redundant module entries.\n" + - "Additionally, make sure the Player actor has the ConditionManager trait and add it manually if it doesn't."; - if (!messageShown) - yield return message; + yield return "You may want to check your AI yamls for possible redundant module entries.\n" + + "Additionally, make sure the Player actor has the ConditionManager trait and add it manually if it doesn't."; messageShown = true; + + if (locations.Any()) + yield return "This update rule can only autoamtically update the base HackyAI definitions,\n" + + "not any overrides in other files (unless they redefine Type).\n" + + "You will have to manually check and possibly update the following locations:\n" + + UpdateUtils.FormatMessageList(locations); + + locations.Clear(); } public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) @@ -54,24 +61,39 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules if (actorNode.Key != "Player") yield break; - var hackyAIs = actorNode.ChildrenMatching("HackyAI"); + var hackyAIs = actorNode.ChildrenMatching("HackyAI", includeRemovals: false); if (!hackyAIs.Any()) yield break; var addNodes = new List(); - // We add a 'default' HarvesterBotModule in any case, + // We add a 'default' HarvesterBotModule in any case (unless the file doesn't contain any HackyAI base definition), // and only add more for AIs that define custom values for one of its fields. var defaultHarvNode = new MiniYamlNode("HarvesterBotModule", ""); + var addDefaultHarvModule = false; foreach (var hackyAINode in hackyAIs) { - // HackyAIInfo.Name might contain spaces, so Type is better suited to be used as condition name - var aiType = hackyAINode.LastChildMatching("Type").NodeValue(); + // HackyAIInfo.Name might contain spaces, so Type is better suited to be used as condition name. + // Type can be 'null' if the place we're updating is overriding the default rules (like a map's rules.yaml). + // If that's the case, it's better to not perform most of the updates on this particular yaml file, + // as most - or more likely all - necessary updates will already have been performed on the base ai yaml. + var aiTypeNode = hackyAINode.LastChildMatching("Type"); + var aiType = aiTypeNode != null ? aiTypeNode.NodeValue() : null; + if (aiType == null) + { + locations.Add("{0} ({1})".F(hackyAINode.Key, hackyAINode.Location.Filename)); + continue; + } + + addDefaultHarvModule = true; + var conditionString = "enable-" + aiType + "-ai"; var requiresCondition = new MiniYamlNode("RequiresCondition", conditionString); var addGrantConditionOnBotOwner = true; + + // Don't add GrantConditionOnBotOwner if it's already been added with matching condition var grantBotConditions = actorNode.ChildrenMatching("GrantConditionOnBotOwner"); foreach (var grant in grantBotConditions) if (grant.LastChildMatching("Condition").NodeValue() == conditionString) @@ -140,7 +162,8 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules } } - addNodes.Add(defaultHarvNode); + if (addDefaultHarvModule) + addNodes.Add(defaultHarvNode); foreach (var node in addNodes) actorNode.AddNode(node);