Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class AddColorPickerValueRange : UpdateRule
{
public override string Name => "ColorPickerManager's PresetHues, PresetSaturations and V were replaced with PresetColors.";
public override string Description =>
"Each preset color can now have their brightness specified. SimilarityThreshold range was changed.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
var manager = actorNode.LastChildMatching("ColorPickerManager");
if (manager == null)
yield break;
manager.RemoveNodes("SimilarityThreshold");
var v = manager.LastChildMatching("V")?.NodeValue<float>() ?? 0.95f;
var hues = manager.LastChildMatching("PresetHues")?.NodeValue<float[]>();
var saturations = manager.LastChildMatching("PresetSaturations")?.NodeValue<float[]>();
manager.RemoveNodes("V");
manager.RemoveNodes("PresetHues");
manager.RemoveNodes("PresetSaturations");
if (hues == null || saturations == null)
yield break;
var colors = new Color[hues.Length];
for (var i = 0; i < colors.Length; i++)
colors[i] = Color.FromAhsv(hues[i], saturations[i], v);
manager.AddNode("PresetColors", colors);
}
}
}

View File

@@ -0,0 +1,500 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ExplicitSequenceFilenames : UpdateRule, IBeforeUpdateSequences
{
public override string Name => "Sequence filenames must be specified explicitly.";
public override string Description =>
"Sequence sprite filenames are no longer automatically inferred, and the AddExtension,\n" +
"UseTilesetExtension, UseTilesetNodes and TilesetOverrides fields have been removed.\n\n" +
"The sprite filename for each sequence must now be defined using the Filename field.\n" +
"Tileset specific overrides can be defined as children of the TilesetFilenames field.";
string defaultSpriteExtension = ".shp";
List<MiniYamlNodeBuilder> resolvedImagesNodes;
readonly Dictionary<string, string> tilesetExtensions = [];
readonly Dictionary<string, string> tilesetCodes = [];
bool parseModYaml = true;
bool reportModYamlChanges;
bool disabled;
public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNodeBuilder> resolvedImagesNodes)
{
// Keep a resolved copy of the sequences so we can account for values imported through inheritance or Defaults.
// This will be modified during processing, so take a deep copy to avoid side-effects on other update rules.
this.resolvedImagesNodes = MiniYaml.FromString(resolvedImagesNodes.WriteToString(), nameof(BeforeUpdateSequences))
.Select(n => new MiniYamlNodeBuilder(n))
.ToList();
var requiredMetadata = new HashSet<string>();
foreach (var imageNode in resolvedImagesNodes)
{
foreach (var sequenceNode in imageNode.Value.Nodes)
{
var useTilesetExtensionNode = sequenceNode.LastChildMatching("UseTilesetExtension");
if (useTilesetExtensionNode != null && tilesetExtensions.Count == 0)
requiredMetadata.Add("TilesetExtensions");
var useTilesetCodeNode = sequenceNode.LastChildMatching("UseTilesetCode");
if (useTilesetCodeNode != null && tilesetCodes.Count == 0)
requiredMetadata.Add("TilesetCodes");
}
}
if (requiredMetadata.Count != 0)
{
yield return
$"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" +
"to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" +
"Add these definitions back and run the update rule again.";
disabled = true;
}
}
public override IEnumerable<string> BeforeUpdate(ModData modData)
{
// Don't reload data when processing maps
if (!parseModYaml)
yield break;
parseModYaml = false;
// HACK: We need to read the obsolete yaml definitions to be able to update the sequences
// TilesetSpecificSpriteSequence no longer defines fields for these, so we must take them directly from mod.yaml
var yamlField = modData.Manifest.GetType().GetField("yaml", BindingFlags.Instance | BindingFlags.NonPublic);
var yaml = (Dictionary<string, MiniYaml>)yamlField?.GetValue(modData.Manifest);
if (yaml != null && yaml.TryGetValue("SpriteSequenceFormat", out var spriteSequenceFormatYaml))
{
if (spriteSequenceFormatYaml.Value == "DefaultSpriteSequence")
{
defaultSpriteExtension = "";
yield break;
}
var spriteSequenceFormatNode = new MiniYamlNodeBuilder("", new MiniYamlBuilder(spriteSequenceFormatYaml));
var defaultSpriteExtensionNode = spriteSequenceFormatNode.LastChildMatching("DefaultSpriteExtension");
if (defaultSpriteExtensionNode != null)
{
reportModYamlChanges = true;
defaultSpriteExtension = defaultSpriteExtensionNode.Value.Value;
}
var fromBackup = false;
var tilesetExtensionsNode = spriteSequenceFormatNode.LastChildMatching("TilesetExtensions")?.Value?.Nodes;
if (tilesetExtensionsNode == null)
{
switch (modData.Manifest.Id)
{
case "cnc":
fromBackup = true;
tilesetExtensionsNode =
[
new("TEMPERAT", ".tem"),
new("SNOW", ".sno"),
new("INTERIOR", ".int"),
new("DESERT", ".des"),
new("JUNGLE", ".jun"),
];
break;
case "ra":
fromBackup = true;
tilesetExtensionsNode =
[
new("TEMPERAT", ".tem"),
new("SNOW", ".sno"),
new("INTERIOR", ".int"),
new("DESERT", ".des"),
];
break;
case "ts":
fromBackup = true;
tilesetExtensionsNode =
[
new("TEMPERATE", ".tem"),
new("SNOW", ".sno"),
];
break;
}
}
if (tilesetExtensionsNode != null)
{
if (!fromBackup)
reportModYamlChanges = true;
foreach (var n in tilesetExtensionsNode)
tilesetExtensions[n.Key] = n.Value.Value;
}
fromBackup = false;
var tilesetCodesNode = spriteSequenceFormatNode.LastChildMatching("TilesetCodes")?.Value?.Nodes;
if (tilesetCodesNode == null && modData.Manifest.Id == "ts")
{
fromBackup = true;
tilesetCodesNode =
[
new("TEMPERATE", "t"),
new("SNOW", "a"),
];
}
if (tilesetCodesNode != null)
{
if (!fromBackup)
reportModYamlChanges = true;
foreach (var n in tilesetCodesNode)
tilesetCodes[n.Key] = n.Value.Value;
}
}
}
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (!reportModYamlChanges)
yield break;
yield return "The DefaultSpriteExtension, TilesetExtensions, and TilesetCodes fields defined\n" +
"under SpriteSequenceFormat in your mod.yaml are no longer used, and can be removed.";
reportModYamlChanges = false;
}
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder imageNode)
{
if (disabled)
yield break;
var resolvedImageNode = resolvedImagesNodes.Single(n => n.Key == imageNode.Key);
// Add a placeholder for inherited sequences that were previously implicitly named
var implicitInheritedSequences = new List<string>();
foreach (var resolvedSequenceNode in resolvedImageNode.Value.Nodes)
{
if (resolvedSequenceNode.Key == "Defaults")
continue;
// Ignore nodes that are not implicitly named or already processed
if (!string.IsNullOrEmpty(resolvedSequenceNode.Value.Value) || resolvedSequenceNode.LastChildMatching("Filename") != null)
continue;
if (imageNode.LastChildMatching(resolvedSequenceNode.Key) == null)
{
imageNode.AddNode(resolvedSequenceNode.Key, "");
implicitInheritedSequences.Add(resolvedSequenceNode.Key);
}
}
var resolvedDefaultsNode = resolvedImageNode.LastChildMatching("Defaults");
if (resolvedDefaultsNode != null)
{
foreach (var resolvedSequenceNode in resolvedImageNode.Value.Nodes)
{
if (resolvedSequenceNode == resolvedDefaultsNode)
continue;
resolvedSequenceNode.Value.Nodes = MiniYaml.Merge(
[
resolvedDefaultsNode.Value.Nodes.Select(n => n.Build()).ToArray(),
resolvedSequenceNode.Value.Nodes.Select(n => n.Build()).ToArray()
]).ConvertAll(n => new MiniYamlNodeBuilder(n));
resolvedSequenceNode.Value.Value ??= resolvedDefaultsNode.Value.Value;
}
}
// Sequences that explicitly defined a filename may be inherited by others that depend on the explicit name
// Keep track of these sequences so we don't remove the filenames later!
var explicitlyNamedSequences = new List<string>();
// Add Filename/TilesetFilenames nodes to every sequence
foreach (var sequenceNode in imageNode.Value.Nodes)
{
if (string.IsNullOrEmpty(sequenceNode.Key) || sequenceNode.KeyMatches("Inherits"))
continue;
if (!string.IsNullOrEmpty(sequenceNode.Value.Value))
explicitlyNamedSequences.Add(sequenceNode.Key);
var resolvedSequenceNode = resolvedImageNode.Value.NodeWithKeyOrDefault(sequenceNode.Key);
if (resolvedSequenceNode == null)
continue;
ProcessNode(modData, sequenceNode, resolvedSequenceNode, imageNode.Key);
}
// Identify a suitable default for deduplication
MiniYamlNodeBuilder defaultFilenameNode = null;
MiniYamlNodeBuilder defaultTilesetFilenamesNode = null;
foreach (var defaultsNode in imageNode.ChildrenMatching("Defaults"))
{
defaultFilenameNode = defaultsNode.LastChildMatching("Filename") ?? defaultFilenameNode;
defaultTilesetFilenamesNode = defaultsNode.LastChildMatching("TilesetFilenames") ?? defaultTilesetFilenamesNode;
}
if ((defaultFilenameNode == null || defaultTilesetFilenamesNode == null) && !imageNode.Key.StartsWith('^'))
{
var duplicateCount = new Dictionary<string, int>();
var duplicateTilesetCount = new Dictionary<string, int>();
foreach (var sequenceNode in imageNode.Value.Nodes)
{
if (string.IsNullOrEmpty(sequenceNode.Key) || explicitlyNamedSequences.Contains(sequenceNode.Key))
continue;
var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames");
if (defaultTilesetFilenamesNode == null && tilesetFilenamesNode != null)
{
var key = tilesetFilenamesNode.Value.Nodes.WriteToString();
duplicateTilesetCount[key] = duplicateTilesetCount.GetValueOrDefault(key, 0) + 1;
}
var filenameNode = sequenceNode.LastChildMatching("Filename");
if (defaultFilenameNode == null && filenameNode != null)
{
var key = filenameNode.Value.Value;
duplicateCount[key] = duplicateCount.GetValueOrDefault(key, 0) + 1;
}
}
var inheritsNode = imageNode.LastChildMatching("Inherits");
var inheritsNodeIndex = inheritsNode == null ? 0 : imageNode.Value.Nodes.IndexOf(inheritsNode) + 1;
var maxDuplicateTilesetCount = duplicateTilesetCount.MaxByOrDefault(kv => kv.Value).Value;
if (maxDuplicateTilesetCount > 1)
{
var defaultsNode = imageNode.LastChildMatching("Defaults");
if (defaultsNode == null)
{
defaultsNode = new MiniYamlNodeBuilder("Defaults", "");
imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode);
}
var nodes = MiniYaml.FromString(duplicateTilesetCount.First(kv => kv.Value == maxDuplicateTilesetCount).Key, nameof(UpdateSequenceNode)).ToList();
defaultTilesetFilenamesNode = new MiniYamlNodeBuilder("TilesetFilenames", "", nodes);
defaultsNode.Value.Nodes.Insert(0, defaultTilesetFilenamesNode);
}
var maxDuplicateCount = duplicateCount.MaxByOrDefault(kv => kv.Value).Value;
if (maxDuplicateCount > 1)
{
var defaultsNode = imageNode.LastChildMatching("Defaults");
if (defaultsNode == null)
{
defaultsNode = new MiniYamlNodeBuilder("Defaults", "");
imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode);
}
defaultFilenameNode = new MiniYamlNodeBuilder("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key);
defaultsNode.Value.Nodes.Insert(0, defaultFilenameNode);
}
}
// Remove redundant definitions
foreach (var sequenceNode in imageNode.Value.Nodes.ToList())
{
if (sequenceNode.Key == "Defaults" || sequenceNode.Key == "Inherits" || string.IsNullOrEmpty(sequenceNode.Key))
continue;
var combineNode = sequenceNode.LastChildMatching("Combine");
var filenameNode = sequenceNode.LastChildMatching("Filename");
var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames");
if (defaultTilesetFilenamesNode != null && combineNode != null)
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("TilesetFilenames", ""));
if (defaultFilenameNode != null && combineNode != null)
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("Filename", ""));
if (defaultTilesetFilenamesNode != null && tilesetFilenamesNode == null && filenameNode != null)
{
var index = sequenceNode.Value.Nodes.IndexOf(filenameNode) + 1;
sequenceNode.Value.Nodes.Insert(index, new MiniYamlNodeBuilder("TilesetFilenames", ""));
}
// Remove redundant overrides
if (!explicitlyNamedSequences.Contains(sequenceNode.Key))
{
if (defaultTilesetFilenamesNode != null && tilesetFilenamesNode != null)
{
var allTilesetsMatch = true;
foreach (var overrideNode in tilesetFilenamesNode.Value.Nodes)
if (!defaultTilesetFilenamesNode.Value.Nodes.Any(n => n.Key == overrideNode.Key && n.Value.Value == overrideNode.Value.Value))
allTilesetsMatch = false;
if (allTilesetsMatch)
sequenceNode.RemoveNode(tilesetFilenamesNode);
}
if (filenameNode?.Value.Value != null && filenameNode?.Value.Value == defaultFilenameNode?.Value.Value)
sequenceNode.RemoveNode(filenameNode);
}
}
var allSequencesHaveFilename = true;
var allSequencesHaveTilesetFilenames = true;
foreach (var sequenceNode in imageNode.Value.Nodes.ToList())
{
if (sequenceNode.Key == "Defaults" || sequenceNode.Key == "Inherits" || string.IsNullOrEmpty(sequenceNode.Key))
continue;
if (sequenceNode.LastChildMatching("Filename") == null)
allSequencesHaveFilename = false;
if (sequenceNode.LastChildMatching("TilesetFilenames") == null)
allSequencesHaveTilesetFilenames = false;
}
if (allSequencesHaveFilename || allSequencesHaveTilesetFilenames)
{
foreach (var sequenceNode in imageNode.Value.Nodes.ToList())
{
if (sequenceNode.Key == "Defaults")
{
if (allSequencesHaveFilename)
sequenceNode.RemoveNodes("Filename");
if (allSequencesHaveTilesetFilenames)
sequenceNode.RemoveNodes("TilesetFilenames");
if (sequenceNode.Value.Nodes.Count == 0)
imageNode.RemoveNode(sequenceNode);
}
if (allSequencesHaveFilename && sequenceNode.LastChildMatching("Combine") != null)
sequenceNode.RemoveNodes("Filename");
if (allSequencesHaveTilesetFilenames && sequenceNode.LastChildMatching("Combine") != null)
sequenceNode.RemoveNodes("TilesetFilenames");
var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames");
if (allSequencesHaveTilesetFilenames && tilesetFilenamesNode != null && tilesetFilenamesNode.Value.Nodes.Count == 0)
sequenceNode.RemoveNode(tilesetFilenamesNode);
}
}
foreach (var sequenceNode in imageNode.Value.Nodes.ToList())
if (implicitInheritedSequences.Contains(sequenceNode.Key) && sequenceNode.Value.Nodes.Count == 0)
imageNode.RemoveNode(sequenceNode);
}
void ProcessNode(ModData modData, MiniYamlNodeBuilder sequenceNode, MiniYamlNodeBuilder resolvedSequenceNode, string imageName)
{
// "Filename" was introduced with this update rule, so that means this node was already processed and can be skipped
if (sequenceNode.LastChildMatching("Filename") != null)
return;
var addExtension = true;
var addExtensionNode = resolvedSequenceNode.LastChildMatching("AddExtension");
if (addExtensionNode != null)
addExtension = FieldLoader.GetValue<bool>("AddExtension", addExtensionNode.Value.Value);
var useTilesetExtension = false;
var useTilesetExtensionNode = resolvedSequenceNode.LastChildMatching("UseTilesetExtension");
if (useTilesetExtensionNode != null)
useTilesetExtension = FieldLoader.GetValue<bool>("UseTilesetExtension", useTilesetExtensionNode.Value.Value);
var useTilesetCode = false;
var useTilesetCodeNode = resolvedSequenceNode.LastChildMatching("UseTilesetCode");
if (useTilesetCodeNode != null)
useTilesetCode = FieldLoader.GetValue<bool>("UseTilesetCode", useTilesetCodeNode.Value.Value);
var tilesetOverrides = new Dictionary<string, string>();
var tilesetOverridesNode = resolvedSequenceNode.LastChildMatching("TilesetOverrides");
if (tilesetOverridesNode != null)
foreach (var tilesetNode in tilesetOverridesNode.Value.Nodes)
tilesetOverrides[tilesetNode.Key] = tilesetNode.Value.Value;
sequenceNode.RemoveNodes("AddExtension");
sequenceNode.RemoveNodes("UseTilesetExtension");
sequenceNode.RemoveNodes("UseTilesetCode");
sequenceNode.RemoveNodes("TilesetOverrides");
// Replace removals with masking
foreach (var node in sequenceNode.Value.Nodes)
if (node.Key?.StartsWith('-') ?? false)
node.Key = node.Key[1..];
var combineNode = sequenceNode.LastChildMatching("Combine");
if (combineNode != null)
{
var i = 0;
foreach (var node in combineNode.Value.Nodes)
{
ProcessNode(modData, node, node, node.Key);
node.Key = i++.ToStringInvariant();
}
return;
}
var filename = string.IsNullOrEmpty(resolvedSequenceNode.Value.Value) ? imageName : resolvedSequenceNode.Value.Value;
if (filename.StartsWith('^'))
return;
if (useTilesetExtension || useTilesetCode)
{
var tilesetFilenamesNode = new MiniYamlNodeBuilder("TilesetFilenames", "");
var duplicateCount = new Dictionary<string, int>();
foreach (var tileset in modData.DefaultTerrainInfo.Keys)
{
if (!tilesetOverrides.TryGetValue(tileset, out var sequenceTileset))
sequenceTileset = tileset;
var overrideFilename = filename;
if (useTilesetCode)
overrideFilename = filename[..1] + tilesetCodes[sequenceTileset] + filename[2..];
if (addExtension)
overrideFilename += useTilesetExtension ? tilesetExtensions[sequenceTileset] : defaultSpriteExtension;
tilesetFilenamesNode.AddNode(tileset, overrideFilename);
duplicateCount[overrideFilename] = duplicateCount.GetValueOrDefault(overrideFilename, 0) + 1;
}
sequenceNode.Value.Nodes.Insert(0, tilesetFilenamesNode);
// Deduplicate tileset overrides
var maxDuplicateCount = duplicateCount.MaxByOrDefault(kv => kv.Value).Value;
if (maxDuplicateCount > 1)
{
var filenameNode = new MiniYamlNodeBuilder("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key);
foreach (var overrideNode in tilesetFilenamesNode.Value.Nodes.ToList())
if (overrideNode.Value.Value == filenameNode.Value.Value)
tilesetFilenamesNode.Value.Nodes.Remove(overrideNode);
if (tilesetFilenamesNode.Value.Nodes.Count == 0)
sequenceNode.RemoveNode(tilesetFilenamesNode);
sequenceNode.Value.Nodes.Insert(0, filenameNode);
}
}
else
{
if (addExtension)
filename += defaultSpriteExtension;
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("Filename", filename));
}
sequenceNode.ReplaceValue("");
}
}
}

View File

@@ -0,0 +1,39 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ProductionTabsWidgetAddTabButtonCollection : UpdateRule
{
public override string Name => "Change name of Button field of ProductionTabsWidget and add ArrowButton if necessary.";
public override string Description =>
"Change the field name from Button to TabButton and add ArrowButton, if Button field was set.";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{
if (!chromeNode.KeyMatches("ProductionTabs"))
yield break;
string buttonCollection = null;
foreach (var field in chromeNode.ChildrenMatching("Button"))
{
field.RenameKey("TabButton");
buttonCollection = field.Value.Value;
}
if (buttonCollection != null)
chromeNode.AddNode(new MiniYamlNodeBuilder("ArrowButton", buttonCollection));
}
}
}

View File

@@ -0,0 +1,50 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveExperienceFromInfiltrates : UpdateRule
{
public override string Name => "Removes PlayerExperience property from Infiltrates.";
public override string Description =>
"Infiltrates property PlayerExperience was removed, " +
"it was replaced by adding PlayerExperience to all InfiltrateFor* Traits.";
readonly List<string> locations = [];
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (locations.Count > 0)
yield return "The 'PlayerExperience' fields have been removed from the 'Infiltrates' trait\n" +
"and added to InfiltrateFor* traits. If you want to keep 'PlayerExperience' you will\n" +
"need to add it to each of the InfiltrateFor* traits. Properties removed from:\n" +
UpdateUtils.FormatMessageList(locations);
locations.Clear();
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
var removed = false;
foreach (var node in actorNode.ChildrenMatching("Infiltrates"))
if (node.RemoveNodes("PlayerExperience") > 0)
removed = true;
if (removed)
locations.Add($"{actorNode.Key} ({actorNode.Location.Name})");
yield break;
}
}
}

