|
16 | 16 |
|
17 | 17 | extern alias BeatSaberFinalIK;
|
18 | 18 |
|
19 |
| -using System; |
20 | 19 | using System.Collections.Generic;
|
| 20 | +using System.Linq; |
21 | 21 | using System.Reflection;
|
22 | 22 | using System.Reflection.Emit;
|
23 | 23 | using BeatSaberFinalIK::RootMotion.FinalIK;
|
|
27 | 27 |
|
28 | 28 | namespace CustomAvatar.Patches
|
29 | 29 | {
|
| 30 | + /// <summary> |
| 31 | + /// This patch makes the constructor of <see cref="IKSolverVR"/> instantiate our <see cref="IKSolverVR_Arm"/> in the <see cref="IKSolverVR.leftArm"/> and <see cref="IKSolverVR.rightArm"/> fields. |
| 32 | + /// </summary> |
30 | 33 | [HarmonyPatch(typeof(IKSolverVR), MethodType.Constructor)]
|
31 | 34 | internal static class IKSolverVR_Constructor
|
32 | 35 | {
|
33 |
| - public static void Postfix(IKSolverVR __instance) |
| 36 | + private static readonly ConstructorInfo kIKSolverVRArmConstructor = typeof(IKSolverVR.Arm).GetConstructors().Single(); |
| 37 | + private static readonly ConstructorInfo kNewIKSolverVRArmConstructor = typeof(IKSolverVR_Arm).GetConstructors().Single(); |
| 38 | + |
| 39 | + public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) |
34 | 40 | {
|
35 |
| - __instance.leftArm = new IKSolverVR_Arm(); |
36 |
| - __instance.rightArm = new IKSolverVR_Arm(); |
| 41 | + foreach (CodeInstruction instruction in instructions) |
| 42 | + { |
| 43 | + if (instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == kIKSolverVRArmConstructor) |
| 44 | + { |
| 45 | + instruction.operand = kNewIKSolverVRArmConstructor; |
| 46 | + } |
| 47 | + |
| 48 | + yield return instruction; |
| 49 | + } |
37 | 50 | }
|
38 | 51 | }
|
39 | 52 |
|
40 |
| - [HarmonyPatch(typeof(IKSolverVR.Arm), nameof(IKSolverVR.Arm.Solve))] |
41 |
| - internal static class IKSolverVR_Arm_PitchAngleOffset |
| 53 | + /// <summary> |
| 54 | + /// This patch prevents locomotion from fighting against the position we set in <see cref="Avatar.AvatarIK"/> when the pelvis target exists. |
| 55 | + /// </summary> |
| 56 | + [HarmonyPatch(typeof(IKSolverVR), nameof(IKSolverVR.Solve))] |
| 57 | + internal static class IKSolverVR_Solve |
42 | 58 | {
|
43 |
| - private static readonly FieldInfo kPitchOffsetAngleField = AccessTools.DeclaredField(typeof(IKSolverVR_Arm), nameof(IKSolverVR_Arm.shoulderPitchOffset)); |
| 59 | + private static readonly MethodInfo kRootBonePropertyGetter = AccessTools.DeclaredPropertyGetter(typeof(IKSolverVR), nameof(IKSolverVR.rootBone)); |
| 60 | + private static readonly FieldInfo kVirtualBoneSolverPositionField = AccessTools.DeclaredField(typeof(IKSolverVR.VirtualBone), nameof(IKSolverVR.VirtualBone.solverPosition)); |
| 61 | + private static readonly FieldInfo kSpineField = AccessTools.DeclaredField(typeof(IKSolverVR), nameof(IKSolverVR.spine)); |
| 62 | + private static readonly FieldInfo kSpinePelvisTargetField = AccessTools.DeclaredField(typeof(IKSolverVR.Spine), nameof(IKSolverVR.Spine.pelvisTarget)); |
| 63 | + private static readonly MethodInfo kUnityObjectEqualsMethod = AccessTools.DeclaredMethod(typeof(UnityEngine.Object), "op_Equality"); |
44 | 64 |
|
45 |
| - public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) |
| 65 | + public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGenerator) |
46 | 66 | {
|
47 |
| - return new CodeMatcher(instructions) |
48 |
| - /* Quaternion.AngleAxis(isLeft ? pitchOffsetAngle : -pitchOffsetAngle, chestForward) */ |
49 |
| - .MatchForward(false, |
50 |
| - new CodeMatch(i => i.Equals(OpCodes.Ldc_R4, 30f)), |
51 |
| - new CodeMatch(i => i.Branches(out Label? _)), |
52 |
| - new CodeMatch(i => i.Equals(OpCodes.Ldc_R4, -30f))) |
53 |
| - .SetAndAdvance(OpCodes.Ldarg_0, null) |
54 |
| - .InsertAndAdvance( |
55 |
| - new CodeInstruction(OpCodes.Ldfld, kPitchOffsetAngleField), |
56 |
| - new CodeInstruction(OpCodes.Neg)) |
57 |
| - .Advance(1) |
58 |
| - .SetAndAdvance(OpCodes.Ldarg_0, null) |
59 |
| - .InsertAndAdvance( |
60 |
| - new CodeInstruction(OpCodes.Ldfld, kPitchOffsetAngleField)) |
61 |
| - |
62 |
| - /* pitch -= pitchOffsetAngle */ |
63 |
| - .MatchForward(false, |
64 |
| - new CodeMatch(i => i.LoadsLocal(11)), |
65 |
| - new CodeMatch(i => i.opcode == OpCodes.Ldc_R4 && (float)i.operand == -30f), |
66 |
| - new CodeMatch(OpCodes.Sub), |
67 |
| - new CodeMatch(i => i.SetsLocal(11))) |
68 |
| - .ThrowIfInvalid("`pitch -= pitchOffsetAngle` not found") |
69 |
| - .Advance(1) |
70 |
| - .SetAndAdvance(OpCodes.Ldarg_0, null) |
71 |
| - .InsertAndAdvance( |
72 |
| - new CodeInstruction(OpCodes.Ldfld, kPitchOffsetAngleField)) |
73 |
| - |
74 |
| - /* DamperValue(pitch, -45f - pitchOffsetAngle, 45f - pitchOffsetAngle) */ |
75 |
| - .MatchForward(false, |
76 |
| - new CodeMatch(i => i.LoadsLocal(11)), |
77 |
| - new CodeMatch(i => i.Equals(OpCodes.Ldc_R4, -15f)), |
78 |
| - new CodeMatch(i => i.Equals(OpCodes.Ldc_R4, 75f))) |
79 |
| - .Advance(1) |
80 |
| - .SetOperandAndAdvance(-45f) |
81 |
| - .InsertAndAdvance( |
82 |
| - new CodeInstruction(OpCodes.Ldarg_0, null), |
83 |
| - new CodeInstruction(OpCodes.Ldfld, kPitchOffsetAngleField), |
84 |
| - new CodeInstruction(OpCodes.Sub)) |
85 |
| - .SetOperandAndAdvance(45f) |
| 67 | + return new CodeMatcher(instructions, ilGenerator) |
| 68 | + .MatchForward( |
| 69 | + false, |
| 70 | + new CodeMatch(OpCodes.Ldarg_0), |
| 71 | + new CodeMatch(i => i.Calls(kRootBonePropertyGetter)), |
| 72 | + new CodeMatch(i => i.LoadsLocal(14)), |
| 73 | + new CodeMatch(i => i.StoresField(kVirtualBoneSolverPositionField))) |
| 74 | + .CreateLabelWithOffsets(4, out Label label) |
86 | 75 | .InsertAndAdvance(
|
87 |
| - new CodeInstruction(OpCodes.Ldarg_0, null), |
88 |
| - new CodeInstruction(OpCodes.Ldfld, kPitchOffsetAngleField), |
89 |
| - new CodeInstruction(OpCodes.Sub)) |
| 76 | + new CodeInstruction(OpCodes.Ldarg_0), |
| 77 | + new CodeInstruction(OpCodes.Ldfld, kSpineField), |
| 78 | + new CodeInstruction(OpCodes.Ldfld, kSpinePelvisTargetField), |
| 79 | + new CodeInstruction(OpCodes.Ldnull), |
| 80 | + new CodeInstruction(OpCodes.Call, kUnityObjectEqualsMethod), |
| 81 | + new CodeInstruction(OpCodes.Brfalse_S, label)) |
| 82 | + .MatchForward( |
| 83 | + false, |
| 84 | + new CodeMatch(OpCodes.Ldarg_0), |
| 85 | + new CodeMatch(i => i.Calls(kRootBonePropertyGetter)), |
| 86 | + new CodeMatch(i => i.LoadsField(kVirtualBoneSolverPositionField))) |
| 87 | + .RemoveInstructions(3) |
| 88 | + .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 14)) |
90 | 89 | .InstructionEnumeration();
|
91 | 90 | }
|
92 | 91 | }
|
|
0 commit comments