From 129db98a2f6d2244e231f058c15c407a67cb4b6b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 25 Dec 2022 10:13:09 +1300 Subject: [PATCH] Add BeforeUpdate* methods for update rules. These make it possible to write more advanced update rules that query state across multiple actors, or based on resolved state. --- OpenRA.Mods.Common/UpdateRules/UpdateRule.cs | 4 ++ OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs | 44 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.Common/UpdateRules/UpdateRule.cs b/OpenRA.Mods.Common/UpdateRules/UpdateRule.cs index aa923d0bab..b5f31ebdf1 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdateRule.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdateRule.cs @@ -36,5 +36,9 @@ namespace OpenRA.Mods.Common.UpdateRules public virtual IEnumerable BeforeUpdate(ModData modData) { yield break; } public virtual IEnumerable AfterUpdate(ModData modData) { yield break; } + + public virtual IEnumerable BeforeUpdateActors(ModData modData, List resolvedActors) { yield break; } + public virtual IEnumerable BeforeUpdateWeapons(ModData modData, List resolvedWeapons) { yield break; } + public virtual IEnumerable BeforeUpdateSequences(ModData modData, List resolvedImages) { yield break; } } } diff --git a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs index d365ebc6a3..f1de1123b0 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs @@ -114,6 +114,9 @@ namespace OpenRA.Mods.Common.UpdateRules var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); if (mapRulesNode != null) { + var resolvedActors = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Rules, mapRulesNode.Value); + manualSteps.AddRange(rule.BeforeUpdateActors(modData, resolvedActors)); + var mapRules = LoadInternalMapYaml(modData, mapPackage, mapRulesNode.Value, externalFilenames); manualSteps.AddRange(ApplyTopLevelTransform(modData, mapRules, rule.UpdateActorNode)); files.AddRange(mapRules); @@ -122,6 +125,9 @@ namespace OpenRA.Mods.Common.UpdateRules var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); if (mapWeaponsNode != null) { + var resolvedWeapons = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Weapons, mapWeaponsNode.Value); + manualSteps.AddRange(rule.BeforeUpdateWeapons(modData, resolvedWeapons)); + var mapWeapons = LoadInternalMapYaml(modData, mapPackage, mapWeaponsNode.Value, externalFilenames); manualSteps.AddRange(ApplyTopLevelTransform(modData, mapWeapons, rule.UpdateWeaponNode)); files.AddRange(mapWeapons); @@ -130,8 +136,11 @@ namespace OpenRA.Mods.Common.UpdateRules var mapSequencesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Sequences"); if (mapSequencesNode != null) { + var resolvedImages = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Sequences, mapSequencesNode.Value); + manualSteps.AddRange(rule.BeforeUpdateSequences(modData, resolvedImages)); + var mapSequences = LoadInternalMapYaml(modData, mapPackage, mapSequencesNode.Value, externalFilenames); - manualSteps.AddRange(ApplyTopLevelTransform(modData, mapSequences, rule.UpdateWeaponNode)); + manualSteps.AddRange(ApplyTopLevelTransform(modData, mapSequences, rule.UpdateSequenceNode)); files.AddRange(mapSequences); } @@ -141,6 +150,29 @@ namespace OpenRA.Mods.Common.UpdateRules return manualSteps; } + public static List LoadMapYaml(IReadOnlyFileSystem fileSystem, IReadOnlyPackage mapPackage, IEnumerable files, MiniYaml mapNode) + { + var yaml = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)).ToList(); + + if (mapNode != null && mapNode.Value != null) + { + var mapFiles = FieldLoader.GetValue("value", mapNode.Value); + yaml.AddRange(mapFiles.Select(filename => + { + // Explicit package paths never refer to a map + if (!filename.Contains('|') && mapPackage.Contains(filename)) + return MiniYaml.FromStream(mapPackage.GetStream(filename)); + + return MiniYaml.FromStream(fileSystem.Open(filename)); + })); + } + + if (mapNode != null && mapNode.Nodes.Count > 0) + yaml.Add(mapNode.Nodes); + + return MiniYaml.Merge(yaml); + } + static IEnumerable FilterExternalModFiles(ModData modData, IEnumerable files, HashSet externalFilenames) { foreach (var f in files) @@ -196,9 +228,19 @@ namespace OpenRA.Mods.Common.UpdateRules } manualSteps.AddRange(rule.BeforeUpdate(modData)); + + var resolvedActors = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Rules, null); + manualSteps.AddRange(rule.BeforeUpdateActors(modData, resolvedActors)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modRules, rule.UpdateActorNode)); + + var resolvedWeapons = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Weapons, null); + manualSteps.AddRange(rule.BeforeUpdateWeapons(modData, resolvedWeapons)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modWeapons, rule.UpdateWeaponNode)); + + var resolvedSequences = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Sequences, null); + manualSteps.AddRange(rule.BeforeUpdateSequences(modData, resolvedSequences)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modSequences, rule.UpdateSequenceNode)); + manualSteps.AddRange(ApplyTopLevelTransform(modData, modTilesets, rule.UpdateTilesetNode)); manualSteps.AddRange(ApplyChromeTransform(modData, modChromeLayout, rule.UpdateChromeNode)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modChromeProvider, rule.UpdateChromeProviderNode));