View File

@@ -0,0 +1,92 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 RemoveNegativeSequenceLength : UpdateRule, IBeforeUpdateSequences
{
public override string Name => "Negative sequence length is no longer allowed.";
public override string Description => "Negative sequence length is no longer allowed, define individual frames in reverse instead.";
List<MiniYamlNodeBuilder> resolvedImagesNodes;
public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNodeBuilder> resolvedImagesNodes)
{
this.resolvedImagesNodes = resolvedImagesNodes;
yield break;
}
readonly Queue<Action> actionQueue = [];
static MiniYamlNodeBuilder GetNode(string key, MiniYamlNodeBuilder node, MiniYamlNodeBuilder defaultNode)
{
return node.LastChildMatching(key, includeRemovals: false) ?? defaultNode?.LastChildMatching(key, includeRemovals: false);
}
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder sequenceNode)
{
var defaultNode = sequenceNode.LastChildMatching("Defaults");
var defaultLengthNode = defaultNode == null ? null : GetNode("Length", defaultNode, null);
foreach (var node in sequenceNode.Value.Nodes)
{
var lengthNode = node.LastChildMatching("Length");
if (lengthNode != null && lengthNode.IsRemoval())
continue;
lengthNode ??= defaultLengthNode;
if (lengthNode == null || lengthNode.Value.Value == "*")
continue;
var length = FieldLoader.GetValue<int>(lengthNode.Key, lengthNode.Value.Value);
if (length >= 0)
continue;
var resolvedImage = resolvedImagesNodes.First(n => n.Key == sequenceNode.Key);
var resolvedNode = resolvedImage.LastChildMatching(node.Key);
var resolvedDefaultNode = resolvedNode.Value.Value == "Defaults" ? null : resolvedImage.LastChildMatching("Defaults");
var startNode = GetNode("Start", node, defaultNode) ?? GetNode("Start", resolvedNode, resolvedDefaultNode);
if (startNode == null)
{
actionQueue.Enqueue(() => node.RemoveNodes("Length"));
continue;
}
var facingsNode = GetNode("Facings", node, defaultNode) ?? GetNode("Facings", resolvedNode, resolvedDefaultNode);
var facings = facingsNode == null ? 1 : FieldLoader.GetValue<int>(facingsNode.Key, facingsNode.Value.Value);
length = -length * facings;
var start = FieldLoader.GetValue<int>(startNode.Key, startNode.Value.Value) - 1;
var frames = new int[length];
for (var i = 0; i < length; i++)
frames[i] = start - i;
actionQueue.Enqueue(() =>
{
node.RemoveNodes("Start");
node.RemoveNodes("Length");
node.AddNode("Frames", frames);
});
}
while (actionQueue.Count != 0)
actionQueue.Dequeue().Invoke();
yield break;
}
}
}

