diff --git a/Config.cs b/Config.cs new file mode 100644 index 0000000..dc9009f --- /dev/null +++ b/Config.cs @@ -0,0 +1,8 @@ +namespace Sandbox +{ + internal class Config + { + public static bool uncapVelocity = false; + public static bool diagonalMovement = false; + } +} diff --git a/ConsoleCommands.cs b/ConsoleCommands.cs index d68aa49..ec56627 100644 --- a/ConsoleCommands.cs +++ b/ConsoleCommands.cs @@ -1,16 +1,53 @@ using MIU; -using System; -using System.Collections.Generic; -using UnityEngine; namespace Sandbox { internal class ConsoleCommands { - [ConsoleCommand(description = "Simple hello world testing command")] - public static string hello(params string[] args) + [ConsoleCommand(description = "Enable/disable velocity cap")] + public static string mbuncapvelocity(params string[] args) { - return "Hello, World!"; + if (args.Length != 1) + return "Expected 1 argument: 0, 1, or show"; + + switch (args[0]) + { + case "show": + return Config.uncapVelocity ? "Velocity is currently uncapped" : "Velocity is currently capped"; + case "0": + case "false": + Config.uncapVelocity = false; + return "Velocity has been capped"; + case "1": + case "true": + Config.uncapVelocity = true; + return "Velocity has been uncapped"; + default: + return "Unknown value. Expected 0, 1, or show"; + } + } + + [ConsoleCommand(description = "Enable/disable diagonal movement")] + public static string mbdiagonal(params string[] args) + { + if (args.Length != 1) + return "Expected 1 argument: 0, 1, or show"; + + switch (args[0]) + { + case "show": + return Config.diagonalMovement ? "Diagonal movement is currently disabled" : "Diagonal movement is currently enabled"; + case "0": + case "false": + Config.diagonalMovement = false; + return "Diagonal movement has been disabled"; + case "1": + case "true": + Config.diagonalMovement = true; + return "Diagonal movement has been enabled"; + default: + return "Unknown value. Expected 0, 1, or show"; + } } } } diff --git a/Patches/MarbleBlastPhysicsPatches.cs b/Patches/MarbleBlastPhysicsPatches.cs new file mode 100644 index 0000000..c6259b5 --- /dev/null +++ b/Patches/MarbleBlastPhysicsPatches.cs @@ -0,0 +1,286 @@ +/* + * 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 + } + } +} +