private static DashCollision CreateDashCollisionHook(DashCollision orig, DashCollisionHook init = null)
        {
            orig ??= DashCollisionDefault;
            var           hooks   = new List <DashCollisionHook>();
            DashCollision handler = (Player player, Vector2 direction) => {
                int i = 0;
                DashCollisionResults Trampoline(DashCollision orig, Player player, Vector2 direction)
                {
                    if (hooks.Count <= i)
                    {
                        return(orig(player, direction));
                    }
                    DashCollisionHook current = hooks[i++];

                    return(Trampoline((Player player, Vector2 direction) => current.Invoke(orig, player, direction), player, direction));
                }

                return(Trampoline(orig, player, direction));
            };

            DynamicData.For(handler).Add(DASHCOLLISIONHOOK_TAG, hooks);
            if (init is not null)
            {
                hooks.Add(init);
            }
            return(handler);
        }
 protected override DashCollisionResults OnDashCollide(DashCollision orig, Player player, Vector2 dir)
 {
     if (CheckDashCollision(player, dir))
     {
         if (!overrideCollision)
         {
             base.OnDashCollide(orig, player, dir);
         }
         return(dashCollisionOverride);
     }
     return(base.OnDashCollide(orig, player, dir));
 }
        /// <returns>Handler to be assigned to Platform.OnDashCollide</returns>
        public static DashCollision ApplyDashCollisionHook(DashCollision orig, DashCollisionHook hook)
        {
            if (hook is null)
            {
                throw new ArgumentNullException("hook");
            }

            if (orig is not null && DynamicData.For(orig).TryGet(DASHCOLLISIONHOOK_TAG, out List <DashCollisionHook> hooks))
            {
                hooks.Add(hook);
                return(orig);
            }
        public LDashSwitch(Vector2 position, Sides side, bool persistent, EntityID id, string spriteName) : base(position, 0f, 0f, true)
        {
            this.side       = side;
            this.persistent = persistent;
            this.id         = id;
            mirrorMode      = (spriteName != "default");
            Add(sprite      = GFX.SpriteBank.Create("DashSwitch_" + spriteName));
            sprite.Play("idle", false, false);
            if (side == Sides.Up || side == Sides.Down)
            {
                Collider.Width  = 16f;
                Collider.Height = 8f;
            }
            else
            {
                Collider.Width  = 8f;
                Collider.Height = 16f;
            }
            switch (side)
            {
            case Sides.Up:
                sprite.Position = new Vector2(8f, 0f);
                sprite.Rotation = -1.5707964f;
                pressedTarget   = Position + Vector2.UnitY * -8f;
                pressDirection  = -Vector2.UnitY;
                break;

            case Sides.Down:
                sprite.Position = new Vector2(8f, 8f);
                sprite.Rotation = 1.5707964f;
                pressedTarget   = Position + Vector2.UnitY * 8f;
                pressDirection  = Vector2.UnitY;
                startY          = Y;
                break;

            case Sides.Left:
                sprite.Position = new Vector2(0f, 8f);
                sprite.Rotation = 3.1415927f;
                pressedTarget   = Position + Vector2.UnitX * -8f;
                pressDirection  = -Vector2.UnitX;
                break;

            case Sides.Right:
                sprite.Position = new Vector2(8f, 8f);
                sprite.Rotation = 0f;
                pressedTarget   = Position + Vector2.UnitX * 8f;
                pressDirection  = Vector2.UnitX;
                break;
            }
            OnDashCollide = new DashCollision(OnDashed);
        }
        public CustomCrushBlock(EntityData data, Vector2 offset) : base(data.Position + offset, data.Width, data.Height, false)
        {
            CrushAccel  = data.Float("crushAcceleration", 250f);
            ReturnAccel = data.Float("returnAcceleration", 160f);
            ReturnSpeed = data.Float("returnSpeed", 60f);
            CrushSpeed  = data.Float("crushSpeed", 120f);
            chillOut    = data.Bool("chillout", false);
            Directory   = data.Attr("directory", "objects/FrostHelper/slowcrushblock/");
            if (!Directory.EndsWith("/"))
            {
                Directory += '/';
            }

            fillColor          = Calc.HexToColor("62222b");
            idleImages         = new List <Image>();
            activeTopImages    = new List <Image>();
            activeRightImages  = new List <Image>();
            activeLeftImages   = new List <Image>();
            activeBottomImages = new List <Image>();
            OnDashCollide      = new DashCollision(OnDashed);
            returnStack        = new List <MoveState>();

            giant           = Width >= 48f && Height >= 48f && chillOut;
            canActivate     = true;
            attackCoroutine = new Coroutine(true);
            attackCoroutine.RemoveOnComplete = false;
            Add(attackCoroutine);
            List <MTexture> atlasSubtextures = GFX.Game.GetAtlasSubtextures(Directory + "block");
            MTexture        idle;

            switch (data.Enum("axes", Axes.Both))
            {
            default:
                idle = atlasSubtextures[3];
                canMoveHorizontally = canMoveVertically = true;
                break;

            case Axes.Horizontal:
                idle = atlasSubtextures[1];
                canMoveHorizontally = true;
                canMoveVertically   = false;
                break;

            case Axes.Vertical:
                idle = atlasSubtextures[2];
                canMoveHorizontally = false;
                canMoveVertically   = true;
                break;
            }
            Add(face      = GFX.SpriteBank.Create(giant ? "giant_crushblock_face" : "crushblock_face"));
            face.Position = new Vector2(Width, Height) / 2f;
            face.Play("idle", false, false);
            face.OnLastFrame = delegate(string f)
            {
                bool flag = f == "hit";
                if (flag)
                {
                    face.Play(nextFaceDirection, false, false);
                }
            };
            int num  = (int)(Width / 8f) - 1;
            int num2 = (int)(Height / 8f) - 1;

            AddImage(idle, 0, 0, 0, 0, -1, -1);
            AddImage(idle, num, 0, 3, 0, 1, -1);
            AddImage(idle, 0, num2, 0, 3, -1, 1);
            AddImage(idle, num, num2, 3, 3, 1, 1);
            for (int i = 1; i < num; i++)
            {
                AddImage(idle, i, 0, Calc.Random.Choose(1, 2), 0, 0, -1);
                AddImage(idle, i, num2, Calc.Random.Choose(1, 2), 3, 0, 1);
            }
            for (int j = 1; j < num2; j++)
            {
                AddImage(idle, 0, j, 0, Calc.Random.Choose(1, 2), -1, 0);
                AddImage(idle, num, j, 3, Calc.Random.Choose(1, 2), 1, 0);
            }
            Add(new LightOcclude(0.2f));
            Add(returnLoopSfx = new SoundSource());
            Add(new WaterInteraction(() => crushDir != Vector2.Zero));
        }
        protected override DashCollisionResults OnDashCollide(DashCollision orig, Player player, Vector2 dir)
        {
            // Correct position/ignore if only partially intersecting
            // Have to use `player.DashDir` instead of `dir` because we need the actual dash direction, not the collision direction
            if (PlayerHasDreamDash)
            {
                switch (Orientation)
                {
                case Spikes.Directions.Up:
                    if (dir.Y > 0 && TryCollidePlayer(player, Vector2.UnitY, player.DashDir))
                    {
                        return(DashCollisionResults.Ignore);
                    }
                    break;

                case Spikes.Directions.Down:
                    if (dir.Y < 0 && TryCollidePlayer(player, -Vector2.UnitY, player.DashDir))
                    {
                        return(DashCollisionResults.Ignore);
                    }
                    break;

                case Spikes.Directions.Left:
                    if (dir.X > 0 && TryCollidePlayer(player, Vector2.UnitX, player.DashDir))
                    {
                        return(DashCollisionResults.Ignore);
                    }
                    break;

                case Spikes.Directions.Right:
                    if (dir.X < 0 && TryCollidePlayer(player, -Vector2.UnitX, player.DashDir))
                    {
                        return(DashCollisionResults.Ignore);
                    }
                    break;
                }
            }
            else
            {
                switch (Orientation)
                {
                case Spikes.Directions.Up when dir.Y > 0:
                case Spikes.Directions.Down when dir.Y < 0:
                    if (player.Left > Left - 4 || !Scene.CollideCheck <Solid>(CenterLeft - Vector2.UnitX) ||
                        player.Right < Right + 4 || !Scene.CollideCheck <Solid>(CenterRight + Vector2.UnitX))
                    {
                        return(DashCollisionResults.NormalCollision);
                    }
                    break;

                case Spikes.Directions.Left when dir.X > 0:
                case Spikes.Directions.Right when dir.X < 0:
                    if (player.Top > Top - 4 || !Scene.CollideCheck <Solid>(TopCenter - Vector2.UnitY) &&
                        player.Bottom < Bottom + 4 || !Scene.CollideCheck <Solid>(BottomCenter + Vector2.UnitY))
                    {
                        return(DashCollisionResults.NormalCollision);
                    }
                    break;
                }
            }
            return(base.OnDashCollide(orig, player, dir));
        }