private static bool onActorMoveHExact(On.Celeste.Actor.orig_MoveHExact orig, Actor self, int moveH, Collision onCollide, Solid pusher) { // fall back to vanilla if no sideways jumpthru is in the room. if (self.SceneAs <Level>().Tracker.CountEntities <SidewaysJumpThru>() == 0) { return(orig(self, moveH, onCollide, pusher)); } Vector2 targetPosition = self.Position + Vector2.UnitX * moveH; int moveDirection = Math.Sign(moveH); int moveAmount = 0; bool movingLeftToRight = moveH > 0; while (moveH != 0) { bool didCollide = false; // check if colliding with a solid Solid solid = self.CollideFirst <Solid>(self.Position + Vector2.UnitX * moveDirection); if (solid != null) { didCollide = true; } else { // check if colliding with a sideways jumpthru SidewaysJumpThru jumpThru = self.CollideFirstOutside <SidewaysJumpThru>(self.Position + Vector2.UnitX * moveDirection); if (jumpThru != null && jumpThru.AllowLeftToRight != movingLeftToRight) { // there is a sideways jump-thru and we are moving in the opposite direction => collision didCollide = true; } } if (didCollide) { Vector2 movementCounter = (Vector2)actorMovementCounter.GetValue(self); movementCounter.X = 0f; actorMovementCounter.SetValue(self, movementCounter); onCollide?.Invoke(new CollisionData { Direction = Vector2.UnitX * moveDirection, Moved = Vector2.UnitX * moveAmount, TargetPosition = targetPosition, Hit = solid, Pusher = pusher }); return(true); } // continue moving moveAmount += moveDirection; moveH -= moveDirection; self.X += moveDirection; } return(false); }
private static void modCollideChecks(ILContext il) { ILCursor cursor = new ILCursor(il); while (cursor.Next != null) { Instruction next = cursor.Next; // we want to replace all CollideChecks with solids here. if (next.OpCode == OpCodes.Call && (next.Operand as MethodReference)?.FullName == "System.Boolean Monocle.Entity::CollideCheck<Celeste.Solid>(Microsoft.Xna.Framework.Vector2)") { Logger.Log("SpringCollab2020/SidewaysJumpThru", $"Patching Entity.CollideCheck to include sideways jumpthrus at {cursor.Index} in IL for {il.Method.Name}"); cursor.Remove(); cursor.EmitDelegate <Func <Entity, Vector2, bool> >((self, checkAtPosition) => { // we still want to check for solids... if (self.CollideCheck <Solid>(checkAtPosition)) { return(true); } // if we are not checking a side, this certainly has nothing to do with jumpthrus. if (self.Position.X == checkAtPosition.X) { return(false); } // our entity also collides if this is with a jumpthru and we are colliding with the solid side of it. // we are in this case if the jumpthru is left to right (the "solid" side of it is the right one) // and we are checking the collision on the left side of the player for example. bool collideOnLeftSideOfPlayer = (self.Position.X > checkAtPosition.X); SidewaysJumpThru jumpthru = self.CollideFirstOutside <SidewaysJumpThru>(checkAtPosition); return(jumpthru != null && self is Player player && (jumpthru.AllowLeftToRight == collideOnLeftSideOfPlayer) && jumpthru.Bottom >= self.Top + checkAtPosition.Y - self.Position.Y + 3); }); } if (next.OpCode == OpCodes.Callvirt && (next.Operand as MethodReference)?.FullName == "System.Boolean Monocle.Scene::CollideCheck<Celeste.Solid>(Microsoft.Xna.Framework.Vector2)") { Logger.Log("SpringCollab2020/SidewaysJumpThru", $"Patching Scene.CollideCheck to include sideways jumpthrus at {cursor.Index} in IL for {il.Method.Name}"); cursor.Remove(); cursor.EmitDelegate <Func <Scene, Vector2, bool> >((self, vector) => self.CollideCheck <Solid>(vector) || self.CollideCheck <SidewaysJumpThru>(vector)); } cursor.Index++; } }
private static int onPlayerNormalUpdate(On.Celeste.Player.orig_NormalUpdate orig, Player self) { int result = orig(self); // kill speed if player is going towards a jumpthru. if (self.Speed.X != 0) { bool movingLeftToRight = self.Speed.X > 0; SidewaysJumpThru jumpThru = self.CollideFirstOutside <SidewaysJumpThru>(self.Position + Vector2.UnitX * Math.Sign(self.Speed.X)); if (jumpThru != null && jumpThru.AllowLeftToRight != movingLeftToRight) { self.Speed.X = 0; } } return(result); }
private static bool onPlatformMoveHExactCollideSolids(On.Celeste.Platform.orig_MoveHExactCollideSolids orig, Platform self, int moveH, bool thruDashBlocks, Action <Vector2, Vector2, Platform> onCollide) { // fall back to vanilla if no sideways jumpthru is in the room. if (self.SceneAs <Level>().Tracker.CountEntities <SidewaysJumpThru>() == 0) { return(orig(self, moveH, thruDashBlocks, onCollide)); } float x = self.X; int moveDirection = Math.Sign(moveH); int moveAmount = 0; Solid solid = null; bool movingLeftToRight = moveH > 0; bool collidedWithJumpthru = false; while (moveH != 0) { if (thruDashBlocks) { // check if we have dash blocks to break on our way. foreach (DashBlock entity in self.Scene.Tracker.GetEntities <DashBlock>()) { if (self.CollideCheck(entity, self.Position + Vector2.UnitX * moveDirection)) { entity.Break(self.Center, Vector2.UnitX * moveDirection, true, true); self.SceneAs <Level>().Shake(0.2f); Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium); } } } // check for collision with a solid solid = self.CollideFirst <Solid>(self.Position + Vector2.UnitX * moveDirection); // check for collision with a sideways jumpthru SidewaysJumpThru jumpThru = self.CollideFirstOutside <SidewaysJumpThru>(self.Position + Vector2.UnitX * moveDirection); if (jumpThru != null && jumpThru.AllowLeftToRight != movingLeftToRight) { // there is a sideways jump-thru and we are moving in the opposite direction => collision collidedWithJumpthru = true; } if (solid != null || collidedWithJumpthru) { break; } // continue moving moveAmount += moveDirection; moveH -= moveDirection; self.X += moveDirection; } // actually move and call the collision callback if any self.X = x; self.MoveHExact(moveAmount); if (solid != null && onCollide != null) { onCollide(Vector2.UnitX * moveDirection, Vector2.UnitX * moveAmount, solid); } return(solid != null || collidedWithJumpthru); }