-
Notifications
You must be signed in to change notification settings - Fork 0
/
ClimbJumpFixerModule.cs
124 lines (108 loc) · 5.66 KB
/
ClimbJumpFixerModule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System;
using System.Reflection;
using Microsoft.Xna.Framework;
using Mono.Cecil.Cil;
using Monocle;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;
namespace Celeste.Mod.ClimbJumpFixer {
public class ClimbJumpFixerModule : EverestModule {
public static ClimbJumpFixerModule Instance { get; private set; }
public static ClimbJumpFixerSettings Settings => (ClimbJumpFixerSettings) Instance._Settings;
public override Type SettingsType => typeof(ClimbJumpFixerSettings);
private static readonly FieldInfo JumpGraceTimer = typeof(Player).GetField("jumpGraceTimer", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly MethodInfo WallJumpCheck = typeof(Player).GetMethod("WallJumpCheck", BindingFlags.Instance | BindingFlags.NonPublic);
private static ILHook ilHook;
public ClimbJumpFixerModule() {
Instance = this;
}
public override void Load() {
IL.Celeste.Player.NormalUpdate += PlayerOnNormalUpdate;
IL.Celeste.Player.ClimbUpdate += ModClimbUpdateCanUnDuck;
IL.Celeste.Player.NormalUpdate += ModNormalUpdateCanUnDuck;
IL.Celeste.Player.DashUpdate += ModNormalUpdateCanUnDuck;
IL.Celeste.Player.RedDashUpdate += ModNormalUpdateCanUnDuck;
IL.Celeste.Player.SuperWallJump += ModDucking;
ilHook = new ILHook(typeof(Player).GetMethod("orig_WallJump", BindingFlags.Instance | BindingFlags.NonPublic), ModDucking);
}
public override void Unload() {
IL.Celeste.Player.NormalUpdate -= PlayerOnNormalUpdate;
IL.Celeste.Player.ClimbUpdate -= ModClimbUpdateCanUnDuck;
IL.Celeste.Player.NormalUpdate -= ModNormalUpdateCanUnDuck;
IL.Celeste.Player.DashUpdate -= ModNormalUpdateCanUnDuck;
IL.Celeste.Player.RedDashUpdate -= ModNormalUpdateCanUnDuck;
IL.Celeste.Player.SuperWallJump -= ModDucking;
ilHook.Dispose();
}
private void ModNormalUpdateCanUnDuck(ILContext il) {
ILCursor ilCursor = new ILCursor(il);
while (ilCursor.TryGotoNext(
MoveType.After,
ins => ins.MatchCallvirt<Player>("get_CanUnDuck")
)) {
ILCursor cursor = ilCursor.Clone();
if (cursor.TryGotoNext(MoveType.After, ins => ins.MatchCallvirt<Player>("WallJumpCheck"))
&& ilCursor.Index + 10 > cursor.Index) {
ilCursor.EmitDelegate<Func<bool, bool>>(canUnDuck => Settings.DuckableWallJump || canUnDuck);
Logger.Log("ClimbJumpFixerModule", il.Method.ToString());
}
}
}
private void ModClimbUpdateCanUnDuck(ILContext il) {
ILCursor ilCursor = new ILCursor(il);
while (ilCursor.TryGotoNext(
MoveType.After,
ins => ins.MatchCallvirt<Player>("get_CanUnDuck")
)) {
ilCursor.EmitDelegate<Func<bool, bool>>(canUnDuck => Settings.DuckableWallJump || canUnDuck);
Logger.Log("ClimbJumpFixerModule", il.Method.ToString());
}
}
private void ModDucking(ILContext il) {
ILCursor ilCursor = new ILCursor(il);
if (ilCursor.TryGotoNext(
MoveType.After,
ins => ins.OpCode == OpCodes.Ldarg_0,
ins => ins.OpCode == OpCodes.Ldc_I4_0,
ins => ins.MatchCallvirt<Player>("set_Ducking")
)) {
ilCursor.Index--;
ilCursor.Emit(OpCodes.Ldarg_0).EmitDelegate<Func<bool, Player, bool>>((duck, player) => {
if (Settings.DuckableWallJump && !player.CanUnDuck) {
Engine.Commands.Log("Jump!");
}
return Settings.DuckableWallJump && !player.CanUnDuck || duck;
});
}
}
private void PlayerOnNormalUpdate(ILContext il) {
ILCursor ilCursor = new ILCursor(il);
if (ilCursor.TryGotoNext(MoveType.After, instruction => instruction.MatchLdsfld(typeof(Input), "Grab"),
instruction => instruction.MatchCallvirt<VirtualButton>("get_Check")
)) {
if (ilCursor.TryGotoNext(MoveType.After, instruction => instruction.OpCode == OpCodes.Ldarg_0,
instruction => instruction.MatchLdflda<Player>("Speed"),
instruction => instruction.MatchLdfld<Vector2>("Y"))
) {
ilCursor.Emit(OpCodes.Ldarg_0);
ilCursor.EmitDelegate<Func<Player, float, float>>(delegate(Player player, float speedY) {
if (ClimbJumpCheck(player)) {
if (Input.Jump.Pressed && Math.Sign(player.Speed.X) != -(int) player.Facing && player.ClimbCheck((int) player.Facing)) {
Level level = player.SceneAs<Level>();
Logger.Log("ClimbJumpFixer",
$"Maybe try to grab but climbjump: chapter time {TimeSpan.FromTicks(level.Session.Time).ShortGameplayFormat()}");
}
return -1f;
} else {
return speedY;
}
});
}
}
}
private bool ClimbJumpCheck(Player player) {
return Settings.FixFallingClimbJump && Input.Jump.Pressed && (float) JumpGraceTimer.GetValue(player) <= 0f && player.CanUnDuck &&
(bool) WallJumpCheck.Invoke(player, new object[] {(int) player.Facing});
}
}
}