diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index ce16bd53c0..132e03c182 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -22,6 +22,8 @@ namespace OpenRA /// public class ActorInfo { + public const string AbstractActorPrefix = "^"; + /// /// The actor name can be anything, but the sprites used in the Render*: traits default to this one. /// If you add an ^ in front of the name, the engine will recognize this as a collection of traits @@ -38,7 +40,6 @@ namespace OpenRA { Name = name; - var abstractActorType = name.StartsWith("^"); foreach (var t in node.Nodes) { try @@ -47,8 +48,7 @@ namespace OpenRA } catch (FieldLoader.MissingFieldsException e) { - if (!abstractActorType) - throw new YamlException(e.Message); + throw new YamlException(e.Message); } } diff --git a/OpenRA.Game/GameRules/Ruleset.cs b/OpenRA.Game/GameRules/Ruleset.cs index b424d6fe17..31536e0d2d 100644 --- a/OpenRA.Game/GameRules/Ruleset.cs +++ b/OpenRA.Game/GameRules/Ruleset.cs @@ -87,16 +87,24 @@ namespace OpenRA public IEnumerable> InstalledMusic { get { return Music.Where(m => m.Value.Exists); } } - static IReadOnlyDictionary MergeOrDefault(string name, IReadOnlyFileSystem fileSystem, IEnumerable files, MiniYaml additional, - IReadOnlyDictionary defaults, Func makeObject) + static IReadOnlyDictionary MergeOrDefault(string name, + IReadOnlyFileSystem fileSystem, + IEnumerable files, + MiniYaml additional, + IReadOnlyDictionary defaults, + Func makeObject, + Func filterNode = null) { if (additional == null && defaults != null) return defaults; - var result = MiniYaml.Load(fileSystem, files, additional) - .ToDictionaryWithConflictLog(k => k.Key.ToLowerInvariant(), makeObject, "LoadFromManifest<" + name + ">"); + IEnumerable yamlNodes = MiniYaml.Load(fileSystem, files, additional); - return new ReadOnlyDictionary(result); + // Optionally, the caller can filter out elements from the loaded set of nodes. Default behavior is unfiltered. + if (filterNode != null) + yamlNodes = yamlNodes.Where(k => !filterNode(k)); + + return new ReadOnlyDictionary(yamlNodes.ToDictionaryWithConflictLog(k => k.Key.ToLowerInvariant(), makeObject, "LoadFromManifest<" + name + ">")); } public static Ruleset LoadDefaults(ModData modData) @@ -108,7 +116,8 @@ namespace OpenRA Action f = () => { var actors = MergeOrDefault("Manifest,Rules", fs, m.Rules, null, null, - k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value)); + k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value), + filterNode: n => n.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal)); var weapons = MergeOrDefault("Manifest,Weapons", fs, m.Weapons, null, null, k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value)); @@ -166,7 +175,8 @@ namespace OpenRA Action f = () => { var actors = MergeOrDefault("Rules", fileSystem, m.Rules, mapRules, dr.Actors, - k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value)); + k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value), + filterNode: n => n.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal)); var weapons = MergeOrDefault("Weapons", fileSystem, m.Weapons, mapWeapons, dr.Weapons, k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value)); @@ -244,7 +254,7 @@ namespace OpenRA MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications, MiniYaml mapSequences) { // Maps that define any weapon, voice, notification, or sequence overrides are always flagged - if (AnyCustomYaml(mapWeapons) || AnyCustomYaml(mapVoices) || AnyCustomYaml(mapNotifications) || AnyCustomYaml(mapSequences)) + if (AnyCustomYaml(mapWeapons) || AnyCustomYaml(mapVoices) || AnyCustomYaml(mapNotifications) || AnyCustomYaml(mapSequences)) return true; // Any trait overrides that aren't explicitly whitelisted are flagged diff --git a/OpenRA.Mods.Common/Lint/CheckConditions.cs b/OpenRA.Mods.Common/Lint/CheckConditions.cs index d8b2f4f849..807670169d 100644 --- a/OpenRA.Mods.Common/Lint/CheckConditions.cs +++ b/OpenRA.Mods.Common/Lint/CheckConditions.cs @@ -23,9 +23,6 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - if (actorInfo.Key.StartsWith("^", StringComparison.Ordinal)) - continue; - var granted = new HashSet(); var consumed = new HashSet(); diff --git a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs index c49f5371b2..0ca6fe049f 100644 --- a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs +++ b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs @@ -22,9 +22,6 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - if (actorInfo.Key.StartsWith("^")) - continue; - var count = actorInfo.Value.TraitInfos().Count(); if (count == 0) diff --git a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs index 9c840f926d..2e8668e73c 100644 --- a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs +++ b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs @@ -22,9 +22,6 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - if (actorInfo.Key.StartsWith("^", StringComparison.Ordinal)) - continue; - var health = actorInfo.Value.TraitInfoOrDefault(); if (health == null) continue; diff --git a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs index 7a1271974f..dd226ad583 100644 --- a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs +++ b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs @@ -22,9 +22,6 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - if (actorInfo.Key.StartsWith("^", StringComparison.Ordinal)) - continue; - var ios = actorInfo.Value.TraitInfoOrDefault(); foreach (var rsi in actorInfo.Value.TraitInfos()) { diff --git a/OpenRA.Mods.Common/Lint/CheckSequences.cs b/OpenRA.Mods.Common/Lint/CheckSequences.cs index 6763f83179..1fb7e366a2 100644 --- a/OpenRA.Mods.Common/Lint/CheckSequences.cs +++ b/OpenRA.Mods.Common/Lint/CheckSequences.cs @@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.Lint foreach (var sequenceProvider in sequenceProviders) { var image = renderInfo.GetImage(actorInfo.Value, sequenceProvider, faction); - if (sequenceDefinitions.All(s => s.Key != image.ToLowerInvariant()) && !actorInfo.Value.Name.Contains("^")) + if (sequenceDefinitions.All(s => s.Key != image.ToLowerInvariant())) emitError("Sprite image {0} from actor {1} using faction {2} has no sequence definition." .F(image, actorInfo.Value.Name, faction)); } diff --git a/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs b/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs index 3a9eaa1ef7..c5fa93fc4a 100644 --- a/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs +++ b/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs @@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Lint { public void Run(Action emitError, Action emitWarning, Ruleset rules) { - foreach (var actorInfo in rules.Actors.Where(a => !a.Key.StartsWith("^"))) + foreach (var actorInfo in rules.Actors) { try { diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckSequenceSprites.cs b/OpenRA.Mods.Common/UtilityCommands/CheckSequenceSprites.cs index e27af8a935..824264617b 100644 --- a/OpenRA.Mods.Common/UtilityCommands/CheckSequenceSprites.cs +++ b/OpenRA.Mods.Common/UtilityCommands/CheckSequenceSprites.cs @@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.UtilityCommands Console.WriteLine("Tileset: " + ts.Name); var sc = new SpriteCache(modData.DefaultFileSystem, modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed)); var nodes = MiniYaml.Merge(modData.Manifest.Sequences.Select(s => MiniYaml.FromStream(modData.DefaultFileSystem.Open(s), s))); - foreach (var n in nodes.Where(node => !node.Key.StartsWith("^"))) + foreach (var n in nodes.Where(node => !node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))) modData.SpriteSequenceLoader.ParseSequences(modData, ts, sc, n); }