using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Interaction;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
using WallGrabJump;
using WallGrabJump.Compatibility;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(WallGrabJumpMod), "WallGrabJump", "1.0.2", "Cesar", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("WallGrabJump")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("WallGrabJump")]
[assembly: AssemblyTitle("WallGrabJump")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace WallGrabJump
{
internal enum ClimbSource
{
None,
Vanilla,
ClimbAnything
}
internal struct ClimbHold
{
internal ClimbSource Source;
internal Handedness Side;
internal Hand Hand;
internal Grip Grip;
internal object State;
internal Vector3 Anchor;
internal Collider Surface;
internal bool Valid => Source != ClimbSource.None;
}
internal static class ClimbDetector
{
internal static int Snapshot(out ClimbHold left, out ClimbHold right)
{
left = Evaluate(Player.LeftHand, (Handedness)1);
right = Evaluate(Player.RightHand, (Handedness)2);
return (left.Valid ? 1 : 0) + (right.Valid ? 1 : 0);
}
private static ClimbHold Evaluate(Hand hand, Handedness side)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
ClimbHold result = new ClimbHold
{
Side = side,
Hand = hand
};
if ((Object)(object)hand == (Object)null || !PlayerState.IsLocalHand(hand))
{
return default(ClimbHold);
}
try
{
HandReciever attachedReceiver = hand.AttachedReceiver;
Grip val = (((Object)(object)attachedReceiver != (Object)null) ? ((Il2CppObjectBase)attachedReceiver).TryCast<Grip>() : null);
if ((Object)(object)val != (Object)null && IsClimbingGrip(val))
{
result.Source = ClimbSource.Vanilla;
result.Grip = val;
result.Anchor = ((Component)hand).transform.position;
result.Surface = SurfaceOf(val);
return result;
}
}
catch (Exception ex)
{
Log.Debug("Vanilla climb check failed (" + ex.GetType().Name + ")");
}
ClimbHold result2 = ClimbAnythingCompatibility.Evaluate(hand, side);
if (!result2.Valid)
{
return default(ClimbHold);
}
return result2;
}
internal static bool IsClimbingGrip(Grip grip)
{
try
{
if ((Object)(object)grip == (Object)null)
{
return false;
}
if ((Object)(object)((Il2CppObjectBase)grip).TryCast<UIInteractionVolume>() != (Object)null)
{
return false;
}
if ((Object)(object)((Il2CppObjectBase)grip).TryCast<WorldGrip>() != (Object)null)
{
return true;
}
if (!((HandReciever)grip).HasHost)
{
return false;
}
return ((HandReciever)grip).IsStatic;
}
catch
{
return false;
}
}
private static Collider SurfaceOf(Grip grip)
{
try
{
if ((Object)(object)((Il2CppObjectBase)grip).TryCast<WorldGrip>() != (Object)null)
{
return null;
}
return ((Component)grip).GetComponentInParent<Collider>();
}
catch
{
return null;
}
}
}
internal sealed class InputController
{
private enum Phase
{
Unknown,
Released,
Held
}
private Phase _phase;
private int _sampledFrame = -1;
private bool _pressedThisFrame;
internal void Reset()
{
_phase = Phase.Unknown;
_sampledFrame = -1;
_pressedThisFrame = false;
}
internal bool PressedThisFrame()
{
int frameCount = Time.frameCount;
if (_sampledFrame == frameCount)
{
return _pressedThisFrame;
}
_sampledFrame = frameCount;
_pressedThisFrame = false;
bool readable;
bool flag = ReadA(out readable);
if (!readable)
{
_phase = Phase.Unknown;
return false;
}
if (!flag)
{
_phase = Phase.Released;
return false;
}
_pressedThisFrame = _phase == Phase.Released;
_phase = Phase.Held;
return _pressedThisFrame;
}
private static bool ReadA(out bool readable)
{
readable = false;
try
{
if (!Player.ControllersExist)
{
return false;
}
BaseController rightController = Player.RightController;
if ((Object)(object)rightController == (Object)null)
{
return false;
}
readable = true;
return rightController.GetAButton();
}
catch
{
readable = false;
return false;
}
}
}
internal static class PlayerState
{
private static PhysicsRig _bodiesOwner;
private static Rigidbody[] _bodies;
internal static bool IsValid
{
get
{
if (HelperMethods.IsLoading())
{
return false;
}
if ((Object)(object)Player.RigManager != (Object)null)
{
return (Object)(object)Player.PhysicsRig != (Object)null;
}
return false;
}
}
internal static Transform Head => Player.Head;
internal static Rigidbody Pelvis
{
get
{
PhysicsRig physicsRig = Player.PhysicsRig;
PhysTorso val = (((Object)(object)physicsRig != (Object)null) ? physicsRig.torso : null);
if (!((Object)(object)val != (Object)null))
{
return null;
}
return val.rbPelvis;
}
}
internal static Rigidbody Chest
{
get
{
PhysicsRig physicsRig = Player.PhysicsRig;
PhysTorso val = (((Object)(object)physicsRig != (Object)null) ? physicsRig.torso : null);
if (!((Object)(object)val != (Object)null))
{
return null;
}
return val.rbChest;
}
}
internal static Vector3 BodyCentre
{
get
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
Rigidbody chest = Chest;
if ((Object)(object)chest != (Object)null)
{
return chest.position;
}
Rigidbody pelvis = Pelvis;
if ((Object)(object)pelvis != (Object)null)
{
return pelvis.position;
}
Transform head = Head;
if (!((Object)(object)head != (Object)null))
{
return Vector3.zero;
}
return head.position;
}
}
internal static Vector3 Velocity
{
get
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
Rigidbody pelvis = Pelvis;
if (!((Object)(object)pelvis != (Object)null))
{
return Vector3.zero;
}
return pelvis.velocity;
}
}
internal static bool IsLocalHand(Hand hand)
{
if ((Object)(object)hand == (Object)null)
{
return false;
}
RigManager rigManager = Player.RigManager;
if ((Object)(object)rigManager != (Object)null)
{
return (Object)(object)hand.manager == (Object)(object)rigManager;
}
return false;
}
internal static bool TryGetMarrowClimbNormal(out Vector3 normal)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Invalid comparison between Unknown and I4
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Invalid comparison between Unknown and I4
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Invalid comparison between Unknown and I4
normal = Vector3.zero;
PhysicsRig physicsRig = Player.PhysicsRig;
if ((Object)(object)physicsRig == (Object)null)
{
return false;
}
StepState stepState = physicsRig._stepState;
if ((int)stepState != 4 && (int)stepState != 3 && (int)stepState != 5)
{
return false;
}
Vector3 climbingNormal = physicsRig.climbingNormal;
if (((Vector3)(ref climbingNormal)).sqrMagnitude < 0.25f)
{
return false;
}
normal = ((Vector3)(ref climbingNormal)).normalized;
return true;
}
internal static Rigidbody[] RigBodies()
{
PhysicsRig physicsRig = Player.PhysicsRig;
if ((Object)(object)physicsRig == (Object)null)
{
_bodiesOwner = null;
_bodies = null;
return null;
}
if (_bodies != null && (Object)(object)_bodiesOwner == (Object)(object)physicsRig)
{
return _bodies;
}
try
{
Il2CppArrayBase<Rigidbody> componentsInChildren = ((Component)physicsRig).GetComponentsInChildren<Rigidbody>(true);
if (componentsInChildren == null)
{
return null;
}
int num = 0;
for (int i = 0; i < componentsInChildren.Length; i++)
{
if ((Object)(object)componentsInChildren[i] != (Object)null)
{
num++;
}
}
Rigidbody[] array = (Rigidbody[])(object)new Rigidbody[num];
int num2 = 0;
for (int j = 0; j < componentsInChildren.Length; j++)
{
if (num2 >= num)
{
break;
}
Rigidbody val = componentsInChildren[j];
if ((Object)(object)val != (Object)null)
{
array[num2++] = val;
}
}
_bodies = array;
_bodiesOwner = physicsRig;
return _bodies;
}
catch (Exception ex)
{
Log.Debug("Could not gather rig rigidbodies (" + ex.GetType().Name + ")");
return null;
}
}
internal static void DropCaches()
{
_bodies = null;
_bodiesOwner = null;
}
}
internal static class Prefs
{
internal static MelonPreferences_Category Category;
internal static MelonPreferences_Entry<bool> Enabled;
internal static MelonPreferences_Entry<float> JumpStrength;
internal static MelonPreferences_Entry<float> LookInfluence;
internal static MelonPreferences_Entry<float> OutwardPush;
internal static MelonPreferences_Entry<float> UpwardBoost;
internal static MelonPreferences_Entry<float> OutwardFloor;
internal static MelonPreferences_Entry<float> AimStrictness;
internal static MelonPreferences_Entry<float> StaminaCost;
internal static MelonPreferences_Entry<bool> DebugLogging;
internal const float DefaultJumpStrength = 1.8f;
internal const float DefaultLookInfluence = 2f;
internal const float DefaultOutwardPush = 0.9f;
internal const float DefaultUpwardBoost = 1.65f;
internal const float DefaultOutwardFloor = 0.35f;
internal const float DefaultAimStrictness = 180f;
internal const float DefaultStaminaCost = 10f;
internal static bool EnabledValue
{
get
{
if (Enabled != null)
{
return Enabled.Value;
}
return true;
}
}
internal static float JumpStrengthValue => Sane(JumpStrength, 1.8f);
internal static float LookInfluenceValue => Sane(LookInfluence, 2f);
internal static float OutwardPushValue => Sane(OutwardPush, 0.9f);
internal static float UpwardBoostValue => Sane(UpwardBoost, 1.65f);
internal static float OutwardFloorValue => Sane(OutwardFloor, 0.35f);
internal static float AimStrictnessValue => Sane(AimStrictness, 180f);
internal static float StaminaCostValue => Sane(StaminaCost, 10f);
internal static bool DebugLoggingValue
{
get
{
if (DebugLogging != null)
{
return DebugLogging.Value;
}
return false;
}
}
private static float Sane(MelonPreferences_Entry<float> entry, float fallback)
{
if (entry == null)
{
return fallback;
}
float value = entry.Value;
if (!float.IsNaN(value) && !float.IsInfinity(value))
{
return value;
}
return fallback;
}
internal static void Setup()
{
Category = MelonPreferences.CreateCategory("WallGrabJump", "Wall Grab Jump");
Enabled = Category.CreateEntry<bool>("Enabled", true, "Enabled", "Master switch for the wall jump.", false, false, (ValueValidator)null, (string)null);
JumpStrength = Category.CreateEntry<float>("JumpStrength", 1.8f, "Jump Strength", "Multiplier on the push. 1.0 is a controlled shove off the wall.", false, false, (ValueValidator)null, (string)null);
LookInfluence = Category.CreateEntry<float>("LookInfluence", 2f, "Look Influence", "How strongly your head direction steers the jump.", false, false, (ValueValidator)null, (string)null);
OutwardPush = Category.CreateEntry<float>("OutwardPush", 0.9f, "Outward Push", "How hard the jump shoves away from the wall face. Lower this if jumps feel like being kicked off the wall.", false, false, (ValueValidator)null, (string)null);
UpwardBoost = Category.CreateEntry<float>("UpwardBoost", 1.65f, "Upward Boost", "How much lift is mixed into every jump.", false, false, (ValueValidator)null, (string)null);
OutwardFloor = Category.CreateEntry<float>("OutwardFloor", 0.35f, "Outward Floor", "Guaranteed separation from the wall, whatever you are looking at. 0 removes the floor entirely.", false, false, (ValueValidator)null, (string)null);
AimStrictness = Category.CreateEntry<float>("AimStrictness", 180f, "Aim Strictness", "Max degrees your look may differ from straight off the wall before the jump is refused. 180 = never refuse.", false, false, (ValueValidator)null, (string)null);
StaminaCost = Category.CreateEntry<float>("StaminaCost", 10f, "Stamina Cost %", "Percent of max climbing stamina a wall jump costs. Only used when ClimbStamina is installed.", false, false, (ValueValidator)null, (string)null);
DebugLogging = Category.CreateEntry<bool>("DebugLogging", false, "Debug Logging", "Verbose per-jump logging for troubleshooting.", false, false, (ValueValidator)null, (string)null);
}
}
internal static class RegrabGuard
{
private static float _blockUntil = float.NegativeInfinity;
private static bool _installed;
internal static void Suppress(float seconds)
{
float num = Time.time + seconds;
if (num > _blockUntil)
{
_blockUntil = num;
}
}
internal static void Clear()
{
_blockUntil = float.NegativeInfinity;
}
internal static void ApplyPatches(Harmony harmony)
{
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Expected O, but got Unknown
if (!_installed)
{
MethodInfo method = typeof(Grip).GetMethod("OnGrabConfirm", AccessTools.all);
MethodInfo method2 = typeof(RegrabGuard).GetMethod("OnGrabConfirmPrefix", AccessTools.all);
if (method == null || method2 == null)
{
Log.Warning("Grip.OnGrabConfirm not found; the brief anti-regrab window after a vanilla wall jump is off.");
return;
}
harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
_installed = true;
}
}
private static bool OnGrabConfirmPrefix(Grip __instance, Hand hand)
{
try
{
if (Time.time >= _blockUntil)
{
return true;
}
if ((Object)(object)hand == (Object)null || !PlayerState.IsLocalHand(hand))
{
return true;
}
if ((Object)(object)__instance == (Object)null || !ClimbDetector.IsClimbingGrip(__instance))
{
return true;
}
return false;
}
catch
{
return true;
}
}
}
public class WallGrabJumpMod : MelonMod
{
private static Instance _logger;
private WallJumpController _controller;
private bool _wasEnabled = true;
private Action<LevelInfo> _onLevelLoaded;
private Action _onLevelUnloaded;
private Action<RigManager> _onPlayerDeath;
public override void OnInitializeMelon()
{
_logger = ((MelonBase)this).LoggerInstance;
try
{
Prefs.Setup();
}
catch (Exception ex)
{
Log.Warning("Could not create preferences (" + ex.GetType().Name + "); running on defaults.");
}
_controller = new WallJumpController();
RegrabGuard.ApplyPatches(((MelonBase)this).HarmonyInstance);
ClimbAnythingCompatibility.Initialize(((MelonBase)this).HarmonyInstance);
ClimbStaminaCompatibility.Initialize();
_onLevelLoaded = delegate
{
ResetAll();
};
_onLevelUnloaded = ResetAll;
_onPlayerDeath = delegate(RigManager rig)
{
RigManager rigManager = Player.RigManager;
if ((Object)(object)rig != (Object)null && (Object)(object)rigManager != (Object)null && (Object)(object)rig == (Object)(object)rigManager)
{
ResetAll();
}
};
Hooking.OnLevelLoaded += _onLevelLoaded;
Hooking.OnLevelUnloaded += _onLevelUnloaded;
Hooking.OnPlayerDeath += _onPlayerDeath;
SetupMenu();
Log.Msg("Initialized.");
}
public override void OnLateInitializeMelon()
{
ClimbAnythingCompatibility.Retry(((MelonBase)this).HarmonyInstance);
ClimbStaminaCompatibility.Retry();
}
public override void OnDeinitializeMelon()
{
Hooking.OnLevelLoaded -= _onLevelLoaded;
Hooking.OnLevelUnloaded -= _onLevelUnloaded;
Hooking.OnPlayerDeath -= _onPlayerDeath;
}
public override void OnUpdate()
{
if (_controller == null)
{
return;
}
bool enabledValue = Prefs.EnabledValue;
if (enabledValue != _wasEnabled)
{
_wasEnabled = enabledValue;
if (!enabledValue)
{
ResetAll();
}
}
if (enabledValue)
{
_controller.Tick();
}
}
public override void OnFixedUpdate()
{
if (_controller != null && _wasEnabled)
{
_controller.FixedTick(Time.fixedDeltaTime);
}
}
private void ResetAll()
{
_controller?.Reset();
ClimbAnythingCompatibility.ClearSuppression();
RegrabGuard.Clear();
PlayerState.DropCaches();
}
private static void SetupMenu()
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
try
{
if (Prefs.Category == null)
{
Log.Warning("Preferences are unavailable, so there is no menu to build; the mod runs on defaults.");
return;
}
Page obj = Page.Root.CreatePage("Wall Grab Jump", Color.cyan, 0, true);
obj.CreateBool("Enabled", Color.cyan, Prefs.EnabledValue, (Action<bool>)delegate(bool value)
{
if (Prefs.Enabled != null)
{
Prefs.Enabled.Value = value;
}
});
obj.CreateFloat("Jump Strength", Color.cyan, Prefs.JumpStrengthValue, 0.1f, 0.5f, 3f, (Action<float>)delegate(float value)
{
if (Prefs.JumpStrength != null)
{
Prefs.JumpStrength.Value = value;
}
});
obj.CreateFloat("Upward Boost", Color.cyan, Prefs.UpwardBoostValue, 0.05f, 0f, 3f, (Action<float>)delegate(float value)
{
if (Prefs.UpwardBoost != null)
{
Prefs.UpwardBoost.Value = value;
}
});
obj.CreateFloat("Stamina Cost %", Color.cyan, Prefs.StaminaCostValue, 5f, 0f, 50f, (Action<float>)delegate(float value)
{
if (Prefs.StaminaCost != null)
{
Prefs.StaminaCost.Value = value;
}
});
}
catch (Exception ex)
{
Log.Warning("Could not build the BoneMenu page (" + ex.GetType().Name + "); preferences still work from the config file.");
}
}
internal static void LogMsg(string message)
{
Instance logger = _logger;
if (logger != null)
{
logger.Msg(message);
}
}
internal static void LogWarn(string message)
{
Instance logger = _logger;
if (logger != null)
{
logger.Warning(message);
}
}
}
internal static class Log
{
internal static void Msg(string message)
{
WallGrabJumpMod.LogMsg(message);
}
internal static void Warning(string message)
{
WallGrabJumpMod.LogWarn(message);
}
internal static void Debug(string message)
{
if (Prefs.DebugLoggingValue)
{
WallGrabJumpMod.LogMsg("[debug] " + message);
}
}
}
internal sealed class WallJumpController
{
private const float BaseJumpSpeed = 3.5f;
private const float AbsoluteMaxDeltaV = 16f;
private const float OutwardInfluence = 0.9f;
private const float UpInfluence = 0.55f;
private const float MinOutwardDot = 0.35f;
private const float MinVerticalComponent = -0.3f;
private const float MinOutwardVertical = -0.25f;
private const float MinOutwardHorizontal = 0.2f;
private const float MaxIntoWallCancel = 1.5f;
private const float PushDuration = 0.09f;
private const float CooldownSeconds = 0.25f;
private const float RegrabBlanketSeconds = 0.3f;
private const float SuccessAmplitude = 0.55f;
private const float SuccessSeconds = 0.09f;
private const float SuccessFrequency = 150f;
private const float DeniedAmplitude = 0.22f;
private const float DeniedSeconds = 0.035f;
private const float DeniedFrequency = 110f;
private readonly InputController _input = new InputController();
private float _readyAt = float.NegativeInfinity;
private bool _pushing;
private Vector3 _pushDirection;
private float _pushBudget;
private float _pushApplied;
private float _pushElapsed;
internal void Reset()
{
_input.Reset();
_readyAt = float.NegativeInfinity;
CancelPush();
}
private void CancelPush()
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
_pushing = false;
_pushDirection = Vector3.zero;
_pushBudget = 0f;
_pushApplied = 0f;
_pushElapsed = 0f;
}
internal void Tick()
{
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
ClimbAnythingCompatibility.Tick();
if (!_input.PressedThisFrame() || !PlayerState.IsValid || Time.time < _readyAt || ClimbDetector.Snapshot(out var left, out var right) == 0)
{
return;
}
if (ClimbStaminaCompatibility.ClimbingBlocked)
{
Deny(left, right);
Log.Debug("Wall jump refused: ClimbStamina has climbing blocked");
return;
}
if (!ClimbStaminaCompatibility.CanAfford())
{
Deny(left, right);
Log.Debug("Wall jump refused: not enough stamina");
return;
}
float num = Mathf.Clamp(Prefs.AimStrictnessValue, 0f, 180f);
if (num < 179.5f)
{
Vector3 val = FindOutward(left, right);
Vector3 val2 = LookDirection();
if (val != Vector3.zero && val2 != Vector3.zero && Vector3.Angle(val2, val) > num)
{
Deny(left, right);
Log.Debug($"Wall jump refused: looking {Vector3.Angle(val2, val):0} deg off the wall (limit {num:0})");
return;
}
}
Execute(left, right);
}
private void Deny(ClimbHold left, ClimbHold right)
{
_readyAt = Time.time + 0.25f;
if (left.Valid)
{
Pulse((Handedness)1, 0.035f, 110f, 0.22f);
}
if (right.Valid)
{
Pulse((Handedness)2, 0.035f, 110f, 0.22f);
}
}
private void Execute(ClimbHold left, ClimbHold right)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_026b: Unknown result type (might be due to invalid IL or missing references)
//IL_026c: Unknown result type (might be due to invalid IL or missing references)
//IL_028f: Unknown result type (might be due to invalid IL or missing references)
//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = FindOutward(left, right);
Vector3 val2 = Compose(LookDirection(), val) * (3.5f * Mathf.Clamp(Prefs.JumpStrengthValue, 0.05f, 6f));
if (val != Vector3.zero)
{
float num = Vector3.Dot(PlayerState.Velocity, -val);
if (num > 0f)
{
val2 += val * Mathf.Min(num, 1.5f);
}
}
float num2 = ((Vector3)(ref val2)).magnitude;
if (!IsFinite(num2) || !IsFinite(val2.x) || !IsFinite(val2.y) || !IsFinite(val2.z))
{
Log.Debug("Wall jump aborted: computed a non-finite velocity");
return;
}
if (num2 > 16f)
{
val2 = val2 / num2 * 16f;
num2 = 16f;
}
if (num2 < 0.01f)
{
return;
}
int num3 = Release(left) + Release(right);
if (num3 == 0)
{
_readyAt = Time.time + 0.25f;
Log.Debug("Wall jump aborted: nothing could be released");
return;
}
ClimbStaminaCompatibility.Spend();
ClimbAnythingCompatibility.SuppressAfterJump(left, right, 0.3f);
RegrabGuard.Suppress(0.3f);
_pushDirection = val2 / num2;
_pushBudget = num2;
_pushApplied = 0f;
_pushElapsed = 0f;
_pushing = true;
_readyAt = Time.time + 0.25f;
if (left.Valid)
{
Pulse((Handedness)1, 0.09f, 150f, 0.55f);
}
if (right.Valid)
{
Pulse((Handedness)2, 0.09f, 150f, 0.55f);
}
Log.Debug($"Wall jump: {num3} hand(s) released, dV={num2:0.00} m/s, dir=({_pushDirection.x:0.00}, {_pushDirection.y:0.00}, {_pushDirection.z:0.00}), outward={((val == Vector3.zero) ? "none" : $"({val.x:0.00}, {val.y:0.00}, {val.z:0.00})")}");
}
private static int Release(ClimbHold hold)
{
if (!hold.Valid)
{
return 0;
}
try
{
switch (hold.Source)
{
case ClimbSource.Vanilla:
if ((Object)(object)hold.Grip == (Object)null || (Object)(object)hold.Hand == (Object)null)
{
return 0;
}
hold.Grip.ForceDetach(hold.Hand);
return 1;
case ClimbSource.ClimbAnything:
return ClimbAnythingCompatibility.Release(hold) ? 1 : 0;
}
}
catch (Exception ex)
{
Log.Debug("Release failed (" + ex.GetType().Name + ")");
}
return 0;
}
private static bool IsFinite(float value)
{
if (!float.IsNaN(value))
{
return !float.IsInfinity(value);
}
return false;
}
private static Vector3 LookDirection()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
Transform head = PlayerState.Head;
if ((Object)(object)head == (Object)null)
{
return Vector3.zero;
}
Vector3 forward = head.forward;
if (!(((Vector3)(ref forward)).sqrMagnitude < 1E-06f))
{
return ((Vector3)(ref forward)).normalized;
}
return Vector3.zero;
}
internal static Vector3 Compose(Vector3 look, Vector3 outward)
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_016e: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_0171: Unknown result type (might be due to invalid IL or missing references)
//IL_0176: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
float num = Mathf.Clamp(Prefs.LookInfluenceValue, 0f, 5f);
float num2 = Mathf.Clamp(Prefs.OutwardPushValue, 0f, 5f);
float num3 = Mathf.Clamp(Prefs.UpwardBoostValue, 0f, 5f);
Vector3 val = look * num + outward * num2 + Vector3.up * num3;
if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
{
val = ((outward != Vector3.zero) ? (outward + Vector3.up * 0.5f) : Vector3.up);
}
Vector3 normalized = ((Vector3)(ref val)).normalized;
normalized = ClampVertical(normalized, outward);
float num4 = Mathf.Clamp(Prefs.OutwardFloorValue, 0f, 0.95f);
if (outward != Vector3.zero && num4 > 0f)
{
float num5 = Vector3.Dot(normalized, outward);
if (num5 < num4)
{
Vector3 val2 = normalized - outward * num5;
if (((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
{
val2 = Vector3.up - outward * outward.y;
}
if (((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
{
return outward;
}
Vector3 val3 = outward * num4 + ((Vector3)(ref val2)).normalized * Mathf.Sqrt(1f - num4 * num4);
normalized = ((Vector3)(ref val3)).normalized;
normalized = ClampVertical(normalized, outward);
}
}
return normalized;
}
private static Vector3 ClampVertical(Vector3 direction, Vector3 outward)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
if (direction.y >= -0.3f)
{
return direction;
}
Vector3 val = default(Vector3);
((Vector3)(ref val))..ctor(direction.x, 0f, direction.z);
if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
{
((Vector3)(ref val))..ctor(outward.x, 0f, outward.z);
}
if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
{
return Vector3.up;
}
float num = -0.3f;
val = ((Vector3)(ref val)).normalized * Mathf.Sqrt(1f - num * num);
return new Vector3(val.x, num, val.z);
}
private static Vector3 FindOutward(ClimbHold left, ClimbHold right)
{
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
Vector3 val;
if (left.Valid && right.Valid)
{
val = (left.Anchor + right.Anchor) * 0.5f;
}
else if (left.Valid)
{
val = left.Anchor;
}
else
{
if (!right.Valid)
{
return Vector3.zero;
}
val = right.Anchor;
}
Vector3 bodyCentre = PlayerState.BodyCentre;
if (TryCastToSurface(bodyCentre, val, out var normal) || TryClosestPoint(left, right, bodyCentre, out normal) || PlayerState.TryGetMarrowClimbNormal(out normal))
{
Vector3 val2 = LimitDownward(normal);
if (val2 != Vector3.zero)
{
return val2;
}
}
Vector3 val3 = bodyCentre - val;
val3.y = 0f;
if (((Vector3)(ref val3)).sqrMagnitude > 0.0025f)
{
return ((Vector3)(ref val3)).normalized;
}
return Vector3.zero;
}
private static bool TryCastToSurface(Vector3 body, Vector3 anchor, out Vector3 normal)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
normal = Vector3.zero;
Vector3 val = anchor - body;
float magnitude = ((Vector3)(ref val)).magnitude;
if (magnitude < 0.05f)
{
return false;
}
try
{
Transform val2 = null;
PhysicsRig physicsRig = Player.PhysicsRig;
if ((Object)(object)physicsRig != (Object)null)
{
val2 = ((Component)physicsRig).transform.root;
}
Il2CppStructArray<RaycastHit> val3 = Physics.RaycastAll(body, val / magnitude, magnitude + 0.4f, -5, (QueryTriggerInteraction)1);
if (val3 == null)
{
return false;
}
float num = float.MaxValue;
float num2 = magnitude * 0.5f;
for (int i = 0; i < ((Il2CppArrayBase<RaycastHit>)(object)val3).Length; i++)
{
RaycastHit val4 = ((Il2CppArrayBase<RaycastHit>)(object)val3)[i];
if (((RaycastHit)(ref val4)).distance < num2 || ((RaycastHit)(ref val4)).distance >= num)
{
continue;
}
Collider collider = ((RaycastHit)(ref val4)).collider;
if (!((Object)(object)collider == (Object)null) && (!((Object)(object)val2 != (Object)null) || !((Object)(object)((Component)collider).transform.root == (Object)(object)val2)))
{
Vector3 normal2 = ((RaycastHit)(ref val4)).normal;
if (!(((Vector3)(ref normal2)).sqrMagnitude < 0.25f))
{
num = ((RaycastHit)(ref val4)).distance;
normal2 = ((RaycastHit)(ref val4)).normal;
normal = ((Vector3)(ref normal2)).normalized;
}
}
}
return num < float.MaxValue;
}
catch (Exception ex)
{
Log.Debug("Surface cast failed (" + ex.GetType().Name + ")");
return false;
}
}
private static bool TryClosestPoint(ClimbHold left, ClimbHold right, Vector3 body, out Vector3 normal)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
normal = Vector3.zero;
Vector3 val = Vector3.zero;
int num = 0;
for (int i = 0; i < 2; i++)
{
ClimbHold climbHold = ((i == 0) ? left : right);
if (!climbHold.Valid || (Object)(object)climbHold.Surface == (Object)null)
{
continue;
}
try
{
Vector3 val2 = body - climbHold.Surface.ClosestPoint(body);
if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0004f))
{
val += ((Vector3)(ref val2)).normalized;
num++;
}
}
catch
{
}
}
if (num == 0 || ((Vector3)(ref val)).sqrMagnitude < 1E-06f)
{
return false;
}
normal = ((Vector3)(ref val)).normalized;
return true;
}
private static Vector3 LimitDownward(Vector3 normal)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
if (normal.y >= -0.25f)
{
return normal;
}
Vector3 val = default(Vector3);
((Vector3)(ref val))..ctor(normal.x, 0f, normal.z);
if (((Vector3)(ref val)).sqrMagnitude < 0.040000003f)
{
return Vector3.zero;
}
float num = -0.25f;
val = ((Vector3)(ref val)).normalized * Mathf.Sqrt(1f - num * num);
return new Vector3(val.x, num, val.z);
}
internal void FixedTick(float fixedDeltaTime)
{
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
if (!_pushing)
{
return;
}
if (fixedDeltaTime <= 0f || !PlayerState.IsValid)
{
CancelPush();
return;
}
Rigidbody[] array = PlayerState.RigBodies();
if (array == null || array.Length == 0)
{
CancelPush();
return;
}
_pushElapsed += fixedDeltaTime;
float num = _pushBudget * (fixedDeltaTime / 0.09f);
float num2 = _pushBudget - _pushApplied;
if (num > num2)
{
num = num2;
}
if (num > 1E-05f)
{
_pushApplied += num;
Vector3 val = _pushDirection * num;
foreach (Rigidbody val2 in array)
{
if (!((Object)(object)val2 == (Object)null) && !val2.isKinematic)
{
val2.AddForce(val, (ForceMode)2);
}
}
}
if (_pushApplied >= _pushBudget - 1E-05f || _pushElapsed >= 0.09f)
{
CancelPush();
}
}
private static void Pulse(Handedness side, float seconds, float frequency, float amplitude)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
try
{
if (!Player.ControllersExist)
{
return;
}
BaseController val = (((int)side == 1) ? Player.LeftController : Player.RightController);
if (!((Object)(object)val == (Object)null))
{
Haptor haptor = val.haptor;
if (!((Object)(object)haptor == (Object)null) && haptor.hapticsAllowed)
{
haptor.SENDHAPTIC(0f, seconds, frequency, Mathf.Clamp01(amplitude));
}
}
}
catch (Exception ex)
{
Log.Debug("Haptic pulse failed (" + ex.GetType().Name + ")");
}
}
}
}
namespace WallGrabJump.Compatibility
{
internal static class ClimbAnythingCompatibility
{
private const float GripHeldThreshold = 0.01f;
private const float UnreadableGripGrace = 1.5f;
private static bool _looked;
private static Harmony _harmony;
private static object _melon;
private static FieldInfo _leftStateField;
private static FieldInfo _rightStateField;
private static FieldInfo _handField;
private static FieldInfo _climbPointField;
private static FieldInfo _isClimbingField;
private static MethodInfo _stopClimbing;
private static object _leftState;
private static object _rightState;
private static bool _leftSuppressed;
private static bool _rightSuppressed;
private static float _leftUnreadableSince = -1f;
private static float _rightUnreadableSince = -1f;
private static float _blockAllUntil = float.NegativeInfinity;
private static int _faults;
private static float _lastFaultAt = float.NegativeInfinity;
private const int MaxFaults = 5;
private const float FaultWindow = 10f;
internal static bool Active { get; private set; }
internal static void Initialize(Harmony harmony)
{
_harmony = harmony;
if (!_looked)
{
MelonBase val = MelonBase.FindMelon("ClimbAnything", "Waspothegreat");
if (val != null)
{
_looked = true;
TryBind(val);
}
}
}
internal static void Retry(Harmony harmony)
{
if (_harmony == null)
{
_harmony = harmony;
}
if (!_looked && _harmony != null)
{
_looked = true;
MelonBase val = MelonBase.FindMelon("ClimbAnything", "Waspothegreat");
if (val != null)
{
TryBind(val);
}
}
}
private static void TryBind(MelonBase melon)
{
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Expected O, but got Unknown
try
{
Type type = ((object)melon).GetType();
_leftStateField = type.GetField("_leftHandState", BindingFlags.Instance | BindingFlags.NonPublic);
_rightStateField = type.GetField("_rightHandState", BindingFlags.Instance | BindingFlags.NonPublic);
Type nestedType = type.GetNestedType("HandState", BindingFlags.Public | BindingFlags.NonPublic);
_handField = nestedType?.GetField("Hand", BindingFlags.Instance | BindingFlags.Public);
_climbPointField = nestedType?.GetField("ClimbPoint", BindingFlags.Instance | BindingFlags.Public);
_isClimbingField = nestedType?.GetField("IsClimbing", BindingFlags.Instance | BindingFlags.Public);
MethodInfo method = type.GetMethod("StartClimbing", BindingFlags.Instance | BindingFlags.NonPublic);
_stopClimbing = type.GetMethod("StopClimbing", BindingFlags.Instance | BindingFlags.NonPublic);
if (!ShapeIsSupported(method, nestedType))
{
Log.Warning("ClimbAnything is installed but its internals don't match the supported layout (verified against 1.0.0 / package 1.0.1). Wall jumping from ClimbAnything holds is disabled; vanilla climbing still works and ClimbAnything itself is untouched.");
return;
}
_melon = melon;
_harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(ClimbAnythingCompatibility), "StartClimbingPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Active = true;
MelonInfoAttribute info = melon.Info;
Log.Msg("ClimbAnything " + (((info != null) ? info.Version : null) ?? "?") + " detected — wall jumping works from its holds, and a hand that jumps needs a fresh grip before it can climb again.");
}
catch (Exception ex)
{
Active = false;
Log.Warning($"Could not set up ClimbAnything compatibility ({ex.GetType().Name}: {ex.Message}). " + "Vanilla wall jumping still works; ClimbAnything itself is untouched.");
}
}
private static bool ShapeIsSupported(MethodInfo startClimbing, Type handStateType)
{
if (handStateType == null || startClimbing == null || _stopClimbing == null)
{
return false;
}
if (_leftStateField == null || _rightStateField == null)
{
return false;
}
if (_handField == null || _climbPointField == null || _isClimbingField == null)
{
return false;
}
if (_handField.FieldType != typeof(Hand))
{
return false;
}
if (_isClimbingField.FieldType != typeof(bool))
{
return false;
}
if (!typeof(Object).IsAssignableFrom(_climbPointField.FieldType))
{
return false;
}
ParameterInfo[] parameters = startClimbing.GetParameters();
if (parameters.Length != 2 || parameters[0].ParameterType != handStateType)
{
return false;
}
ParameterInfo[] parameters2 = _stopClimbing.GetParameters();
if (parameters2.Length != 1 || parameters2[0].ParameterType != handStateType)
{
return false;
}
return true;
}
internal static ClimbHold Evaluate(Hand hand, Handedness side)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
if (!Active || (Object)(object)hand == (Object)null)
{
return default(ClimbHold);
}
try
{
object obj = StateFor(side);
if (obj == null)
{
return default(ClimbHold);
}
if (!(bool)_isClimbingField.GetValue(obj))
{
return default(ClimbHold);
}
object? value = _climbPointField.GetValue(obj);
Object val = (Object)((value is Object) ? value : null);
if (val == (Object)null)
{
return default(ClimbHold);
}
object? value2 = _handField.GetValue(obj);
Hand val2 = (Hand)((value2 is Hand) ? value2 : null);
if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)hand)
{
return default(ClimbHold);
}
ClimbHold result = new ClimbHold
{
Source = ClimbSource.ClimbAnything,
Side = side,
Hand = hand,
State = obj,
Anchor = ((Component)hand).transform.position
};
GameObject val3 = ((Il2CppObjectBase)val).TryCast<GameObject>();
if ((Object)(object)val3 != (Object)null)
{
result.Anchor = val3.transform.position;
Transform parent = val3.transform.parent;
if ((Object)(object)parent != (Object)null)
{
result.Surface = ((Component)parent).GetComponent<Collider>();
}
}
return result;
}
catch (Exception e)
{
Fault(e);
return default(ClimbHold);
}
}
private static object StateFor(Handedness side)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Invalid comparison between Unknown and I4
if (_melon == null)
{
return null;
}
if ((int)side == 1)
{
return _leftState ?? (_leftState = _leftStateField.GetValue(_melon));
}
if ((int)side == 2)
{
return _rightState ?? (_rightState = _rightStateField.GetValue(_melon));
}
return null;
}
internal static bool Release(ClimbHold hold)
{
if (!Active || hold.Source != ClimbSource.ClimbAnything || hold.State == null)
{
return false;
}
try
{
_stopClimbing.Invoke(_melon, new object[1] { hold.State });
return true;
}
catch (Exception e)
{
Fault(e);
return false;
}
}
internal static void SuppressAfterJump(ClimbHold left, ClimbHold right, float blanketSeconds)
{
_blockAllUntil = Time.time + blanketSeconds;
if (left.Valid)
{
_leftSuppressed = true;
_leftUnreadableSince = -1f;
}
if (right.Valid)
{
_rightSuppressed = true;
_rightUnreadableSince = -1f;
}
}
internal static void Tick()
{
if (_leftSuppressed || _rightSuppressed)
{
if (_leftSuppressed && GripReleased((Handedness)1, ref _leftUnreadableSince))
{
_leftSuppressed = false;
Log.Debug("Left hand may climb again (grip released)");
}
if (_rightSuppressed && GripReleased((Handedness)2, ref _rightUnreadableSince))
{
_rightSuppressed = false;
Log.Debug("Right hand may climb again (grip released)");
}
}
}
private static bool GripReleased(Handedness side, ref float unreadableSince)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Invalid comparison between Unknown and I4
float time = Time.time;
try
{
BaseController val = null;
if (Player.ControllersExist)
{
val = (((int)side == 1) ? Player.LeftController : Player.RightController);
}
if ((Object)(object)val == (Object)null)
{
if (unreadableSince < 0f)
{
unreadableSince = time;
}
return time - unreadableSince >= 1.5f;
}
unreadableSince = -1f;
return val.GetGripForce() <= 0.01f;
}
catch
{
if (unreadableSince < 0f)
{
unreadableSince = time;
}
return time - unreadableSince >= 1.5f;
}
}
internal static void ClearSuppression()
{
_leftSuppressed = false;
_rightSuppressed = false;
_leftUnreadableSince = -1f;
_rightUnreadableSince = -1f;
_blockAllUntil = float.NegativeInfinity;
}
private static bool StartClimbingPrefix(object __0)
{
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Invalid comparison between Unknown and I4
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Invalid comparison between Unknown and I4
if (!Active)
{
return true;
}
if (!_leftSuppressed && !_rightSuppressed && Time.time >= _blockAllUntil)
{
return true;
}
try
{
if (Time.time < _blockAllUntil)
{
return false;
}
if (__0 == _leftState)
{
return !_leftSuppressed;
}
if (__0 == _rightState)
{
return !_rightSuppressed;
}
object? value = _handField.GetValue(__0);
Hand val = (Hand)((value is Hand) ? value : null);
if ((Object)(object)val == (Object)null)
{
return true;
}
Handedness handedness = val.handedness;
if ((int)handedness != 1)
{
if ((int)handedness == 2)
{
return !_rightSuppressed;
}
return true;
}
return !_leftSuppressed;
}
catch (Exception e)
{
Fault(e);
return true;
}
}
private static void Fault(Exception e)
{
float realtimeSinceStartup = Time.realtimeSinceStartup;
if (realtimeSinceStartup - _lastFaultAt > 10f)
{
_faults = 0;
}
_lastFaultAt = realtimeSinceStartup;
_faults++;
if (_faults == 1)
{
Log.Warning("Error talking to ClimbAnything: " + e.GetType().Name + ": " + e.Message);
}
if (_faults >= 5 && Active)
{
Active = false;
ClearSuppression();
Log.Warning("Disabling ClimbAnything compatibility after repeated errors. Vanilla wall jumping continues, and ClimbAnything runs exactly as it does unpatched.");
}
}
}
internal static class ClimbStaminaCompatibility
{
private static bool _looked;
private static object _controller;
private static FieldInfo _staminaField;
private static FieldInfo _drainField;
private static PropertyInfo _maxProp;
private static FieldInfo _blockedField;
private static FieldInfo _enabledField;
private static MelonPreferences_Entry<bool> _enabledEntry;
private static int _faults;
private const int MaxFaults = 3;
internal static bool Active { get; private set; }
internal static bool ClimbingBlocked
{
get
{
if (!Active)
{
return false;
}
try
{
return (bool)_blockedField.GetValue(null);
}
catch (Exception e)
{
Fault(e);
return false;
}
}
}
internal static void Initialize()
{
if (!_looked && MelonBase.FindMelon("ClimbStamina", "Cesar") != null && TryBind(final: false))
{
_looked = true;
}
}
internal static void Retry()
{
if (!_looked)
{
_looked = true;
if (MelonBase.FindMelon("ClimbStamina", "Cesar") != null)
{
TryBind(final: true);
}
}
}
private static bool TryBind(bool final)
{
try
{
MelonBase val = MelonBase.FindMelon("ClimbStamina", "Cesar");
if (val == null)
{
return false;
}
Type type = ((object)val).GetType();
_enabledField = type.Assembly.GetType("ClimbStamina.Prefs")?.GetField("Enabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (_enabledField != null && _enabledField.FieldType != typeof(MelonPreferences_Entry<bool>))
{
_enabledField = null;
}
_blockedField = type.GetField("ClimbBlocked", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
object obj = type.GetField("_stamina", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(val);
if (obj == null)
{
if (final)
{
Log.Warning("ClimbStamina is installed but its stamina controller could not be reached; wall jumping will not cost stamina. ClimbStamina itself is untouched.");
}
return false;
}
Type type2 = obj.GetType();
_staminaField = type2.GetField("_stamina", BindingFlags.Instance | BindingFlags.NonPublic);
_drainField = type2.GetField("_timeSinceDrain", BindingFlags.Instance | BindingFlags.NonPublic);
_maxProp = type2.GetProperty("MaxStamina", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (!ShapeIsSupported())
{
Log.Warning("ClimbStamina is installed but its internals don't match the supported layout (verified against 1.3.4). Wall jumping will not cost stamina; ClimbStamina itself is untouched.");
return true;
}
_controller = obj;
Active = true;
MelonInfoAttribute info = val.Info;
Log.Msg("ClimbStamina " + (((info != null) ? info.Version : null) ?? "?") + " detected — wall jumps draw from its stamina pool.");
return true;
}
catch (Exception ex)
{
Active = false;
Log.Warning($"Could not set up ClimbStamina integration ({ex.GetType().Name}: {ex.Message}). " + "Wall jumping works and costs nothing; ClimbStamina itself is untouched.");
return true;
}
}
private static bool ShapeIsSupported()
{
if (_staminaField == null || _staminaField.FieldType != typeof(float))
{
return false;
}
if (_drainField == null || _drainField.FieldType != typeof(float))
{
return false;
}
if (_maxProp == null || _maxProp.PropertyType != typeof(float) || !_maxProp.CanRead)
{
return false;
}
if (_blockedField == null || _blockedField.FieldType != typeof(bool))
{
return false;
}
return true;
}
private static bool PoolIsLive()
{
if (_enabledField == null)
{
return true;
}
try
{
if (_enabledEntry == null)
{
_enabledEntry = _enabledField.GetValue(null) as MelonPreferences_Entry<bool>;
}
return _enabledEntry == null || _enabledEntry.Value;
}
catch (Exception e)
{
Fault(e);
return true;
}
}
internal static float CostOfJump()
{
if (!Active || !PoolIsLive())
{
return 0f;
}
try
{
float num = (float)_maxProp.GetValue(_controller);
float num2 = Mathf.Clamp(Prefs.StaminaCostValue, 0f, 100f);
return num * (num2 / 100f);
}
catch (Exception e)
{
Fault(e);
return 0f;
}
}
internal static float Current()
{
if (!Active)
{
return float.MaxValue;
}
try
{
return (float)_staminaField.GetValue(_controller);
}
catch (Exception e)
{
Fault(e);
return float.MaxValue;
}
}
internal static bool CanAfford()
{
if (!Active)
{
return true;
}
float num = CostOfJump();
if (!(num <= 0f))
{
return Current() >= num;
}
return true;
}
internal static void Spend()
{
if (!Active)
{
return;
}
try
{
float num = CostOfJump();
if (!(num <= 0f))
{
float num2 = (float)_staminaField.GetValue(_controller);
float num3 = Mathf.Max(0f, num2 - num);
_staminaField.SetValue(_controller, num3);
_drainField.SetValue(_controller, 0f);
Log.Debug($"Wall jump cost {num:0.0} stamina ({num2:0.0} -> {num3:0.0})");
}
}
catch (Exception e)
{
Fault(e);
}
}
private static void Fault(Exception e)
{
_faults++;
if (_faults == 1)
{
Log.Warning("ClimbStamina integration error: " + e.GetType().Name + ": " + e.Message);
}
if (_faults >= 3 && Active)
{
Active = false;
_controller = null;
Log.Warning("Disabling the ClimbStamina integration after repeated errors. Wall jumping continues without a stamina cost; ClimbStamina itself is untouched.");
}
}
}
}