/* * Ever wanted to play with diagonal movement and no speedcap? */ using HarmonyLib; using MIU; using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; namespace Sandbox.Patches { /* * Turn on the rewind flag if you're using these, so you don't submit any scores */ [HarmonyPatch(typeof(MarbleManager), "FixedUpdate")] internal class MarbleManagerFixedUpdatePatch { static bool Prefix() { if (Config.diagonalMovement || Config.uncapVelocity) MarbleManager.usedRewind = true; return true; } } /* * Reimplement MoveNormal except don't normalize the input. * Wish I didn't have to do this to remove one function call... */ [HarmonyPatch(typeof(MarbleController), "MoveNormal")] internal class MarbleControllerMoveNormalPatch { static bool Prefix(MarbleController __instance, float dT, GameMove move) { if (!Config.diagonalMovement) return true; // Run original method if (move == null) { move = new GameMove(); } Assembly miuuAsm = typeof(MarbleController).Assembly; Type marbleControllerType = miuuAsm.GetType("MarbleController"); FieldInfo marbleControllerRespawnCounterField = marbleControllerType.GetField("respawnCounter", BindingFlags.Instance | BindingFlags.NonPublic); int respawnCounter = (int)marbleControllerRespawnCounterField.GetValue(__instance); if (respawnCounter > 0) { respawnCounter--; marbleControllerRespawnCounterField.SetValue(__instance, respawnCounter); if (respawnCounter <= 0) { marbleControllerType.GetField("respawnCounter", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(__instance, true); return false; // Skip original method } } if (__instance.InMode(256)) { if (__instance.GrinderEffects != null) { __instance.GrinderEffects.AdvanceCrush(__instance, dT); } return false; // Skip original method } if (__instance.GrinderEffects != null) { __instance.GrinderEffects.Reset(); } FieldInfo marbleControllerContactsField = marbleControllerType.GetField("Contacts", BindingFlags.Instance | BindingFlags.NonPublic); List contacts = (List)marbleControllerContactsField.GetValue(__instance); if (__instance.InMode(8)) { marbleControllerType.GetMethod("MoveStarting", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, new object[] { dT }); __instance.SetVelocity(Vector3.zero); contacts.Clear(); if (!NetworkManager.IsMultiplayer && __instance.IsServerObject()) { Powerup[] array = ((GameProcess)marbleControllerType.GetField("LocalProcess", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance)).FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { array[i].Restore(); } } } bool flag = !MarbleManager.Rewinding && GamePlayManager.Get().PlayType != PlayType.Replay && !ConsoleGUI.IsActive; if (__instance.InMode(32)) { if (__instance.TrackingTarget != null) { Vector3 position = __instance.TrackingTarget.position; if (position != Vector3.zero && contacts.Count > 0) { FieldInfo marbleControllerTrackingErrorSumField = marbleControllerType.GetField("TrackingErrorSum", BindingFlags.Instance | BindingFlags.NonPublic); Vector3 trackingErrorSum = (Vector3)marbleControllerTrackingErrorSumField.GetValue(__instance); Vector3 vector = position - __instance.transform.position; Vector3 vector2 = vector * __instance.TrackingPID.x + trackingErrorSum * __instance.TrackingPID.y - __instance.GetVelocity() * __instance.TrackingPID.z; move.d.x = vector2.x; move.d.y = vector2.z; trackingErrorSum += vector; marbleControllerTrackingErrorSumField.SetValue(__instance, trackingErrorSum); } } } else if (flag && __instance.InMode(8)) { move.trigger0 = false; move.trigger1 = false; move.trigger2 = false; } // PATCH STARTS HERE - don't normalize the input! /*if (move.d.magnitude > 1f) { move.d.Normalize(); }*/ // PATCH ENDS HERE Type marbleInputManagerType = miuuAsm.GetType("MarbleInputManager"); marbleInputManagerType.GetField("DebugMoveClamped", BindingFlags.Public | BindingFlags.Static).SetValue(null, move.d); ((HashSet)marbleControllerType.GetField("_ContactsThisTickList", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance)).Clear(); marbleControllerType.GetMethod("AdvancePhysics", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, new object[] { move, dT }); if (NetworkManager.IsDedicated && !MarbleManager.Replaying) { __instance.transform.position = __instance.GetPosition(); __instance.transform.rotation = (Quaternion)marbleControllerType.GetField("qW", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); } return false; // Skip original method } } /* * Reimplement this one without normalization as well * aaaaaaaaaaaaaaa */ [HarmonyPatch(typeof(MarbleController), "ComputeMoveForces")] internal class MarbleControllerComputeMoveForcesPatch { static bool Prefix(MarbleController __instance, ref bool __result, ref Vector3 aControl, ref Vector3 desiredOmega, GameMove move) { if (!Config.diagonalMovement) return true; // Run original method Assembly miuuAsm = typeof(MarbleController).Assembly; Type marbleControllerType = miuuAsm.GetType("MarbleController"); aControl = Vector3.zero; desiredOmega = Vector3.zero; Vector3 vector = -__instance.GetGravityDir() * __instance.Radius; Vector3 lhs = Vector3.Cross(__instance.GetAngularVelocity(), vector); Vector3 sideDir = Vector3.zero; Vector3 motionDir = Vector3.zero; Vector3 upDir = Vector3.zero; object[] inputParams = new object[] { sideDir, motionDir, upDir, move }; marbleControllerType.GetMethod("GetMarbleAxis", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, inputParams); // Just in case? sideDir = (Vector3)inputParams[0]; motionDir = (Vector3)inputParams[1]; upDir = (Vector3)inputParams[2]; float num = Vector3.Dot(lhs, motionDir); float num2 = Vector3.Dot(lhs, sideDir); // PATCH 1 STARTS HERE - don't do anything here. Note that this might reduce the input range on an analog stick Vector2 vector2 = move.d; /*Vector2 vector2 = move.d * 1f / 0.65f; if (vector2.magnitude > 1f) { vector2.Normalize(); }*/ // PATCH 1 ENDS HERE float maxRollVelocity = (float)marbleControllerType.GetField("MaxRollVelocity", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); float num3 = maxRollVelocity * vector2.y; float num4 = maxRollVelocity * vector2.x; float num5 = Math.Max(__instance.physMod.RollForceMult.x, __instance.physMod.RollForceMult.y); if (vector2.magnitude > 0.01f) { TimeGraph.GraphLog("moveMag", vector2.magnitude); if (num > num3 && num3 > 0.9f) { num3 = num; } else if (num < num3 && num3 < -0.9f) { num3 = num; } if (num2 > num4 && num4 > 0.9f) { num4 = num2; } else if (num2 < num4 && num4 < -0.9f) { num4 = num2; } Vector2 to = new Vector2(num2, num); // PATCH 2 STARTS HERE - don't normalize //Vector2 vector3 = new Vector2(num4, num3).normalized * Mathf.Max(maxRollVelocity * num5, to.magnitude); Vector2 vector3 = new Vector2(num4, num3) * Mathf.Max(maxRollVelocity * num5, to.magnitude); // PATCH 2 ENDS HERE float num6 = Vector2.Angle(vector3, to); float num7 = num6; if (num7 >= 180f) { num7 = 179.99f; } if (num7 <= 0f) { num7 = 0f; } if (float.IsNaN(num7)) { num7 = 0f; } float a = __instance.TurningCompensationFactor.Evaluate(0f); float b = __instance.TurningCompensationFactor.Evaluate(num6); float turningVelStartVelocity = (float)marbleControllerType.GetField("TurningVelStartVelocity", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); float num8 = Mathf.Lerp(a, b, (to.magnitude - turningVelStartVelocity * num5) / (maxRollVelocity - turningVelStartVelocity) * num5); if (to.magnitude < turningVelStartVelocity * num5) { num8 = __instance.TurningCompensationFactor.Evaluate(0f); } // PATCH 3 STARTS HERE - don't normalize //Vector2 vector4 = vector3.normalized * num8; Vector2 vector4 = vector3 * num8; // PATCH 3 ENDS HERE vector4 = new Vector2(vector4.x * __instance.physMod.RollForceMult.y, vector4.y * __instance.physMod.RollForceMult.x); vector3 = (vector3 + vector4) * vector2.magnitude; num4 = vector3.x; num3 = vector3.y; desiredOmega = Vector3.Cross(vector, num3 * motionDir + num4 * sideDir) / Vector3.Dot(vector, vector); aControl = desiredOmega - __instance.GetAngularVelocity(); float magnitude = aControl.magnitude; float angularAcceleration = (float)marbleControllerType.GetField("AngularAcceleration", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); if (magnitude > angularAcceleration) { aControl *= angularAcceleration / magnitude; } __result = false; return false; // Skip original method } __result = true; return false; // Skip original method } } /* * Another one, awesome!!! A lot easier at least since we can do a Postfix and grab the correct value */ [HarmonyPatch(typeof(MarbleController), nameof(MarbleController.GetNextMove))] internal class MarbleControllerGetNextMovePatch { static bool printed = false; static void Postfix(MarbleController __instance, ref bool __result, ref GameMove move) { if (!Config.diagonalMovement) return; // Don't do anything here if (!__result) return; Assembly miuuAsm = typeof(MarbleController).Assembly; //Type marbleControllerType = miuuAsm.GetType("MarbleController"); Type marbleInputManagerType = miuuAsm.GetType("MarbleInputManager"); move.d = (Vector2)marbleInputManagerType.GetField("DebugMoveScaled", BindingFlags.Public | BindingFlags.Static).GetValue(null); marbleInputManagerType.GetField("DebugMoveEased", BindingFlags.Public | BindingFlags.Static).SetValue(null, move.d); } } /* * Remove speed cap. Very easy, just don't do it */ [HarmonyPatch(typeof(MarbleController), "EnforceVelocityCap")] internal class MarbleControllerEnforceVelocityCapPatch { static bool Prefix() { if (!Config.uncapVelocity) return true; // Run original method return false; // Skip original method } } }