diff --git a/OpenRA.Mods.Cnc/Graphics/ClassicSpriteSequence.cs b/OpenRA.Mods.Cnc/Graphics/ClassicSpriteSequence.cs index 900b9d3b148d..527f3fae2c33 100644 --- a/OpenRA.Mods.Cnc/Graphics/ClassicSpriteSequence.cs +++ b/OpenRA.Mods.Cnc/Graphics/ClassicSpriteSequence.cs @@ -29,25 +29,24 @@ public override ISpriteSequence CreateSequence(ModData modData, string tileSet, [Desc("A sprite sequence that has the oddities that come with first-generation Westwood titles.")] public class ClassicSpriteSequence : DefaultSpriteSequence { - // This needs to be a public property for the documentation generation to work. - [Desc("Incorporate a compensation factor due to the distortion caused by 3D-Studio " + - "when it tried to render 45% angles which was used by Westwood Studios at that time.")] - public bool UseClassicFacings { get; } + [Desc("Incorporate a compensation factor for the rotational distortion present in the first-generation Westwood games.")] + static readonly SpriteSequenceField UseClassicFacings = new SpriteSequenceField(nameof(UseClassicFacings), false); + readonly bool useClassicFacings; public ClassicSpriteSequence(ModData modData, string tileSet, SpriteCache cache, ISpriteSequenceLoader loader, string sequence, string animation, MiniYaml info) : base(modData, tileSet, cache, loader, sequence, animation, info) { var d = info.ToDictionary(); - UseClassicFacings = LoadField(d, nameof(UseClassicFacings), UseClassicFacings); + useClassicFacings = LoadField(d, UseClassicFacings); - if (UseClassicFacings && Facings != 32) + if (useClassicFacings && facings != 32) throw new InvalidOperationException( $"{info.Nodes[0].Location}: Sequence {sequence}.{animation}: UseClassicFacings is only valid for 32 facings"); } protected override int GetFacingFrameOffset(WAngle facing) { - return UseClassicFacings ? Util.ClassicIndexFacing(facing, Facings) : Common.Util.IndexFacing(facing, Facings); + return useClassicFacings ? Util.ClassicIndexFacing(facing, facings) : Common.Util.IndexFacing(facing, facings); } } } diff --git a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs index 5dadc18219f4..cf389a14453a 100644 --- a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; +using OpenRA.Mods.Common.Graphics; namespace OpenRA.Mods.Cnc.Graphics { @@ -45,21 +46,20 @@ public override ISpriteSequence CreateSequence(ModData modData, string tileSet, "that come with first-generation Westwood titles.")] public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence { - // These need to be public properties for the documentation generation to work. [Desc("Dictionary of with tileset name to override -> tileset name to use instead.")] - public static Dictionary TilesetOverrides => null; + static readonly SpriteSequenceField> TilesetOverrides = new SpriteSequenceField>(nameof(TilesetOverrides), null); [Desc("Use `TilesetCodes` as defined in `mod.yaml` to add a letter as a second character " + - "into the sprite filename like the Westwood 2.5D titles did for tileset-specific variants.")] - public static bool UseTilesetCode => false; + "into the sprite filename like the Westwood 2.5D titles did for tileset-specific variants.")] + static readonly SpriteSequenceField UseTilesetCode = new SpriteSequenceField(nameof(UseTilesetCode), false); [Desc("Append a tileset-specific extension to the file name " + - "- either as defined in `mod.yaml`'s `TilesetExtensions` (if `UseTilesetExtension` is used) " + - "or the default hardcoded one for this sequence type (.shp).")] - public static bool AddExtension => true; + "- either as defined in `mod.yaml`'s `TilesetExtensions` (if `UseTilesetExtension` is used) " + + "or the default hardcoded one for this sequence type (.shp).")] + static readonly SpriteSequenceField AddExtension = new SpriteSequenceField(nameof(AddExtension), true); [Desc("Whether `mod.yaml`'s `TilesetExtensions` should be used with the sequence's file name.")] - public static bool UseTilesetExtension { get; private set; } + static readonly SpriteSequenceField UseTilesetExtension = new SpriteSequenceField(nameof(UseTilesetExtension), false); public ClassicTilesetSpecificSpriteSequence(ModData modData, string tileSet, SpriteCache cache, ISpriteSequenceLoader loader, string sequence, string animation, MiniYaml info) : base(modData, tileSet, cache, loader, sequence, animation, info) { } @@ -82,16 +82,15 @@ protected override string GetSpriteSrc(ModData modData, string tileSet, string s var spriteName = sprite ?? sequence; - if (LoadField(d, nameof(UseTilesetCode), UseTilesetCode)) + if (LoadField(d, UseTilesetCode)) { if (loader.TilesetCodes.TryGetValue(ResolveTilesetId(tileSet, d), out var code)) spriteName = spriteName.Substring(0, 1) + code + spriteName.Substring(2, spriteName.Length - 2); } - if (LoadField(d, nameof(AddExtension), AddExtension)) + if (LoadField(d, AddExtension)) { - UseTilesetExtension = LoadField(d, nameof(UseTilesetExtension), UseTilesetExtension); - if (UseTilesetExtension && loader.TilesetExtensions.TryGetValue(ResolveTilesetId(tileSet, d), out var tilesetExtension)) + if (LoadField(d, UseTilesetExtension) && loader.TilesetExtensions.TryGetValue(ResolveTilesetId(tileSet, d), out var tilesetExtension)) return spriteName + tilesetExtension; return spriteName + loader.DefaultSpriteExtension; diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index 075b9df0d744..244cce8dbcbb 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -122,98 +122,131 @@ public FileNotFoundSequence(FileNotFoundException exception) float ISpriteSequence.GetAlpha(int frame) { throw exception; } } + public struct SpriteSequenceField + { + public string Key; + public T DefaultValue; + + public SpriteSequenceField(string key, T defaultValue) + { + Key = key; + DefaultValue = defaultValue; + } + } + [Desc("Generic sprite sequence implementation, mostly unencumbered with game- or artwork-specific logic.")] public class DefaultSpriteSequence : ISpriteSequence { - static readonly WDist DefaultShadowSpriteZOffset = new WDist(-5); protected Sprite[] sprites; readonly bool reverseFacings, transpose; readonly string sequence; protected readonly ISpriteSequenceLoader Loader; + public Rectangle Bounds { get; } + public string Name { get; } [Desc("Frame index to start from.")] - public int Start { get; private set; } + static readonly SpriteSequenceField Start = new SpriteSequenceField(nameof(Start), 0); + int ISpriteSequence.Start => start; + int start; [Desc("Number of frames to use. Does not have to be the total amount the sprite sheet has.")] - public int Length { get; private set; } = 1; + static readonly SpriteSequenceField Length = new SpriteSequenceField(nameof(Length), 1); + int ISpriteSequence.Length => length; + int length; - [Desc("Multiplier for the number of facings.")] - public int Stride { get; private set; } + [Desc("Overrides Length if a different number of frames is defined between facings.")] + static readonly SpriteSequenceField Stride = new SpriteSequenceField(nameof(Stride), -1); + int ISpriteSequence.Stride => stride; + int stride; [Desc("The amount of directions the unit faces. Use negative values to rotate counter-clockwise.")] - public int Facings { get; } = 1; + static readonly SpriteSequenceField Facings = new SpriteSequenceField(nameof(Facings), 1); + int ISpriteSequence.Facings => facings; + protected int facings; - [Desc("Time (in milliseconds) to wait until playing the next frame in the animation.")] - public int Tick { get; } = 40; + [Desc("Time (in milliseconds at default game speed) to wait until playing the next frame in the animation.")] + static readonly SpriteSequenceField Tick = new SpriteSequenceField(nameof(Tick), 40); + int ISpriteSequence.Tick => tick; + readonly int tick; [Desc("Value controlling the Z-order. A higher values means rendering on top of other sprites at the same position. " + - "Use power of 2 values to avoid glitches.")] - public int ZOffset { get; } + "Use power of 2 values to avoid glitches.")] + static readonly SpriteSequenceField ZOffset = new SpriteSequenceField(nameof(ZOffset), WDist.Zero); + int ISpriteSequence.ZOffset => zOffset; + readonly int zOffset; - [Desc("")] - public float ZRamp { get; } + [Desc("Additional sprite depth Z offset to apply as a function of sprite Y (0: vertical, 1: flat on terrain)")] + static readonly SpriteSequenceField ZRamp = new SpriteSequenceField(nameof(ZRamp), 0); [Desc("If the shadow is not part of the sprite, but baked into the same sprite sheet at a fixed offset, " + - "set this to the frame index where it starts.")] - public int ShadowStart { get; } = -1; + "set this to the frame index where it starts.")] + static readonly SpriteSequenceField ShadowStart = new SpriteSequenceField(nameof(ShadowStart), -1); + int ISpriteSequence.ShadowStart => shadowStart; + readonly int shadowStart; - [Desc("Set Z-Offset for the separate shadow. Used by the later Westwood 2.5D titles. Defined in WDist units!")] - public int ShadowZOffset { get; } + [Desc("Set Z-Offset for the separate shadow. Used by the later Westwood 2.5D titles.")] + static readonly SpriteSequenceField ShadowZOffset = new SpriteSequenceField(nameof(ShadowZOffset), new WDist(-5)); + int ISpriteSequence.ShadowZOffset => shadowZOffset; + readonly int shadowZOffset; [Desc("The individual frames to play instead of going through them sequentially from the `Start`.")] - public int[] Frames { get; private set; } - - public Rectangle Bounds { get; } + static readonly SpriteSequenceField Frames = new SpriteSequenceField(nameof(Frames), null); + int[] ISpriteSequence.Frames => frames; + int[] frames; [Desc("Don't apply terrain lighting or colored overlays.")] - public bool IgnoreWorldTint { get; } + static readonly SpriteSequenceField IgnoreWorldTint = new SpriteSequenceField(nameof(IgnoreWorldTint), false); + bool ISpriteSequence.IgnoreWorldTint => ignoreWorldTint; + readonly bool ignoreWorldTint; - [Desc("")] - public float Scale { get; } = 1f; + [Desc("Adjusts the rendered size of the sprite")] + static readonly SpriteSequenceField Scale = new SpriteSequenceField(nameof(Scale), 1); + float ISpriteSequence.Scale => scale; + readonly float scale; - // These need to be public properties for the documentation generation to work. [Desc("Play the sprite sequence back and forth.")] - public static bool Reverses => false; + static readonly SpriteSequenceField Reverses = new SpriteSequenceField(nameof(Reverses), false); [Desc("Support a frame order where each animation step is split per each direction.")] - public static bool Transpose => false; + static readonly SpriteSequenceField Transpose = new SpriteSequenceField(nameof(Transpose), false); [Desc("Mirror on the X axis.")] - public bool FlipX { get; } + static readonly SpriteSequenceField FlipX = new SpriteSequenceField(nameof(FlipX), false); [Desc("Mirror on the Y axis.")] - public bool FlipY { get; } + static readonly SpriteSequenceField FlipY = new SpriteSequenceField(nameof(FlipY), false); [Desc("Change the position in-game on X, Y, Z.")] - public float3 Offset { get; } = float3.Zero; + static readonly SpriteSequenceField Offset = new SpriteSequenceField(nameof(Offset), float3.Zero); [Desc("Apply an OpenGL/Photoshop inspired blend mode.")] - public BlendMode BlendMode { get; } = BlendMode.Alpha; + static readonly SpriteSequenceField BlendMode = new SpriteSequenceField(nameof(BlendMode), OpenRA.BlendMode.Alpha); [Desc("Allows to append multiple sequence definitions which are indented below this node " + - "like when offsets differ per frame or a sequence is spread across individual files.")] - public static object Combine => null; + "like when offsets differ per frame or a sequence is spread across individual files.")] + static readonly SpriteSequenceField Combine = new SpriteSequenceField(nameof(Combine), null); [Desc("Sets transparency - use one value to set for all frames or provide a value for each frame.")] - public float[] Alpha { get; } + static readonly SpriteSequenceField Alpha = new SpriteSequenceField(nameof(Alpha), null); + readonly float[] alpha; - [Desc("Plays a fade out effect.")] - public static bool AlphaFade => false; + [Desc("Fade the animation from fully opaque on the first frame to fully transparent after the last frame.")] + static readonly SpriteSequenceField AlphaFade = new SpriteSequenceField(nameof(AlphaFade), false); [Desc("Name of the file containing the depth data sprite.")] - public string DepthSprite { get; } + static readonly SpriteSequenceField DepthSprite = new SpriteSequenceField(nameof(DepthSprite), null); [Desc("Frame index containing the depth data.")] - public static int DepthSpriteFrame => 0; + static readonly SpriteSequenceField DepthSpriteFrame = new SpriteSequenceField(nameof(DepthSpriteFrame), 0); - [Desc("")] - public static float2 DepthSpriteOffset => float2.Zero; + [Desc("X, Y offset to apply to the depth sprite.")] + static readonly SpriteSequenceField DepthSpriteOffset = new SpriteSequenceField(nameof(DepthSpriteOffset), float2.Zero); [Desc("Make a custom palette embedded in the sprite available to the PaletteFromEmbeddedSpritePalette trait.")] - public static bool HasEmbeddedPalette => false; + static readonly SpriteSequenceField HasEmbeddedPalette = new SpriteSequenceField(nameof(HasEmbeddedPalette), false); public readonly uint[] EmbeddedPalette; @@ -230,6 +263,14 @@ protected static T LoadField(Dictionary d, string key, T fa return fallback; } + protected static T LoadField(Dictionary d, SpriteSequenceField field) + { + if (d.TryGetValue(field.Key, out var value)) + return FieldLoader.GetValue(field.Key, value.Value); + + return field.DefaultValue; + } + protected static Rectangle FlipRectangle(Rectangle rect, bool flipX, bool flipY) { var left = flipX ? rect.Right : rect.Left; @@ -249,89 +290,90 @@ public DefaultSpriteSequence(ModData modData, string tileSet, SpriteCache cache, try { - Start = LoadField(d, nameof(Start), 0); - ShadowStart = LoadField(d, nameof(ShadowStart), ShadowStart); - ShadowZOffset = LoadField(d, nameof(ShadowZOffset), DefaultShadowSpriteZOffset).Length; - ZOffset = LoadField(d, nameof(ZOffset), WDist.Zero).Length; - ZRamp = LoadField(d, nameof(ZRamp), 0f); - Tick = LoadField(d, nameof(Tick), Tick); - transpose = LoadField(d, nameof(Transpose), false); - Frames = LoadField(d, nameof(Frames), null); - IgnoreWorldTint = LoadField(d, nameof(IgnoreWorldTint), false); - Scale = LoadField(d, nameof(Scale), Scale); - - FlipX = LoadField(d, nameof(FlipX), false); - FlipY = LoadField(d, nameof(FlipY), false); - - Facings = LoadField(d, nameof(Facings), Facings); - if (Facings < 0) + start = LoadField(d, Start); + shadowStart = LoadField(d, ShadowStart); + shadowZOffset = LoadField(d, ShadowZOffset).Length; + zOffset = LoadField(d, ZOffset).Length; + tick = LoadField(d, Tick); + transpose = LoadField(d, Transpose); + frames = LoadField(d, Frames); + ignoreWorldTint = LoadField(d, IgnoreWorldTint); + scale = LoadField(d, Scale); + + var flipX = LoadField(d, FlipX); + var flipY = LoadField(d, FlipY); + var zRamp = LoadField(d, ZRamp); + + facings = LoadField(d, Facings); + if (facings < 0) { reverseFacings = true; - Facings = -Facings; + facings = -facings; } - Offset = LoadField(d, nameof(Offset), Offset); - BlendMode = LoadField(d, nameof(BlendMode), BlendMode); + var offset = LoadField(d, Offset); + var blendMode = LoadField(d, BlendMode); Func> getUsedFrames = frameCount => { - if (d.TryGetValue(nameof(Length), out var length) && length.Value == "*") - Length = Frames?.Length ?? frameCount - Start; + if (d.TryGetValue(Length.Key, out var lengthYaml) && lengthYaml.Value == "*") + length = frames?.Length ?? frameCount - start; else - Length = LoadField(d, nameof(Length), Length); + length = LoadField(d, Length); // Plays the animation forwards, and then in reverse - if (LoadField(d, nameof(Reverses), false)) + if (LoadField(d, Reverses)) { - var frames = Frames != null ? Frames.Skip(Start).Take(Length).ToArray() : Exts.MakeArray(Length, i => Start + i); - Frames = frames.Concat(frames.Skip(1).Take(Length - 2).Reverse()).ToArray(); - Length = 2 * Length - 2; - Start = 0; + var frames = this.frames != null ? this.frames.Skip(start).Take(length).ToArray() : Exts.MakeArray(length, i => start + i); + this.frames = frames.Concat(frames.Skip(1).Take(length - 2).Reverse()).ToArray(); + length = 2 * length - 2; + start = 0; } - Stride = LoadField(d, nameof(Stride), Length); + // Overrides Length with a custom stride + stride = LoadField(d, Stride.Key, length); - if (Length > Stride) + if (length > stride) throw new YamlException($"Sequence {sequence}.{animation}: Length must be <= stride"); - if (Frames != null && Length > Frames.Length) + if (frames != null && length > frames.Length) throw new YamlException($"Sequence {sequence}.{animation}: Length must be <= Frames.Length"); - var end = Start + (Facings - 1) * Stride + Length - 1; - if (Frames != null) + var end = start + (facings - 1) * stride + length - 1; + if (frames != null) { - foreach (var f in Frames) + foreach (var f in frames) if (f < 0 || f >= frameCount) - throw new YamlException($"Sequence {sequence}.{animation} defines a Frames override that references frame {f}, but only [{Start}..{end}] actually exist"); + throw new YamlException($"Sequence {sequence}.{animation} defines a Frames override that references frame {f}, but only [{start}..{end}] actually exist"); - if (Start < 0 || end >= Frames.Length) - throw new YamlException($"Sequence {sequence}.{animation} uses indices [{Start}..{end}] of the Frames list, but only {Frames.Length} frames are defined"); + if (start < 0 || end >= frames.Length) + throw new YamlException($"Sequence {sequence}.{animation} uses indices [{start}..{end}] of the Frames list, but only {frames.Length} frames are defined"); } - else if (Start < 0 || end >= frameCount) - throw new YamlException($"Sequence {sequence}.{animation} uses frames [{Start}..{end}], but only [0..{frameCount - 1}] actually exist"); + else if (start < 0 || end >= frameCount) + throw new YamlException($"Sequence {sequence}.{animation} uses frames [{start}..{end}], but only [0..{frameCount - 1}] actually exist"); - if (ShadowStart >= 0 && ShadowStart + (Facings - 1) * Stride + Length > frameCount) - throw new YamlException($"Sequence {sequence}.{animation}'s shadow frames use frames [{ShadowStart}..{ShadowStart + (Facings - 1) * Stride + Length - 1}], but only [0..{frameCount - 1}] actually exist"); + if (shadowStart >= 0 && shadowStart + (facings - 1) * stride + length > frameCount) + throw new YamlException($"Sequence {sequence}.{animation}'s shadow frames use frames [{shadowStart}..{shadowStart + (facings - 1) * stride + length - 1}], but only [0..{frameCount - 1}] actually exist"); var usedFrames = new List(); - for (var facing = 0; facing < Facings; facing++) + for (var facing = 0; facing < facings; facing++) { - for (var frame = 0; frame < Length; frame++) + for (var frame = 0; frame < length; frame++) { - var i = transpose ? (frame % Length) * Facings + facing : - (facing * Stride) + (frame % Length); + var i = transpose ? (frame % length) * facings + facing : + (facing * stride) + (frame % length); - usedFrames.Add(Frames != null ? Frames[i] : Start + i); + usedFrames.Add(frames != null ? frames[i] : start + i); } } - if (ShadowStart >= 0) - return usedFrames.Concat(usedFrames.Select(i => i + ShadowStart - Start)); + if (shadowStart >= 0) + return usedFrames.Concat(usedFrames.Select(i => i + shadowStart - start)); return usedFrames; }; - if (d.TryGetValue(nameof(Combine), out var combine)) + if (d.TryGetValue(Combine.Key, out var combine)) { var combined = Enumerable.Empty(); foreach (var sub in combine.Nodes) @@ -339,19 +381,20 @@ public DefaultSpriteSequence(ModData modData, string tileSet, SpriteCache cache, var sd = sub.Value.ToDictionary(); // Allow per-sprite offset, flipping, start, and length - var subStart = LoadField(sd, nameof(Start), 0); - var subOffset = LoadField(sd, nameof(Offset), Offset); - var subFlipX = LoadField(sd, nameof(FlipX), false); - var subFlipY = LoadField(sd, nameof(FlipY), false); - var subFrames = LoadField(sd, nameof(Frames), null); + // These shouldn't inherit Start/Offset/etc from the main definition + var subStart = LoadField(sd, Start); + var subOffset = LoadField(sd, Offset); + var subFlipX = LoadField(sd, FlipX); + var subFlipY = LoadField(sd, FlipY); + var subFrames = LoadField(sd, Frames); var subLength = 0; Func> subGetUsedFrames = subFrameCount => { - if (sd.TryGetValue(nameof(Length), out var subLengthYaml) && subLengthYaml.Value == "*") + if (sd.TryGetValue(Length.Key, out var subLengthYaml) && subLengthYaml.Value == "*") subLength = subFrames != null ? subFrames.Length : subFrameCount - subStart; else - subLength = LoadField(sd, nameof(Length), Length); + subLength = LoadField(sd, Length); return subFrames != null ? subFrames.Skip(subStart).Take(subLength) : Enumerable.Range(subStart, subLength); }; @@ -363,11 +406,11 @@ public DefaultSpriteSequence(ModData modData, string tileSet, SpriteCache cache, return null; var bounds = FlipRectangle(s.Bounds, subFlipX, subFlipY); - var dx = subOffset.X + Offset.X + (subFlipX ? -s.Offset.X : s.Offset.X); - var dy = subOffset.Y + Offset.Y + (subFlipY ? -s.Offset.Y : s.Offset.Y); - var dz = subOffset.Z + Offset.Z + s.Offset.Z + ZRamp * dy; + var dx = subOffset.X + offset.X + (subFlipX ? -s.Offset.X : s.Offset.X); + var dy = subOffset.Y + offset.Y + (subFlipY ? -s.Offset.Y : s.Offset.Y); + var dz = subOffset.Z + offset.Z + s.Offset.Z + zRamp * dy; - return new Sprite(s.Sheet, bounds, ZRamp, new float3(dx, dy, dz), s.Channel, BlendMode); + return new Sprite(s.Sheet, bounds, zRamp, new float3(dx, dy, dz), s.Channel, blendMode); }).ToList(); var frames = subFrames != null ? subFrames.Skip(subStart).Take(subLength).ToArray() : Exts.MakeArray(subLength, i => subStart + i); @@ -387,39 +430,39 @@ public DefaultSpriteSequence(ModData modData, string tileSet, SpriteCache cache, if (s == null) return null; - var bounds = FlipRectangle(s.Bounds, FlipX, FlipY); - var dx = Offset.X + (FlipX ? -s.Offset.X : s.Offset.X); - var dy = Offset.Y + (FlipY ? -s.Offset.Y : s.Offset.Y); - var dz = Offset.Z + s.Offset.Z + ZRamp * dy; + var bounds = FlipRectangle(s.Bounds, flipX, flipY); + var dx = offset.X + (flipX ? -s.Offset.X : s.Offset.X); + var dy = offset.Y + (flipY ? -s.Offset.Y : s.Offset.Y); + var dz = offset.Z + s.Offset.Z + zRamp * dy; - return new Sprite(s.Sheet, bounds, ZRamp, new float3(dx, dy, dz), s.Channel, BlendMode); + return new Sprite(s.Sheet, bounds, zRamp, new float3(dx, dy, dz), s.Channel, blendMode); }).ToArray(); } - Alpha = LoadField(d, nameof(Alpha), (float[])null); - if (Alpha != null) + alpha = LoadField(d, Alpha); + if (alpha != null) { - if (Alpha.Length == 1) - Alpha = Exts.MakeArray(Length, _ => Alpha[0]); - else if (Alpha.Length != Length) - throw new YamlException($"Sequence {sequence}.{animation} must define either 1 or {Length} Alpha values."); + if (alpha.Length == 1) + alpha = Exts.MakeArray(length, _ => alpha[0]); + else if (alpha.Length != length) + throw new YamlException($"Sequence {sequence}.{animation} must define either 1 or {length} Alpha values."); } - if (LoadField(d, nameof(AlphaFade), false)) + if (LoadField(d, AlphaFade)) { - if (Alpha != null) + if (alpha != null) throw new YamlException($"Sequence {sequence}.{animation} cannot define both AlphaFade and Alpha."); - Alpha = Exts.MakeArray(Length, i => float2.Lerp(1f, 0f, i / (Length - 1f))); + alpha = Exts.MakeArray(length, i => float2.Lerp(1f, 0f, i / (length - 1f))); } - DepthSprite = LoadField(d, nameof(DepthSprite), null); - if (!string.IsNullOrEmpty(DepthSprite)) + var depthSprite = LoadField(d, DepthSprite); + if (!string.IsNullOrEmpty(depthSprite)) { - var depthSpriteFrame = LoadField(d, nameof(DepthSpriteFrame), 0); - var depthOffset = LoadField(d, nameof(DepthSpriteOffset), DepthSpriteOffset); + var depthSpriteFrame = LoadField(d, DepthSpriteFrame); + var depthOffset = LoadField(d, DepthSpriteOffset); IEnumerable GetDepthFrame(int _) => new[] { depthSpriteFrame }; - var ds = cache[DepthSprite, GetDepthFrame][depthSpriteFrame]; + var ds = cache[depthSprite, GetDepthFrame][depthSpriteFrame]; sprites = sprites.Select(s => { @@ -436,21 +479,20 @@ public DefaultSpriteSequence(ModData modData, string tileSet, SpriteCache cache, }).ToArray(); } - var hasEmbeddedPalette = LoadField(d, nameof(HasEmbeddedPalette), HasEmbeddedPalette); - if (hasEmbeddedPalette) + if (LoadField(d, HasEmbeddedPalette)) { var src = GetSpriteSrc(modData, tileSet, sequence, animation, info.Value, d); var metadata = cache.FrameMetadata(src); - var i = Frames != null ? Frames[0] : Start; + var i = frames != null ? frames[0] : start; var palettes = metadata?.GetOrDefault(); if (palettes == null || !palettes.TryGetPaletteForFrame(i, out EmbeddedPalette)) throw new YamlException($"Cannot export palette from {src}: frame {i} does not define an embedded palette"); } - var boundSprites = SpriteBounds(sprites, Frames, Start, Facings, Length, Stride, transpose); - if (ShadowStart > 0) - boundSprites = boundSprites.Concat(SpriteBounds(sprites, Frames, ShadowStart, Facings, Length, Stride, transpose)); + var boundSprites = SpriteBounds(sprites, frames, start, facings, length, stride, transpose); + if (shadowStart > 0) + boundSprites = boundSprites.Concat(SpriteBounds(sprites, frames, shadowStart, facings, length, stride, transpose)); Bounds = boundSprites.Union(); } @@ -481,29 +523,29 @@ static IEnumerable SpriteBounds(Sprite[] sprites, int[] frames, int s public Sprite GetSprite(int frame) { - return GetSprite(Start, frame, WAngle.Zero); + return GetSprite(start, frame, WAngle.Zero); } public Sprite GetSprite(int frame, WAngle facing) { - return GetSprite(Start, frame, facing); + return GetSprite(start, frame, facing); } public Sprite GetShadow(int frame, WAngle facing) { - return ShadowStart >= 0 ? GetSprite(ShadowStart, frame, facing) : null; + return shadowStart >= 0 ? GetSprite(shadowStart, frame, facing) : null; } protected virtual Sprite GetSprite(int start, int frame, WAngle facing) { var f = GetFacingFrameOffset(facing); if (reverseFacings) - f = (Facings - f) % Facings; + f = (facings - f) % facings; - var i = transpose ? (frame % Length) * Facings + f : - (f * Stride) + (frame % Length); + var i = transpose ? (frame % length) * facings + f : + (f * stride) + (frame % length); - var j = Frames != null ? Frames[i] : start + i; + var j = frames != null ? frames[i] : start + i; if (sprites[j] == null) throw new InvalidOperationException($"Attempted to query unloaded sprite from {Name}.{sequence} start={start} frame={frame} facing={facing}"); @@ -512,12 +554,12 @@ protected virtual Sprite GetSprite(int start, int frame, WAngle facing) protected virtual int GetFacingFrameOffset(WAngle facing) { - return Util.IndexFacing(facing, Facings); + return Util.IndexFacing(facing, facings); } public virtual float GetAlpha(int frame) { - return Alpha?[frame] ?? 1f; + return alpha?[frame] ?? 1f; } } } diff --git a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs index 875420051f56..b451686711af 100644 --- a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs @@ -44,21 +44,20 @@ public override ISpriteSequence CreateSequence(ModData modData, string tileSet, [Desc("A sprite sequence that can have tileset-specific variants.")] public class TilesetSpecificSpriteSequence : DefaultSpriteSequence { - // These need to be public properties for the documentation generation to work. [Desc("Dictionary of with tileset name to override -> tileset name to use instead.")] - public static Dictionary TilesetOverrides => null; + static readonly SpriteSequenceField> TilesetOverrides = new SpriteSequenceField>(nameof(TilesetOverrides), null); [Desc("Use `TilesetCodes` as defined in `mod.yaml` to add a letter as a second character " + - "into the sprite filename like the Westwood 2.5D titles did for tileset-specific variants.")] - public static bool UseTilesetCode => false; + "into the sprite filename like the Westwood 2.5D titles did for tileset-specific variants.")] + static readonly SpriteSequenceField UseTilesetCode = new SpriteSequenceField(nameof(UseTilesetCode), false); [Desc("Append a tileset-specific extension to the file name " + - "- either as defined in `mod.yaml`'s `TilesetExtensions` (if `UseTilesetExtension` is used) " + - "or the default hardcoded one for this sequence type (.shp).")] - public static bool AddExtension => true; + "- either as defined in `mod.yaml`'s `TilesetExtensions` (if `UseTilesetExtension` is used) " + + "or the default hardcoded one for this sequence type (.shp).")] + static readonly SpriteSequenceField AddExtension = new SpriteSequenceField(nameof(AddExtension), true); [Desc("Whether `mod.yaml`'s `TilesetExtensions` should be used with the sequence's file name.")] - public static bool UseTilesetExtension { get; private set; } + static readonly SpriteSequenceField UseTilesetExtension = new SpriteSequenceField(nameof(UseTilesetExtension), false); public TilesetSpecificSpriteSequence(ModData modData, string tileSet, SpriteCache cache, ISpriteSequenceLoader loader, string sequence, string animation, MiniYaml info) : base(modData, tileSet, cache, loader, sequence, animation, info) { } @@ -81,16 +80,15 @@ protected override string GetSpriteSrc(ModData modData, string tileSet, string s var spriteName = sprite ?? sequence; - if (LoadField(d, nameof(UseTilesetCode), UseTilesetCode)) + if (LoadField(d, UseTilesetCode)) { if (loader.TilesetCodes.TryGetValue(ResolveTilesetId(tileSet, d), out var code)) spriteName = spriteName.Substring(0, 1) + code + spriteName.Substring(2, spriteName.Length - 2); } - if (LoadField(d, nameof(AddExtension), AddExtension)) + if (LoadField(d, AddExtension)) { - UseTilesetExtension = LoadField(d, nameof(UseTilesetExtension), UseTilesetExtension); - if (UseTilesetExtension && loader.TilesetExtensions.TryGetValue(ResolveTilesetId(tileSet, d), out var tilesetExtension)) + if (LoadField(d, UseTilesetExtension) && loader.TilesetExtensions.TryGetValue(ResolveTilesetId(tileSet, d), out var tilesetExtension)) return spriteName + tilesetExtension; return spriteName + loader.DefaultSpriteExtension;