예제 #1
0
        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));
                    }
                }
            }
        }
예제 #2
0
        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);
                        }
                    }
                }
            }
        }
예제 #3
0
        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));
                            }
                        }
                    }
                }
            }
        }
예제 #5
0
        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));
                }
            }
        }
예제 #6
0
        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);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #7
0
        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));
                        }
                    }
                }
            }
        }
예제 #8
0
        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));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }