public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache, ISpriteSequenceLoader loader, string sequence, string animation, MiniYaml info) { Name = animation; Loader = loader; //Console.WriteLine("Sequence:" + sequence + " animation:" + animation); var d = info.ToDictionary(); try { Start = LoadField(d, "Start", 0); ShadowStart = LoadField(d, "ShadowStart", -1); ShadowZOffset = LoadField(d, "ShadowZOffset", DefaultShadowSpriteZOffset).Length; ZOffset = LoadField(d, "ZOffset", WDist.Zero).Length; ZRamp = LoadField(d, "ZRamp", 0); Tick = LoadField(d, "Tick", 40); transpose = LoadField(d, "Transpose", false); Frames = LoadField <int[]>(d, "Frames", null); useClassicFacingFudge = LoadField(d, "UseClassicFacingFudge", false); var flipX = LoadField(d, "FlipX", false); var flipY = LoadField(d, "FlipY", false); Facings = LoadField(d, "Facings", 1); if (Facings < 0) { reverseFacings = true; Facings = -Facings; } if (useClassicFacingFudge && Facings != 32) { throw new InvalidDataException("{0}: Sequence {1}.{2}: UseClassicFacingFudge is only valid for 32 facings".F(info.Nodes[0].Location, sequence, animation)); } var offset = LoadField(d, "Offset", Vector3.Zero); var blendmode = LoadField(d, "BlendMode", BlendMode.Alpha); MiniYaml combine; if (d.TryGetValue("Combine", out combine)) { var combined = Enumerable.Empty <Sprite>(); foreach (var sub in combine.Nodes) { var sd = sub.Value.ToDictionary(); //Allow per-sprite offset,flipping,start,and length var subStart = LoadField(sd, "Start", 0); var subOffset = LoadField(sd, "Offset", Vector3.Zero); var subFlipX = LoadField(sd, "FlipX", false); var subFlipY = LoadField(sd, "FlipY", false); var subSrc = GetSpriteSrc(modData, tileSet, sequence, animation, sub.Key, sd); var subSprites = cache[subSrc].Select(s => new Sprite(s.Sheet, FlipRectangle(s.Bounds, subFlipX, subFlipY), ZRamp, new Vector3(subFlipX?-s.Offset.X:s.Offset.X, subFlipY?-s.Offset.Y:s.Offset.Y, s.Offset.Z) + subOffset + offset, s.Channel, blendmode)); var subLength = 0; MiniYaml subLengthYaml; if (sd.TryGetValue("Length", out subLengthYaml) && subLengthYaml.Value == "*") { subLength = subSprites.Count() - subStart; } else { subLength = LoadField(sd, "Length", 1); } combined = combined.Concat(subSprites.Skip(subStart).Take(subLength)); } sprites = combined.ToArray(); } else { //Apply offset to each sprite in the sequence //Different sequences may apply different offsets to the same frame. //对序列中的每个子画面应用偏移,不同的序列可以对同一帧应用不同的偏移 var src = GetSpriteSrc(modData, tileSet, sequence, animation, info.Value, d); sprites = cache[src].Select(s => new Sprite(s.Sheet, FlipRectangle(s.Bounds, flipX, flipY), ZRamp, new Vector3(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + offset, s.Channel, blendmode)).ToArray(); } var depthSprite = LoadField <string>(d, "DepthSprite", null); if (!string.IsNullOrEmpty(depthSprite)) { //Console.WriteLine("DepthSprite:" + depthSprite); var depthSpriteFrame = LoadField(d, "DepthSpriteFrame", 0); var depthOffset = LoadField(d, "DepthSpriteOffset", Vector2.Zero); var depthSprites = cache.AllCached(depthSprite).Select(s => s[depthSpriteFrame]); sprites = sprites.Select(s => { //The depth sprite must be live on the same sheet as the main sprite var ds = depthSprites.FirstOrDefault(dss => dss.Sheet == s.Sheet); if (ds == null) { //The sequence has probably overflowed onto a new sheet. //Allocating a new depth sprite on this sheet will almost certainly work //该序列可能已经溢出到新的Sheet上, ds = cache.Reload(depthSprite)[depthSpriteFrame]; depthSprites = cache.AllCached(depthSprite).Select(ss => ss[depthSpriteFrame]); if (ds.Sheet != s.Sheet) { throw new SheetOverflowException("Cross-sheet depth sprite reference:{0}.{1}: {2}"); } } var cw = (ds.Bounds.Left + ds.Bounds.Right) / 2 + (int)(s.Offset.X + depthOffset.X); var ch = (ds.Bounds.Top + ds.Bounds.Bottom) / 2 + (int)(s.Offset.Y + depthOffset.Y); var w = s.Bounds.Width / 2; var h = s.Bounds.Height / 2; var r = Rectangle.FromLTRB(cw - w, ch - h, cw + w, ch + h); return(new SpriteWithSecondaryData(s, r, ds.Channel)); }).ToArray(); } MiniYaml length; if (d.TryGetValue("Length", out length) && length.Value == "*") { Length = sprites.Length - Start; } else { Length = LoadField(d, "Length", 1); } //Plays the animation forwards,and then in reverse if (LoadField(d, "Reverses", false)) { var frames = Frames ?? Exts.MakeArray(Length, i => Start + i); Frames = frames.Concat(frames.Skip(1).Take(frames.Length - 2).Reverse()).ToArray(); Length = 2 * Length - 2; } Stride = LoadField(d, "Stride", Length); if (Length > Stride) { throw new InvalidOperationException("{0}:Sequence {1}.{2}: Length must be <= Stride".F(info.Nodes[0].Location, sequence, animation)); } if (Frames != null && Length > Frames.Length) { throw new InvalidOperationException("{0}:Sequence {1}.{2}: Length must be <=Frames.Length".F(info.Nodes[0].Location, sequence, animation)); } if (Start < 0 || Start + Facings * Stride > sprites.Length) { throw new InvalidOperationException("{5}: Sequence {0}.{1} uses frames [{2}...{3}],but only 0..{4} actually exist".F(sequence, animation, Start, Start + Facings * Stride - 1, sprites.Length - 1, info.Nodes[0].Location)); } if (ShadowStart + Facings * Stride > sprites.Length) { throw new InvalidOperationException("{5}:Sequence {0}.{1}'s shadow frames use frames [{2}...{3}],but only [0..{4}] actually exist".F(sequence, animation, ShadowStart, ShadowStart + Facings * Stride - 1, sprites.Length - 1, info.Nodes[0].Location)); } var boundSprites = SpriteBounds(sprites, Frames, Start, Facings, Length); if (ShadowStart > 0) { boundSprites = boundSprites.Concat(SpriteBounds(sprites, Frames, ShadowStart, Facings, Length)); } if (boundSprites.Any()) { Bounds = boundSprites.First(); foreach (var b in boundSprites.Skip(1)) { Bounds = Rectangle.Union(Bounds, b); } } } catch (FormatException f) { throw new FormatException("Failed to parse sequences for {0}.{1} at {2}:\n{3}".F(sequence, animation, info.Nodes[0].Location, f)); } }