diff --git a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs index cfa6bea57e..51ae5cb3da 100644 --- a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs +++ b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs @@ -33,24 +33,32 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var count = actorInfo.Value.TraitInfos().Count(); - - if (count == 0) - emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type!"); - else if (count > 1) - emitError($"Actor type `{actorInfo.Key}` defines multiple default visibility types!"); - else + // Catch TypeDictionary errors + try { - var vis = actorInfo.Value.TraitInfoOrDefault(); - if (vis != null && vis.Type == VisibilityType.Footprint) + var count = actorInfo.Value.TraitInfos().Count(); + + if (count == 0) + emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type!"); + else if (count > 1) + emitError($"Actor type `{actorInfo.Key}` defines multiple default visibility types!"); + else { - var ios = actorInfo.Value.TraitInfoOrDefault(); - if (ios == null) - emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but has no IOccupySpace traits!"); - else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0) - emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but does not have any footprint cells!"); + var vis = actorInfo.Value.TraitInfoOrDefault(); + if (vis != null && vis.Type == VisibilityType.Footprint) + { + var ios = actorInfo.Value.TraitInfoOrDefault(); + if (ios == null) + emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but has no IOccupySpace traits!"); + else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0) + emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but does not have any footprint cells!"); + } } } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } } } } diff --git a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs index 7093e69feb..89157d59e4 100644 --- a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs +++ b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs @@ -33,13 +33,21 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var health = actorInfo.Value.TraitInfoOrDefault(); - if (health == null) - continue; + // Catch TypeDictionary errors + try + { + var health = actorInfo.Value.TraitInfoOrDefault(); + if (health == null) + continue; - var hitShapes = actorInfo.Value.TraitInfos(); - if (!hitShapes.Any()) - emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait!"); + var hitShapes = actorInfo.Value.TraitInfos(); + if (!hitShapes.Any()) + emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait!"); + } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } } } } diff --git a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs index afcb63675f..f6ae8e8a26 100644 --- a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs +++ b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs @@ -32,16 +32,24 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var ios = actorInfo.Value.TraitInfoOrDefault(); - foreach (var rsi in actorInfo.Value.TraitInfos()) + // Catch TypeDictionary errors + try { - if (rsi.Type != VisibilityType.Footprint) - continue; + var ios = actorInfo.Value.TraitInfoOrDefault(); + foreach (var rsi in actorInfo.Value.TraitInfos()) + { + if (rsi.Type != VisibilityType.Footprint) + continue; - if (ios == null) - emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but has no IOccupySpace traits!"); - else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0) - emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but does not have any footprint cells!"); + if (ios == null) + emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but has no IOccupySpace traits!"); + else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0) + emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but does not have any footprint cells!"); + } + } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); } } } diff --git a/OpenRA.Mods.Common/Lint/CheckSequences.cs b/OpenRA.Mods.Common/Lint/CheckSequences.cs index 28468ea517..c3e277fb02 100644 --- a/OpenRA.Mods.Common/Lint/CheckSequences.cs +++ b/OpenRA.Mods.Common/Lint/CheckSequences.cs @@ -40,68 +40,76 @@ namespace OpenRA.Mods.Common.Lint var factions = rules.Actors[SystemActors.World].TraitInfos().Select(f => f.InternalName).ToArray(); foreach (var actorInfo in rules.Actors) { - var images = new HashSet(); - - // Actors may have 0 or 1 RenderSprites traits - var renderInfo = actorInfo.Value.TraitInfoOrDefault(); - if (renderInfo != null) + // Catch TypeDictionary errors + try { - images.Add(renderInfo.GetImage(actorInfo.Value, null).ToLowerInvariant()); + var images = new HashSet(); - // Some actors define faction-specific artwork - foreach (var faction in factions) - images.Add(renderInfo.GetImage(actorInfo.Value, faction).ToLowerInvariant()); - } - - foreach (var traitInfo in actorInfo.Value.TraitInfos()) - { - // Remove the "Info" suffix - var traitName = traitInfo.GetType().Name; - traitName = traitName.Remove(traitName.Length - 4); - - var fields = traitInfo.GetType().GetFields(); - foreach (var field in fields) + // Actors may have 0 or 1 RenderSprites traits + var renderInfo = actorInfo.Value.TraitInfoOrDefault(); + if (renderInfo != null) { - var sequenceReference = field.GetCustomAttributes(true).FirstOrDefault(); - if (sequenceReference == null) - continue; + images.Add(renderInfo.GetImage(actorInfo.Value, null).ToLowerInvariant()); - // Some sequences may specify their own Image override - IEnumerable sequenceImages = images; - if (!string.IsNullOrEmpty(sequenceReference.ImageReference)) + // Some actors define faction-specific artwork + foreach (var faction in factions) + images.Add(renderInfo.GetImage(actorInfo.Value, faction).ToLowerInvariant()); + } + + foreach (var traitInfo in actorInfo.Value.TraitInfos()) + { + // Remove the "Info" suffix + var traitName = traitInfo.GetType().Name; + traitName = traitName.Remove(traitName.Length - 4); + + var fields = traitInfo.GetType().GetFields(); + foreach (var field in fields) { - var imageField = fields.First(f => f.Name == sequenceReference.ImageReference); - var imageOverride = (string)imageField.GetValue(traitInfo); - if (string.IsNullOrEmpty(imageOverride)) - { - if (!sequenceReference.AllowNullImage) - emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` must define a value for `{sequenceReference.ImageReference}`"); - + var sequenceReference = field.GetCustomAttributes(true).FirstOrDefault(); + if (sequenceReference == null) continue; + + // Some sequences may specify their own Image override + IEnumerable sequenceImages = images; + if (!string.IsNullOrEmpty(sequenceReference.ImageReference)) + { + var imageField = fields.First(f => f.Name == sequenceReference.ImageReference); + var imageOverride = (string)imageField.GetValue(traitInfo); + if (string.IsNullOrEmpty(imageOverride)) + { + if (!sequenceReference.AllowNullImage) + emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` must define a value for `{sequenceReference.ImageReference}`"); + + continue; + } + + sequenceImages = new[] { imageOverride.ToLowerInvariant() }; } - sequenceImages = new[] { imageOverride.ToLowerInvariant() }; - } - - foreach (var sequence in LintExts.GetFieldValues(traitInfo, field, sequenceReference.DictionaryReference)) - { - if (string.IsNullOrEmpty(sequence)) - continue; - - foreach (var i in sequenceImages) + foreach (var sequence in LintExts.GetFieldValues(traitInfo, field, sequenceReference.DictionaryReference)) { - if (sequenceReference.Prefix) + if (string.IsNullOrEmpty(sequence)) + continue; + + foreach (var i in sequenceImages) { - // TODO: Remove prefixed sequence references and instead use explicit lists of lintable references - if (!sequences.Sequences(i).Any(s => s.StartsWith(sequence))) - emitWarning($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` defines a prefix `{sequence}` that does not match any sequences on image `{i}`."); + if (sequenceReference.Prefix) + { + // TODO: Remove prefixed sequence references and instead use explicit lists of lintable references + if (!sequences.Sequences(i).Any(s => s.StartsWith(sequence))) + emitWarning($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` defines a prefix `{sequence}` that does not match any sequences on image `{i}`."); + } + else if (!sequences.HasSequence(i, sequence)) + emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` references an undefined sequence `{sequence}` on image `{i}`."); } - else if (!sequences.HasSequence(i, sequence)) - emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` references an undefined sequence `{sequence}` on image `{i}`."); } } } } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } } foreach (var weaponInfo in rules.Weapons) diff --git a/OpenRA.Mods.Common/Lint/CheckTooltips.cs b/OpenRA.Mods.Common/Lint/CheckTooltips.cs index dbfa65b6c3..31faec5853 100644 --- a/OpenRA.Mods.Common/Lint/CheckTooltips.cs +++ b/OpenRA.Mods.Common/Lint/CheckTooltips.cs @@ -32,13 +32,21 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var buildable = actorInfo.Value.TraitInfoOrDefault(); - if (buildable == null) - continue; + // Catch TypeDictionary errors + try + { + var buildable = actorInfo.Value.TraitInfoOrDefault(); + if (buildable == null) + continue; - var tooltip = actorInfo.Value.TraitInfos().FirstOrDefault(info => info.EnabledByDefault); - if (tooltip == null) - emitError("The following buildable actor has no (enabled) Tooltip: " + actorInfo.Key); + var tooltip = actorInfo.Value.TraitInfos().FirstOrDefault(info => info.EnabledByDefault); + if (tooltip == null) + emitError("The following buildable actor has no (enabled) Tooltip: " + actorInfo.Key); + } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } } } } diff --git a/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs b/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs index 11412b853c..73082dbaab 100644 --- a/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs +++ b/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs @@ -33,14 +33,21 @@ namespace OpenRA.Mods.Common.Lint var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos().SelectMany(p => p.Prerequisites(a.Value))); // TODO: this check is case insensitive while the real check in-game is not - foreach (var i in rules.Actors) + foreach (var actorInfo in rules.Actors) { - var bi = i.Value.TraitInfoOrDefault(); - if (bi != null) - foreach (var prereq in bi.Prerequisites) - if (!prereq.StartsWith("~disabled")) - if (!providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", ""))) - emitError($"Buildable actor {i.Key} has prereq {prereq} not provided by anything."); + // Catch TypeDictionary errors + try + { + var bi = actorInfo.Value.TraitInfoOrDefault(); + if (bi != null) + foreach (var prereq in bi.Prerequisites) + if (!prereq.StartsWith("~disabled") && !providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", ""))) + emitError($"Buildable actor {actorInfo.Key} has prereq {prereq} not provided by anything."); + } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } } } }