void CheckActorReference(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary <string, ActorInfo> dict, ActorReferenceAttribute attribute) { var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); foreach (var value in values) { if (value == null) { continue; } // NOTE: Once https://github.com/OpenRA/OpenRA/issues/4124 is resolved we won't // have to .ToLower* anything here. var v = value.ToLowerInvariant(); if (!dict.ContainsKey(v)) { emitError("{0}.{1}.{2}: Missing actor `{3}`." .F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, value)); continue; } foreach (var requiredTrait in attribute.RequiredTraits) { if (!dict[v].TraitsInConstructOrder().Any(t => t.GetType() == requiredTrait || t.GetType().IsSubclassOf(requiredTrait))) { emitError("Actor type {0} does not have trait {1} which is required by {2}.{3}." .F(value, requiredTrait.Name, traitInfo.GetType().Name, fieldInfo.Name)); } } } }
public void Run(Action <string> emitError, Action <string> emitWarning, ModData modData, Ruleset rules) { var worldActor = rules.Actors["world"]; var locomotorInfos = worldActor.TraitInfos <LocomotorInfo>().ToArray(); foreach (var li in locomotorInfos) { foreach (var otherLocomotor in locomotorInfos) { if (li != otherLocomotor && li.Name == otherLocomotor.Name) { emitError("There is more than one Locomotor with name {0}!".F(li.Name)); } } } foreach (var actorInfo in rules.Actors) { foreach (var traitInfo in actorInfo.Value.TraitInfos <TraitInfo>()) { var fields = traitInfo.GetType().GetFields().Where(f => f.HasAttribute <LocomotorReferenceAttribute>()); foreach (var field in fields) { var locomotors = LintExts.GetFieldValues(traitInfo, field, emitError); foreach (var locomotor in locomotors) { if (string.IsNullOrEmpty(locomotor)) { continue; } CheckLocomotors(actorInfo.Value, emitError, rules, locomotorInfos, locomotor); } } } } }
public void Run(Action <string> emitError, Action <string> emitWarning, ModData modData, Map map) { if (map.SequenceDefinitions == null) { return; } this.emitError = emitError; sequenceDefinitions = MiniYaml.Load(map, modData.Manifest.Sequences, map.SequenceDefinitions); var rules = map.Rules; var factions = rules.Actors["world"].TraitInfos <FactionInfo>().Select(f => f.InternalName).ToArray(); var sequenceProviders = new[] { rules.Sequences }; foreach (var actorInfo in rules.Actors) { foreach (var renderInfo in actorInfo.Value.TraitInfos <RenderSpritesInfo>()) { foreach (var faction in factions) { foreach (var sequenceProvider in sequenceProviders) { var image = renderInfo.GetImage(actorInfo.Value, sequenceProvider, faction); 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)); } } } } foreach (var traitInfo in actorInfo.Value.TraitInfos <ITraitInfo>()) { var fields = traitInfo.GetType().GetFields(); foreach (var field in fields) { if (field.HasAttribute <SequenceReferenceAttribute>()) { var sequences = LintExts.GetFieldValues(traitInfo, field, emitError); foreach (var sequence in sequences) { if (string.IsNullOrEmpty(sequence)) { continue; } var renderInfo = actorInfo.Value.TraitInfos <RenderSpritesInfo>().FirstOrDefault(); if (renderInfo == null) { continue; } foreach (var faction in factions) { var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) { var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); if (imageField != null) { foreach (var imageOverride in LintExts.GetFieldValues(traitInfo, imageField, emitError)) { if (string.IsNullOrEmpty(imageOverride)) { emitWarning("Custom sprite image of actor {0} is null.".F(actorInfo.Value.Name)); continue; } if (sequenceDefinitions.All(s => s.Key != imageOverride.ToLowerInvariant())) { emitError("Custom sprite image {0} from actor {1} has no sequence definition.".F(imageOverride, actorInfo.Value.Name)); } else { CheckDefinitions(imageOverride, sequenceReference, actorInfo, sequence, faction, field, traitInfo); } } } } else { foreach (var sequenceProvider in sequenceProviders) { var image = renderInfo.GetImage(actorInfo.Value, sequenceProvider, faction); CheckDefinitions(image, sequenceReference, actorInfo, sequence, faction, field, traitInfo); } } } } } } } foreach (var weaponInfo in rules.Weapons) { var projectileInfo = weaponInfo.Value.Projectile; if (projectileInfo == null) { continue; } var fields = projectileInfo.GetType().GetFields(); foreach (var field in fields) { if (field.HasAttribute <SequenceReferenceAttribute>()) { var sequences = LintExts.GetFieldValues(projectileInfo, field, emitError); foreach (var sequence in sequences) { if (string.IsNullOrEmpty(sequence)) { continue; } var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) { var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); if (imageField != null) { foreach (var imageOverride in LintExts.GetFieldValues(projectileInfo, imageField, emitError)) { if (!string.IsNullOrEmpty(imageOverride)) { var definitions = sequenceDefinitions.FirstOrDefault(n => n.Key == imageOverride.ToLowerInvariant()); if (definitions == null) { emitError("Can't find sequence definition for projectile image {0} at weapon {1}.".F(imageOverride, weaponInfo.Key)); } else if (!definitions.Value.Nodes.Any(n => n.Key == sequence)) { emitError("Projectile sprite image {0} from weapon {1} does not define sequence {2} from field {3} of {4}" .F(imageOverride, weaponInfo.Key, sequence, field.Name, projectileInfo)); } } } } } } } } } } }
public void Run(Action <string> emitError, Action <string> emitWarning, Ruleset rules) { GetPalettes(emitError, rules); foreach (var actorInfo in rules.Actors) { foreach (var traitInfo in actorInfo.Value.TraitInfos <ITraitInfo>()) { var fields = traitInfo.GetType().GetFields(); foreach (var field in fields.Where(x => x.HasAttribute <PaletteReferenceAttribute>())) { var isPlayerPalette = false; var paletteReference = field.GetCustomAttributes <PaletteReferenceAttribute>(true).FirstOrDefault(); if (paletteReference != null) { if (!string.IsNullOrEmpty(paletteReference.PlayerPaletteReferenceSwitch)) { var fieldInfo = fields.First(f => f.Name == paletteReference.PlayerPaletteReferenceSwitch); isPlayerPalette = (bool)fieldInfo.GetValue(traitInfo); } else { isPlayerPalette = paletteReference.IsPlayerPalette; } } var references = LintExts.GetFieldValues(traitInfo, field, emitError); foreach (var reference in references) { if (string.IsNullOrEmpty(reference)) { continue; } if (isPlayerPalette) { if (!playerPalettes.Contains(reference)) { emitError("Undefined player palette reference {0} detected at {1} for {2}".F(reference, traitInfo, actorInfo.Key)); } } else { if (!palettes.Contains(reference)) { emitError("Undefined palette reference {0} detected at {1} for {2}".F(reference, traitInfo, actorInfo.Key)); } } } } } } foreach (var weaponInfo in rules.Weapons) { var projectileInfo = weaponInfo.Value.Projectile; if (projectileInfo == null) { continue; } var fields = projectileInfo.GetType().GetFields(); foreach (var field in fields.Where(x => x.HasAttribute <PaletteReferenceAttribute>())) { var isPlayerPalette = false; var paletteReference = field.GetCustomAttributes <PaletteReferenceAttribute>(true).First(); if (paletteReference != null) { if (!string.IsNullOrEmpty(paletteReference.PlayerPaletteReferenceSwitch)) { var fieldInfo = fields.First(f => f.Name == paletteReference.PlayerPaletteReferenceSwitch); isPlayerPalette = (bool)fieldInfo.GetValue(projectileInfo); } else { isPlayerPalette = paletteReference.IsPlayerPalette; } } var references = LintExts.GetFieldValues(projectileInfo, field, emitError); foreach (var reference in references) { if (string.IsNullOrEmpty(reference)) { continue; } if (isPlayerPalette) { if (!playerPalettes.Contains(reference)) { emitError("Undefined player palette reference {0} detected at weapon {1}.".F(reference, weaponInfo.Key)); } } else { if (!palettes.Contains(reference)) { emitError("Undefined palette reference {0} detected at weapon {1}.".F(reference, weaponInfo.Key)); } } } } } }
public void Run(Action <string> emitError, Action <string> emitWarning, Ruleset rules) { foreach (var actorInfo in rules.Actors) { if (actorInfo.Key.StartsWith("^", StringComparison.Ordinal)) { continue; } var granted = new HashSet <string>(); var consumed = new HashSet <string>(); foreach (var trait in actorInfo.Value.TraitInfos <ITraitInfo>()) { var fieldConsumed = trait.GetType().GetFields() .Where(x => x.HasAttribute <ConsumedConditionReferenceAttribute>()) .SelectMany(f => LintExts.GetFieldValues(trait, f, emitError)); var propertyConsumed = trait.GetType().GetProperties() .Where(x => x.HasAttribute <ConsumedConditionReferenceAttribute>()) .SelectMany(p => LintExts.GetPropertyValues(trait, p, emitError)); var fieldGranted = trait.GetType().GetFields() .Where(x => x.HasAttribute <GrantedConditionReferenceAttribute>()) .SelectMany(f => LintExts.GetFieldValues(trait, f, emitError)); var propertyGranted = trait.GetType().GetProperties() .Where(x => x.HasAttribute <GrantedConditionReferenceAttribute>()) .SelectMany(f => LintExts.GetPropertyValues(trait, f, emitError)); foreach (var c in fieldConsumed.Concat(propertyConsumed)) { if (!string.IsNullOrEmpty(c)) { consumed.Add(c); } } foreach (var g in fieldGranted.Concat(propertyGranted)) { if (!string.IsNullOrEmpty(g)) { granted.Add(g); } } } var unconsumed = granted.Except(consumed); if (unconsumed.Any()) { emitWarning("Actor type `{0}` grants conditions that are not consumed: {1}".F(actorInfo.Key, unconsumed.JoinWith(", "))); } var ungranted = consumed.Except(granted); if (ungranted.Any()) { emitError("Actor type `{0}` consumes conditions that are not granted: {1}".F(actorInfo.Key, ungranted.JoinWith(", "))); } if ((consumed.Any() || granted.Any()) && actorInfo.Value.TraitInfoOrDefault <ConditionManagerInfo>() == null) { emitError("Actor type `{0}` defines conditions but does not include a ConditionManager".F(actorInfo.Key)); } } }
public void Run(Action <string> emitError, Action <string> emitWarning, Map map) { this.emitWarning = emitWarning; sequenceDefinitions = MiniYaml.MergeLiberal(map.SequenceDefinitions, Game.ModData.Manifest.Sequences.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal)); var races = map.Rules.Actors["world"].Traits.WithInterface <FactionInfo>().Select(f => f.InternalName).ToArray(); foreach (var actorInfo in map.Rules.Actors) { foreach (var renderInfo in actorInfo.Value.Traits.WithInterface <RenderSpritesInfo>()) { foreach (var race in races) { var image = renderInfo.GetImage(actorInfo.Value, map.Rules.Sequences[map.Tileset], race); if (sequenceDefinitions.All(s => s.Key != image.ToLowerInvariant()) && !actorInfo.Value.Name.Contains("^")) { emitWarning("Sprite image {0} from actor {1} on tileset {2} using race {3} has no sequence definition." .F(image, actorInfo.Value.Name, map.Tileset, race)); } } } foreach (var traitInfo in actorInfo.Value.Traits.WithInterface <ITraitInfo>()) { var fields = traitInfo.GetType().GetFields(); foreach (var field in fields) { if (field.HasAttribute <SequenceReferenceAttribute>()) { var sequences = LintExts.GetFieldValues(traitInfo, field, emitError); foreach (var sequence in sequences) { if (string.IsNullOrEmpty(sequence)) { continue; } var renderInfo = actorInfo.Value.Traits.WithInterface <RenderSpritesInfo>().FirstOrDefault(); if (renderInfo == null) { continue; } foreach (var race in races) { var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) { var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); if (imageField != null) { foreach (var imageOverride in LintExts.GetFieldValues(traitInfo, imageField, emitError)) { if (!string.IsNullOrEmpty(imageOverride) && sequenceDefinitions.All(s => s.Key != imageOverride.ToLowerInvariant())) { emitWarning("Custom sprite image {0} from actor {1} has no sequence definition.".F(imageOverride, actorInfo.Value.Name)); } else { CheckDefintions(imageOverride, sequenceReference, actorInfo, sequence, race, field, traitInfo); } } } } else { var image = renderInfo.GetImage(actorInfo.Value, map.SequenceProvider, race); CheckDefintions(image, sequenceReference, actorInfo, sequence, race, field, traitInfo); } } } } } } } }
void Run(Action <string> emitError, Action <string> emitWarning, Ruleset rules, SequenceProvider sequences) { var factions = rules.Actors[SystemActors.World].TraitInfos <FactionInfo>().Select(f => f.InternalName).ToArray(); foreach (var actorInfo in rules.Actors) { // Actors may have 0 or 1 RenderSprites traits var renderInfo = actorInfo.Value.TraitInfoOrDefault <RenderSpritesInfo>(); if (renderInfo == null) { continue; } var images = new HashSet <string>() { renderInfo.GetImage(actorInfo.Value, sequences, null).ToLowerInvariant() }; // Some actors define faction-specific artwork foreach (var faction in factions) { images.Add(renderInfo.GetImage(actorInfo.Value, sequences, faction).ToLowerInvariant()); } foreach (var traitInfo in actorInfo.Value.TraitInfos <TraitInfo>()) { // 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 sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference == null) { continue; } // Some sequences may specify their own Image override IEnumerable <string> 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 `{0}` trait `{1}` must define a value for `{2}`".F(actorInfo.Value.Name, traitName, sequenceReference.ImageReference)); } continue; } sequenceImages = new[] { imageOverride.ToLowerInvariant() }; } foreach (var sequence in LintExts.GetFieldValues(traitInfo, field, emitError, sequenceReference.DictionaryReference)) { if (string.IsNullOrEmpty(sequence)) { continue; } foreach (var i in sequenceImages) { 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 `{0}` trait `{1}` field `{2}` defines a prefix `{3}` that does not match any sequences on image `{4}`.".F(actorInfo.Value.Name, traitName, field.Name, sequence, i)); } } else if (!sequences.HasSequence(i, sequence)) { emitError("Actor type `{0}` trait `{1}` field `{2}` references an undefined sequence `{3}` on image `{4}`.".F(actorInfo.Value.Name, traitName, field.Name, sequence, i)); } } } } } } foreach (var weaponInfo in rules.Weapons) { var projectileInfo = weaponInfo.Value.Projectile; if (projectileInfo == null) { continue; } var fields = projectileInfo.GetType().GetFields(); foreach (var field in fields) { var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference == null) { continue; } // All weapon sequences must specify their corresponding image var image = ((string)fields.First(f => f.Name == sequenceReference.ImageReference).GetValue(projectileInfo)); if (string.IsNullOrEmpty(image)) { if (!sequenceReference.AllowNullImage) { emitError("Weapon type `{0}` projectile field `{1}` must define a value".F(weaponInfo.Key, sequenceReference.ImageReference)); } continue; } image = image.ToLowerInvariant(); foreach (var sequence in LintExts.GetFieldValues(projectileInfo, field, emitError, sequenceReference.DictionaryReference)) { if (string.IsNullOrEmpty(sequence)) { continue; } if (sequenceReference.Prefix) { // TODO: Remove prefixed sequence references and instead use explicit lists of lintable references if (!sequences.Sequences(image).Any(s => s.StartsWith(sequence))) { emitWarning("Weapon type `{0}` projectile field `{1}` defines a prefix `{2}` that does not match any sequences on image `{3}`.".F(weaponInfo.Key, field.Name, sequence, image)); } } else if (!sequences.HasSequence(image, sequence)) { emitError("Weapon type `{0}` projectile field `{1}` references an undefined sequence `{2}` on image `{3}`.".F(weaponInfo.Key, field.Name, sequence, image)); } } } } }
public void Run(Action <string> emitError, Action <string> emitWarning, Map map) { if (map != null && !map.SequenceDefinitions.Any()) { return; } this.emitWarning = emitWarning; var sequenceSource = map != null ? map.SequenceDefinitions : new List <MiniYamlNode>(); sequenceDefinitions = MiniYaml.MergeLiberal(sequenceSource, Game.ModData.Manifest.Sequences.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal)); var rules = map == null ? Game.ModData.DefaultRules : map.Rules; var races = rules.Actors["world"].Traits.WithInterface <FactionInfo>().Select(f => f.InternalName).ToArray(); var sequenceProviders = map == null ? rules.Sequences.Values : new[] { rules.Sequences[map.Tileset] }; foreach (var actorInfo in rules.Actors) { foreach (var renderInfo in actorInfo.Value.Traits.WithInterface <RenderSpritesInfo>()) { foreach (var race in races) { foreach (var sequenceProvider in sequenceProviders) { var image = renderInfo.GetImage(actorInfo.Value, sequenceProvider, race); if (sequenceDefinitions.All(s => s.Key != image.ToLowerInvariant()) && !actorInfo.Value.Name.Contains("^")) { emitWarning("Sprite image {0} from actor {1} on tileset {2} using race {3} has no sequence definition." .F(image, actorInfo.Value.Name, map.Tileset, race)); } } } } foreach (var traitInfo in actorInfo.Value.Traits.WithInterface <ITraitInfo>()) { var fields = traitInfo.GetType().GetFields(); foreach (var field in fields) { if (field.HasAttribute <SequenceReferenceAttribute>()) { var sequences = LintExts.GetFieldValues(traitInfo, field, emitError); foreach (var sequence in sequences) { if (string.IsNullOrEmpty(sequence)) { continue; } var renderInfo = actorInfo.Value.Traits.WithInterface <RenderSpritesInfo>().FirstOrDefault(); if (renderInfo == null) { continue; } foreach (var race in races) { var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) { var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); if (imageField != null) { foreach (var imageOverride in LintExts.GetFieldValues(traitInfo, imageField, emitError)) { if (!string.IsNullOrEmpty(imageOverride) && sequenceDefinitions.All(s => s.Key != imageOverride.ToLowerInvariant())) { emitWarning("Custom sprite image {0} from actor {1} has no sequence definition.".F(imageOverride, actorInfo.Value.Name)); } else { CheckDefintions(imageOverride, sequenceReference, actorInfo, sequence, race, field, traitInfo); } } } } else { foreach (var sequenceProvider in sequenceProviders) { var image = renderInfo.GetImage(actorInfo.Value, sequenceProvider, race); CheckDefintions(image, sequenceReference, actorInfo, sequence, race, field, traitInfo); } } } } } } } foreach (var weaponInfo in rules.Weapons) { var projectileInfo = weaponInfo.Value.Projectile; if (projectileInfo == null) { continue; } var fields = projectileInfo.GetType().GetFields(); foreach (var field in fields) { if (field.HasAttribute <SequenceReferenceAttribute>()) { var sequences = LintExts.GetFieldValues(projectileInfo, field, emitError); foreach (var sequence in sequences) { if (string.IsNullOrEmpty(sequence)) { continue; } var sequenceReference = field.GetCustomAttributes <SequenceReferenceAttribute>(true).FirstOrDefault(); if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) { var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); if (imageField != null) { foreach (var imageOverride in LintExts.GetFieldValues(projectileInfo, imageField, emitError)) { if (!string.IsNullOrEmpty(imageOverride)) { var definitions = sequenceDefinitions.FirstOrDefault(n => n.Key == imageOverride.ToLowerInvariant()); if (definitions == null) { emitWarning("Can't find sequence definition for projectile image {0} at weapon {1}.".F(imageOverride, weaponInfo.Key)); } else if (!definitions.Value.Nodes.Any(n => n.Key == sequence)) { emitWarning("Projectile sprite image {0} from weapon {1} does not define sequence {2} from field {3} of {4}" .F(imageOverride, weaponInfo.Key, sequence, field.Name, projectileInfo)); } } } } } } } } } } }