View File

@@ -0,0 +1,57 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveSequenceHasEmbeddedPalette : UpdateRule
{
public override string Name => "Remove sequence HasEmbeddedPalette property.";
public override string Description =>
"The PaletteFromEmbeddedSpritePalette trait no longer references a sequence.\n" +
"Image and Sequence are replaced by Filename and Frame.";
readonly HashSet<string> locations = [];
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (locations.Count > 0)
yield return
"The PaletteFromEmbeddedSpritePalette trait no longer references a sequence.\n" +
"You must manually define Filename (and Frame if needed) on the following actors:\n" +
UpdateUtils.FormatMessageList(locations);
locations.Clear();
}
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder imageNode)
{
foreach (var sequenceNode in imageNode.Value.Nodes)
sequenceNode.RemoveNodes("HasEmbeddedPalette");
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var traitNode in actorNode.ChildrenMatching("PaletteFromEmbeddedSpritePalette"))
{
traitNode.RemoveNodes("Image");
traitNode.RemoveNodes("Sequence");
locations.Add($"{actorNode.Key} ({actorNode.Location.Name})");
}
yield break;
}
}
}

View File

@@ -0,0 +1,29 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveTSRefinery : UpdateRule
{
public override string Name => "TiberianSunRefinery removed.";
public override string Description => "TiberianSunRefinery was removed, use Refinery instead.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("TiberianSunRefinery", "Refinery");
yield break;
}
}
}

View File

@@ -0,0 +1,42 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameContrailWidth : UpdateRule
{
public override string Name => "Rename contrail width";
public override string Description => "Rename contrail `TrailWidth` to `StartWidth` in traits and weapons to account for added `EndWidth` functionality";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var traitNode in actorNode.ChildrenMatching("Contrail"))
traitNode.RenameChildrenMatching("TrailWidth", "StartWidth");
yield break;
}
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{
foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile"))
traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth");
foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Bullet"))
traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth");
yield break;
}
}
}

View File

@@ -0,0 +1,31 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameEngineerRepair : UpdateRule
{
public override string Name => "Traits revolving around instant (building) repairs were renamed.";
public override string Description =>
"'EngineerRepair' was renamed to 'InstantlyRepairs' " +
"and 'EngineerRepairable' to 'InstantlyRepairable'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("EngineerRepair", "InstantlyRepairs");
actorNode.RenameChildrenMatching("EngineerRepairable", "InstantlyRepairable");
yield break;
}
}
}

View File

@@ -0,0 +1,30 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameMcvCrateAction : UpdateRule
{
public override string Name => "Rename 'GiveMcvCrateAction' to 'GiveBaseBuilderCrateAction'.";
public override string Description => "The 'GiveMcvCrateAction' has been renamed to 'GiveBaseBuilderCrateAction'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var node in actorNode.ChildrenMatching("GiveMcvCrateAction"))
node.RenameKey("GiveBaseBuilderCrateAction");
yield break;
}
}
}

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class TextNotificationsDisplayWidgetRemoveTime : UpdateRule
{
public override string Name => "Change name and unit of RemoveTime field of TextNotificationsDisplayWidget.";
public override string Description =>
"Change the field name from RemoveTime to DisplayDurationMs and convert the value from ticks to milliseconds";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{
if (!chromeNode.KeyMatches("TextNotificationsDisplay"))
yield break;
foreach (var field in chromeNode.ChildrenMatching("RemoveTime"))
{
field.RenameKey("DisplayDurationMs");
var durationMilliseconds = field.NodeValue<int>() * 40;
field.ReplaceValue(FieldSaver.FormatValue(durationMilliseconds));
}
}
}
}

View File

@@ -0,0 +1,166 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class AbstractDocking : UpdateRule, IBeforeUpdateActors
{
readonly string[] moveRefineyValues = ["DockAngle", "IsDragRequired", "DragOffset", "DragLength"];
readonly string[] moveHarvesterValues = ["EnterCursor", "EnterBlockedCursor"];
readonly string[] buildings = ["Building", "D2kBuilding"];
readonly string[,] moveAndRenameHarvesterValues = new string[4, 2]
{
{ "DeliverVoice", "Voice" },
{ "DeliverLineColor", "DockLineColor" },
{ "UnloadQueueCostModifier", "OccupancyCostModifier" },
{ "SearchForDeliveryBuildingDelay", "SearchForDockDelay" }
};
readonly Dictionary<string, List<MiniYamlNodeBuilder>> refineryNodes = [];
public override string Name => "Docking was abstracted from Refinery & Harvester.";
public override string Description =>
"Fields moved from Refinery to new trait DockHost, fields moved from Harvester to new trait DockClientManager and to DockHost";
public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
grid = modData.GetOrCreate<MapGrid>();
var harvesters = new Dictionary<string, HashSet<string>>();
var refineries = new List<string>();
foreach (var actorNode in resolvedActors)
{
var harvesterNode = actorNode.ChildrenMatching("Harvester", includeRemovals: false).FirstOrDefault();
if (harvesterNode != null)
harvesters[actorNode.Key] = harvesterNode.ChildrenMatching("DeliveryBuildings", includeRemovals: false)
.FirstOrDefault()?.NodeValue<HashSet<string>>() ?? [];
if (actorNode.HasChild("Refinery"))
refineries.Add(actorNode.Key.ToLowerInvariant());
}
foreach (var harvester in harvesters)
{
foreach (var deliveryBuildingHigh in harvester.Value)
{
var deliveryBuilding = deliveryBuildingHigh.ToLowerInvariant();
foreach (var refinery in refineries)
{
if (refinery == deliveryBuilding)
{
if (!refineryNodes.ContainsKey(refinery))
refineryNodes[refinery] = [];
var node = new MiniYamlNodeBuilder("Type", deliveryBuilding.ToString());
if (!refineryNodes[refinery].Any(n => n.Key == node.Key))
refineryNodes[refinery].Add(node);
}
}
}
}
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
var refineryNode = actorNode.ChildrenMatching("Refinery", includeRemovals: false).FirstOrDefault();
if (refineryNode != null)
{
var dockNode = new MiniYamlNodeBuilder("DockHost", "");
var lowActorName = actorNode.Key.ToLowerInvariant();
if (!refineryNodes.TryGetValue(lowActorName, out var nodes) || !nodes.Any(n => n.Key == "Type"))
dockNode.AddNode("Type", "Unload");
else
dockNode.AddNode(nodes.First(n => n.Key == "Type"));
foreach (var value in moveRefineyValues)
{
foreach (var node in refineryNode.ChildrenMatching(value).ToList())
{
dockNode.AddNode(node);
refineryNode.RemoveNode(node);
}
}
var oldOffset = CVec.Zero;
var dockOffsetNode = refineryNode.ChildrenMatching("DockOffset", includeRemovals: false).FirstOrDefault();
if (dockOffsetNode != null)
{
oldOffset = dockOffsetNode.NodeValue<CVec>();
refineryNode.RemoveNode(dockOffsetNode);
}
var buildingNode = actorNode.Value.Nodes.FirstOrDefault(n => buildings.Any(b => n.KeyMatches(b, includeRemovals: false)));
if (buildingNode != null)
{
var dimensions = buildingNode.ChildrenMatching("Dimensions", includeRemovals: false).FirstOrDefault()?.NodeValue<CVec>() ?? new CVec(1, 1);
var localCenterOffset = buildingNode.ChildrenMatching("LocalCenterOffset", includeRemovals: false).FirstOrDefault()?.NodeValue<WVec>() ?? WVec.Zero;
var offset = CenterOfCell(oldOffset) - CenterOfCell(CVec.Zero) - BuildingCenter(dimensions, localCenterOffset);
if (offset != WVec.Zero)
dockNode.AddNode("DockOffset", offset);
}
actorNode.AddNode(dockNode);
}
var harvesterNode = actorNode.ChildrenMatching("Harvester", includeRemovals: false).FirstOrDefault();
if (harvesterNode != null)
{
var dockClientNode = new MiniYamlNodeBuilder("DockClientManager", "");
foreach (var value in moveHarvesterValues)
{
foreach (var node in harvesterNode.ChildrenMatching(value).ToList())
{
dockClientNode.AddNode(node);
harvesterNode.RemoveNode(node);
}
}
for (var i = 0; i < moveAndRenameHarvesterValues.GetLength(0); i++)
{
foreach (var node in harvesterNode.ChildrenMatching(moveAndRenameHarvesterValues[i, 0]).ToList())
{
harvesterNode.RemoveNode(node);
node.RenameKey(moveAndRenameHarvesterValues[i, 1]);
dockClientNode.AddNode(node);
}
}
harvesterNode.RenameChildrenMatching("DeliveryBuildings", "DockType");
harvesterNode.RemoveNodes("MaxUnloadQueue");
actorNode.AddNode(dockClientNode);
}
yield break;
}
MapGrid grid;
public WVec CenterOfCell(CVec cell)
{
if (grid.Type == MapGridType.Rectangular)
return new WVec(1024 * cell.X + 512, 1024 * cell.Y + 512, 0);
return new WVec(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), 0);
}
public WVec BuildingCenter(CVec dimensions, WVec localCenterOffset)
{
return (CenterOfCell(dimensions) - CenterOfCell(new CVec(1, 1))) / 2 + localCenterOffset;
}
}
}

View File

@@ -0,0 +1,32 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class AddMarkerLayerOverlay : UpdateRule
{
public override string Name => "Add MarkerLayerOverlay.";
public override string Description =>
"Add MarkerLayerOverlay to editor.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
if (!actorNode.KeyMatches("EditorWorld") || actorNode.LastChildMatching("MarkerLayerOverlay") != null)
yield break;
var markerLayerOverlayNode = new MiniYamlNodeBuilder("MarkerLayerOverlay", new MiniYamlBuilder(""));
actorNode.AddNode(markerLayerOverlayNode);
}
}
}

View File

@@ -0,0 +1,38 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class AddSupportPowerBlockedCursor : UpdateRule
{
public override string Name => "Add SpawnActorPower/GrantExternalConditionPower BlockedCursor.";
public override string Description =>
"SpawnActorPower and GrantExternalConditionPower field BlockedCursor changed its default value.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var power in new List<string> { "SpawnActorPower", "GrantExternalConditionPower" })
{
foreach (var spawnActorPower in actorNode.ChildrenMatching(power))
{
var cursor = spawnActorPower.LastChildMatching("BlockedCursor");
if (cursor != null && !cursor.IsRemoval())
yield break;
var blockedCursorNode = new MiniYamlNodeBuilder("BlockedCursor", new MiniYamlBuilder("move-blocked"));
spawnActorPower.AddNode(blockedCursorNode);
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ExtractResourceStorageFromHarvester : UpdateRule
{
public override string Name => "Renames StoresResources to StoresPlayerResources and extracts StoresResources from Harvester.";
public override string Description =>
"Resource storage was extracted from Harvester. WithHarvesterPipsDecoration was also renamed to WithStoresResourcesPipsDecoration.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("StoresResources", "StoresPlayerResources");
actorNode.RenameChildrenMatching("WithHarvesterPipsDecoration", "WithStoresResourcesPipsDecoration");
var harvester = actorNode.LastChildMatching("Harvester", false);
if (harvester == null)
yield break;
var storesResources = new MiniYamlNodeBuilder("StoresResources", "");
var capacity = harvester.LastChildMatching("Capacity", false);
if (capacity != null)
{
storesResources.AddNode(capacity);
harvester.RemoveNode(capacity);
}
var resources = harvester.LastChildMatching("Resources", false);
if (resources != null)
storesResources.AddNode(resources);
actorNode.AddNode(storesResources);
}
}
}

View File

@@ -0,0 +1,42 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class MovePreviewFacing : UpdateRule
{
public override string Name => "Move map editor preview facing to EditorActorLayer";
public override string Description =>
"PreviewFacing property was moved from the EditorCursorLayer to the EditorActorLayer.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
var cursorLayer = actorNode.LastChildMatching("EditorCursorLayer");
if (cursorLayer == null || cursorLayer.IsRemoval())
yield break;
var node = cursorLayer.LastChildMatching("PreviewFacing");
cursorLayer.RemoveNodes("PreviewFacing");
if (node == null || node.IsRemoval())
yield break;
var actorLayer = actorNode.LastChildMatching("EditorActorLayer");
if (actorLayer != null && !actorLayer.IsRemoval())
{
node.RenameKey("DefaultActorFacing");
actorLayer.AddNode(node);
}
}
}
}

View File

@@ -0,0 +1,33 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveConyardChronoReturnAnimation : UpdateRule
{
public override string Name => "Remove Sequence and Body properties from ConyardChronoReturn.";
public override string Description => "These properties have been replaced with a dynamic vortex renderable.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var trait in actorNode.ChildrenMatching("ConyardChronoReturn"))
{
trait.RemoveNodes("Sequence");
trait.RemoveNodes("Body");
}
yield break;
}
}
}

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveEditorSelectionLayerProperties : UpdateRule
{
public override string Name => "Remove defunct properties from EditorSelectionLayer.";
public override string Description =>
"Map editor was refactored and many of EditorSelectionLayer properties were removed.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var editorSelectionLayer in actorNode.ChildrenMatching("EditorSelectionLayer"))
{
editorSelectionLayer.RemoveNodes("Palette");
editorSelectionLayer.RemoveNodes("FootprintAlpha");
editorSelectionLayer.RemoveNodes("Image");
editorSelectionLayer.RemoveNodes("CopySequence");
editorSelectionLayer.RemoveNodes("PasteSequence");
}
yield break;
}
}
}

View File

@@ -0,0 +1,51 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveParentTopParentLeftSubstitutions : UpdateRule
{
public override string Name => "Remove PARENT_TOP and PARENT_LEFT from integer expressions for widgets.";
public override string Description =>
"PARENT_TOP is replaced with 0 and PARENT_LEFT is replaced with 0 in integer expressions for width, height and position.";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{
var dimensionFields =
chromeNode.ChildrenMatching("Width")
.Concat(chromeNode.ChildrenMatching("Height"))
.Concat(chromeNode.ChildrenMatching("X"))
.Concat(chromeNode.ChildrenMatching("Y")).ToArray();
foreach (var field in dimensionFields)
{
if (field.Value.Value == "PARENT_TOP" || field.Value.Value == "PARENT_LEFT")
{
chromeNode.RemoveNode(field);
}
else if (field.Value.Value.Contains("PARENT_TOP"))
{
field.ReplaceValue(field.Value.Value.Replace("PARENT_TOP", "0"));
}
else if (field.Value.Value.Contains("PARENT_LEFT"))
{
field.ReplaceValue(field.Value.Value.Replace("PARENT_LEFT", "0"));
}
}
yield break;
}
}
}

View File

@@ -0,0 +1,45 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveValidRelationsFromCapturable : UpdateRule
{
public override string Name => "Remove ValidRelations property from Capturable.";
public override string Description => "ValidRelations has been moved from Capturable to Captures to match weapon definitions.";
readonly List<string> locations = [];
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (locations.Count > 0)
yield return Description + "\n" +
"ValidRelations have been removed from:\n" +
UpdateUtils.FormatMessageList(locations);
locations.Clear();
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var capturable in actorNode.ChildrenMatching("Capturable"))
{
if (capturable.RemoveNodes("ValidRelations") > 0)
locations.Add($"{actorNode.Key}: {capturable.Key} ({actorNode.Location.Name})");
}
yield break;
}
}
}

View File

@@ -0,0 +1,32 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameOnDeath : UpdateRule
{
public override string Name => "Rename Explodes to FireWarheadOnDeath and ThrowsShrapnel to FireProjectilesOnDeath.";
public override string Description =>
" The Explodes trait was renamed to FireWarheadsOnDeath." +
" The ThrowsShrapnel trait was renamed to FireProjectilesOnDeath.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("Explodes", "FireWarheadsOnDeath");
actorNode.RenameChildrenMatching("ThrowsShrapnel", "FireProjectilesOnDeath");
yield break;
}
}
}

View File

@@ -0,0 +1,45 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameWidgetSubstitutions : UpdateRule
{
public override string Name => "Rename *_RIGHT to *_WIDTH and *_BOTTOM to *_HEIGHT in integer expressions for widgets.";
public override string Description =>
"PARENT_RIGHT -> PARENT_WIDTH, PARENT_BOTTOM -> PARENT_HEIGHT, " +
"WINDOW_RIGHT -> WINDOW_WIDTH, WINDOW_BOTTOM -> WINDOW_HEIGHT " +
"in integer expressions for width, height and position.";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{
var dimensionFields =
chromeNode.ChildrenMatching("Width")
.Concat(chromeNode.ChildrenMatching("Height"))
.Concat(chromeNode.ChildrenMatching("X"))
.Concat(chromeNode.ChildrenMatching("Y")).ToArray();
foreach (var field in dimensionFields)
{
field.ReplaceValue(field.Value.Value.Replace("PARENT_RIGHT", "PARENT_WIDTH"));
field.ReplaceValue(field.Value.Value.Replace("PARENT_BOTTOM", "PARENT_HEIGHT"));
field.ReplaceValue(field.Value.Value.Replace("WINDOW_RIGHT", "WINDOW_WIDTH"));
field.ReplaceValue(field.Value.Value.Replace("WINDOW_BOTTOM", "WINDOW_HEIGHT"));
}
yield break;
}
}
}

View File

@@ -0,0 +1,64 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ReplaceCloakPalette : UpdateRule, IBeforeUpdateActors
{
public override string Name => "Change default Cloak style from Palette to Alpha.";
public override string Description =>
"Cloak has gained several new rendering modes\n" +
"and its default behaviour has changed from using a palette to native alpha.";
readonly List<(string, string)> actorsWithDefault = [];
IEnumerable<string> IBeforeUpdateActors.BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
foreach (var actor in resolvedActors)
foreach (var cloak in actor.ChildrenMatching("Cloak"))
if (cloak.HasChild("Palette"))
actorsWithDefault.Add((actor.Key, cloak.Key));
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var cloak in actorNode.ChildrenMatching("Cloak"))
{
if (actorsWithDefault.Any(pair => pair.Item1 == actorNode.Key && pair.Item2 == cloak.Key))
{
cloak.AddNode("CloakedPalette", "cloak");
cloak.AddNode("CloakStyle", "Palette");
yield break;
}
var palette = cloak.LastChildMatching("Palette", false);
if (palette != null)
{
if (string.IsNullOrEmpty(palette.Value.Value))
{
cloak.RemoveNode(palette);
cloak.AddNode("CloakStyle", "None");
}
else
{
palette.RenameKey("CloakedPalette");
cloak.AddNode("CloakStyle", "Palette");
}
}
}
}
}
}

View File

@@ -0,0 +1,49 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ReplacePaletteModifiers : UpdateRule
{
public override string Name => "Replace palette modifiers with post-processing shaders.";
public override string Description =>
"MenuPaletteEffect is renamed to MenuPostProcessEffect\n" +
"ChronoshiftPaletteEffect is renamed to ChronoshiftPostProcessEffect\n" +
"FlashPaletteEffect is renamed to FlashPostProcessEffect\n" +
"GlobalLightingPaletteEffect is renamed to TintPostProcessEffect\n" +
"D2kFogPalette is removed\n" +
"PaletteFromScaledPalette is removed";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("MenuPaletteEffect", "MenuPostProcessEffect");
actorNode.RenameChildrenMatching("ChronoshiftPaletteEffect", "ChronoshiftPostProcessEffect");
actorNode.RenameChildrenMatching("FlashPaletteEffect", "FlashPostProcessEffect");
actorNode.RenameChildrenMatching("GlobalLightingPaletteEffect", "TintPostProcessEffect");
actorNode.RemoveNodes("D2kFogPalette");
actorNode.RemoveNodes("PaletteFromScaledPalette");
yield break;
}
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{
foreach (var warheadNode in weaponNode.ChildrenMatching("Warhead"))
if (warheadNode.Value.Value == "FlashPaletteEffect")
warheadNode.Value.Value = "FlashEffect";
yield break;
}
}
}

View File

@@ -0,0 +1,44 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
/// <summary>
/// Adds color names to the editor history list.
/// </summary>
public class EditorMarkerTileLabels : UpdateRule, IBeforeUpdateActors
{
public override string Name => "Add labels to MarkerLayerOverlay colors.";
public override string Description => "Adds color names to the editor history list.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var layerNode in actorNode.ChildrenMatching("MarkerLayerOverlay"))
{
foreach (var colorsNode in layerNode.ChildrenMatching("Colors"))
{
var colors = FieldLoader.GetValue<Color[]>("Colors", colorsNode.Value.Value);
colorsNode.Value.Value = null;
foreach (var color in colors)
{
var c = FieldSaver.FormatValue(color);
colorsNode.AddNode("notification-added-marker-tiles-markers." + c, c);
}
}
}
yield break;
}
}
}

View File

@@ -0,0 +1,27 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemoveAlwaysVisible : UpdateRule
{
public override string Name => "Remove AlwaysVisible";
public override string Description => "AlwaysVisible trait was removed and now is the default behavior.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RemoveNodes("AlwaysVisible");
yield break;
}
}
}

View File

@@ -0,0 +1,32 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
sealed class RemoveBarracksTypesAndVehiclesTypesInBaseBuilderBotModule : UpdateRule
{
public override string Name => "Remove BarracksTypes and VehiclesTypes in BaseBuilderBotModule";
public override string Description => "BarracksTypes and VehiclesTypes were removed and now BaseBuilderBotModule check by using ProductionTypes.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var baseBuilder in actorNode.ChildrenMatching("BaseBuilderBotModule"))
{
baseBuilder.RemoveNodes("BarracksTypes");
baseBuilder.RemoveNodes("VehiclesFactoryTypes");
}
yield break;
}
}
}

View File

@@ -0,0 +1,68 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
/// <summary>
/// Replaces the BaseAttackNotifier with a new AttackNotifier that uses the
/// new attack system.
/// </summary>
public class RemoveBuildingInfoAllowPlacementOnResources : UpdateRule, IBeforeUpdateActors
{
public override string Name => "Remove AllowPlacementOnResources from BuildingInfo";
public override string Description => "Removes AllowPlacementOnResources from BuildingInfo and adds terrains with resources" +
"to TerrainTypes (if a Building trait uses AllowPlacementOnResources: true).";
readonly HashSet<string> terrainTypesWithResources = [];
IEnumerable<string> IBeforeUpdateActors.BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
var resourceLayerInfo = modData.DefaultRules.Actors[SystemActors.World].TraitInfoOrDefault<ResourceLayerInfo>();
if (resourceLayerInfo == null)
yield break;
foreach (var resourceType in resourceLayerInfo.ResourceTypes.Values)
{
terrainTypesWithResources.Add(resourceType.TerrainType);
}
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var buildingInfo in actorNode.ChildrenMatching("Building"))
{
if (buildingInfo.RemoveNodes("AllowPlacementOnResources") == 0)
continue;
var terrainTypesNode = buildingInfo.Value.NodeWithKeyOrDefault("TerrainTypes");
if (terrainTypesNode == null)
{
terrainTypesNode = new MiniYamlNodeBuilder("TerrainTypes", "");
buildingInfo.AddNode(terrainTypesNode);
}
var allowedTerrainTypes = terrainTypesNode?.NodeValue<List<string>>() ?? [];
foreach (var terrainType in terrainTypesWithResources)
{
if (!allowedTerrainTypes.Contains(terrainType))
allowedTerrainTypes.Add(terrainType);
}
terrainTypesNode.ReplaceValue(string.Join(", ", allowedTerrainTypes));
}
yield break;
}
}
}

View File

@@ -0,0 +1,63 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
/// <summary>
/// Replaces the BaseAttackNotifier with a new AttackNotifier that uses the
/// new attack system.
/// </summary>
public class ReplaceBaseAttackNotifier : UpdateRule, IBeforeUpdateActors
{
public override string Name => "Replace BaseAttackNotifier with DamageNotifier";
public override string Description => "Replaces the BaseAttackNotifier with a new DamageNotifier that uses BaseBuilding target type.";
bool any = false;
readonly HashSet<string> definedNotifications = [];
IEnumerable<string> IBeforeUpdateActors.BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
any = false;
foreach (var actor in resolvedActors)
foreach (var notifier in actor.ChildrenMatching("BaseAttackNotifier"))
if (notifier.LastChildMatching("Notification", false) != null)
definedNotifications.Add(actor.Key + ":" + notifier.Key);
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var notifier in actorNode.ChildrenMatching("BaseAttackNotifier"))
{
any = true;
notifier.AddNode("ValidTargets", "BaseBuilding");
if (!definedNotifications.Contains(actorNode.Key + ":" + notifier.Key))
notifier.AddNode("Notification", "BaseAttack");
notifier.RenameKey("DamageNotifier");
}
yield break;
}
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (!any)
yield break;
yield return "BaseAttackNotifier has been replaced by DamageNotifier with ValidTargets: BaseBuilding" +
"\nPlease replace the target type or add it to actor types as needed.";
}
}
}

View File

@@ -0,0 +1,64 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class WithDamageOverlayPropertyRename : UpdateRule, IBeforeUpdateActors
{
public override string Name => "Renamed `WithDamageOverlay`'s `IdleSequence` to `StartSequence`.";
public override string Description => "WithDamageOverlay's property `IdleSequence` was renamed to `StartSequence`"
+ " and functionality of WithDamageOverlay changed.";
readonly List<(string, string)> hasIdleDefault = [];
readonly List<(string, string)> hasEndDefault = [];
public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
foreach (var actorNode in resolvedActors)
foreach (var damage in actorNode.ChildrenMatching("WithDamageOverlay"))
if (damage.HasChild("IdleSequence") || damage.HasChild("StartSequence"))
hasIdleDefault.Add((actorNode.Key, damage.Key));
foreach (var actorNode in resolvedActors)
foreach (var damage in actorNode.ChildrenMatching("WithDamageOverlay"))
if (damage.HasChild("EndSequence"))
hasEndDefault.Add((actorNode.Key, damage.Key));
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var damage in actorNode.ChildrenMatching("WithDamageOverlay"))
{
var renamed = false;
foreach (var start in damage.ChildrenMatching("IdleSequence"))
{
start.RenameKey("StartSequence");
renamed = true;
}
if (!renamed && !hasIdleDefault.Contains((actorNode.Key, damage.Key)))
damage.AddNode("StartSequence", "idle");
damage.AddNode("LoopCount", 1);
if (!hasEndDefault.Contains((actorNode.Key, damage.Key)))
damage.AddNode("EndSequence", "end");
}
yield break;
}
}
}