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));
            }
        }