using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("RealControl")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RealControl")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("486de57c-6d4c-406a-adf7-a4433b917004")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [BepInPlugin("Sapphire009.RealControl", "RealControl", "1.0.2")] public sealed class RealControlPlugin : BaseUnityPlugin { private enum ExpectedHand { Left, Right } private enum StepDirection { Up, Down, Left, Right } private sealed class InputState { public bool WasControlledWallClimbing; public float LastPrimaryPressedTime = float.NegativeInfinity; public ExpectedHand NextHand = ExpectedHand.Left; } [HarmonyPatch(typeof(CharacterInput), "Sample", new Type[] { typeof(bool) })] private static class CharacterInputSamplePatch { [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Sapphire009.WASDX", "Sapphire009.JumpingPEAK" })] private static void Postfix(CharacterInput __instance) { //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_004a: 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_0059: 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) if ((Object)(object)__instance == (Object)null) { return; } Character component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsLocal) { return; } InputState orCreateState = GetOrCreateState(__instance); Vector2 movementInput = __instance.movementInput; bool upHeld = ReadUpHeld(movementInput); bool leftHeld = ReadLeftHeld(movementInput); bool rightHeld = ReadRightHeld(movementInput); bool downHeld = ReadDownHeld(movementInput); if (!IsControlledWallClimbing(component)) { if (__instance.usePrimaryWasPressed) { orCreateState.LastPrimaryPressedTime = Time.unscaledTime; } orCreateState.WasControlledWallClimbing = false; } else { if (!orCreateState.WasControlledWallClimbing) { BeginControlledClimb(component, orCreateState); } ProcessControlledClimbInput(__instance, component, orCreateState, upHeld, leftHeld, rightHeld, downHeld); } } } public const string PluginGUID = "Sapphire009.RealControl"; public const string PluginName = "RealControl"; public const string PluginVersion = "1.0.2"; private const string BuildMarker = "DirectionalAlternatingInput-Stamina-R3"; private const float NormalGrabDetectionWindow = 0.75f; private const float MinimumDirectionSqrMagnitude = 0.0001f; private const float StepStaminaCost = 0.05f; internal static ManualLogSource ModLogger; private static ConfigEntry upStepDistance; private static ConfigEntry sideStepDistance; private static ConfigEntry downStepDistance; private static ConfigEntry logSuccessfulSteps; private static readonly Dictionary InputStates = new Dictionary(); private Harmony harmony; private void Awake() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown ModLogger = ((BaseUnityPlugin)this).Logger; upStepDistance = ((BaseUnityPlugin)this).Config.Bind("Climb Step", "UpStepDistance", 1.5f, "W를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 위로 이동할 거리입니다."); sideStepDistance = ((BaseUnityPlugin)this).Config.Bind("Climb Step", "SideStepDistance", 1.5f, "A 또는 D를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 좌우로 이동할 거리입니다."); downStepDistance = ((BaseUnityPlugin)this).Config.Bind("Climb Step", "DownStepDistance", 1.5f, "S를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 아래로 이동할 거리입니다."); logSuccessfulSteps = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogSuccessfulSteps", false, "true이면 적용된 등반 스텝을 로그에 기록합니다."); harmony = new Harmony("Sapphire009.RealControl"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RealControl 1.0.2 loaded. Developer=Sapphire009. Build=DirectionalAlternatingInput-Stamina-R3."); } private void OnDestroy() { InputStates.Clear(); if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } ModLogger = null; } private static InputState GetOrCreateState(CharacterInput characterInput) { int instanceID = ((Object)characterInput).GetInstanceID(); if (!InputStates.TryGetValue(instanceID, out var value)) { value = new InputState(); InputStates.Add(instanceID, value); } return value; } private static bool IsControlledWallClimbing(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null || character.refs == null || (Object)(object)character.refs.climbing == (Object)null) { return false; } return character.data.isClimbing && !character.data.isRopeClimbing && !character.data.isVineClimbing && (Object)(object)character.data.currentClimbHandle == (Object)null; } private static bool ReadUpHeld(Vector2 sampledMovement) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (CharacterInput.action_moveForward != null) { return CharacterInput.action_moveForward.IsPressed(); } return sampledMovement.y > 0.5f; } private static bool ReadLeftHeld(Vector2 sampledMovement) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (CharacterInput.action_moveLeft != null) { return CharacterInput.action_moveLeft.IsPressed(); } return sampledMovement.x < -0.5f; } private static bool ReadRightHeld(Vector2 sampledMovement) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (CharacterInput.action_moveRight != null) { return CharacterInput.action_moveRight.IsPressed(); } return sampledMovement.x > 0.5f; } private static bool ReadDownHeld(Vector2 sampledMovement) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (CharacterInput.action_moveBackward != null) { return CharacterInput.action_moveBackward.IsPressed(); } return sampledMovement.y < -0.5f; } private static bool TryGetHeldDirection(bool upHeld, bool leftHeld, bool rightHeld, bool downHeld, out StepDirection stepDirection) { stepDirection = StepDirection.Up; int num = 0; if (upHeld) { stepDirection = StepDirection.Up; num++; } if (downHeld) { stepDirection = StepDirection.Down; num++; } if (leftHeld) { stepDirection = StepDirection.Left; num++; } if (rightHeld) { stepDirection = StepDirection.Right; num++; } return num == 1; } private static void BeginControlledClimb(Character character, InputState state) { //IL_0030: 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) bool flag = Time.unscaledTime - state.LastPrimaryPressedTime <= 0.75f; state.NextHand = (flag ? ExpectedHand.Right : ExpectedHand.Left); character.refs.climbing.playerSlide = Vector2.zero; state.WasControlledWallClimbing = true; } private static bool HasEnoughStepStamina(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return false; } float num = Mathf.Max(0f, character.data.currentStamina) + Mathf.Max(0f, character.data.extraStamina); return num + 0.0001f >= 0.05f; } private static bool ConsumeStepStamina(Character character) { if (!HasEnoughStepStamina(character)) { return false; } float num = 0.05f; float num2 = Mathf.Min(Mathf.Max(0f, character.data.currentStamina), num); CharacterData data = character.data; data.currentStamina -= num2; num -= num2; if (num > 0f) { float num3 = Mathf.Min(Mathf.Max(0f, character.data.extraStamina), num); CharacterData data2 = character.data; data2.extraStamina -= num3; num -= num3; } character.ClampStamina(); character.data.extraStamina = Mathf.Max(0f, character.data.extraStamina); character.data.sinceUseStamina = 0f; if ((Object)(object)GUIManager.instance != (Object)null && (Object)(object)GUIManager.instance.bar != (Object)null) { GUIManager.instance.bar.ChangeBar(); } return num <= 0.0001f; } private static bool TryApplyStep(Character character, StepDirection stepDirection) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0055: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) if (!IsControlledWallClimbing(character)) { return false; } Vector3 climbNormal = character.data.climbNormal; if (((Vector3)(ref climbNormal)).sqrMagnitude < 0.0001f) { return false; } ((Vector3)(ref climbNormal)).Normalize(); Vector3 val = Vector3.ProjectOnPlane(Vector3.up, climbNormal); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { return false; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = Vector3.Cross(climbNormal, val); if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { return false; } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.zero; switch (stepDirection) { case StepDirection.Up: val3 = val * Mathf.Max(0.01f, upStepDistance.Value); break; case StepDirection.Down: val3 = -val * Mathf.Max(0.01f, downStepDistance.Value); break; case StepDirection.Left: val3 = -val2 * Mathf.Max(0.01f, sideStepDistance.Value); break; case StepDirection.Right: val3 = val2 * Mathf.Max(0.01f, sideStepDistance.Value); break; } if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f) { return false; } if (!ConsumeStepStamina(character)) { return false; } character.refs.climbing.playerSlide = Vector2.zero; CharacterData data = character.data; data.climbPos += val3; character.data.sinceGrounded = 0f; if (logSuccessfulSteps.Value) { ManualLogSource modLogger = ModLogger; if (modLogger != null) { modLogger.LogInfo((object)($"[RealControl] Step={stepDirection}, " + $"Distance={((Vector3)(ref val3)).magnitude:0.000}, " + $"StaminaCost={0.05f:0.00}, " + $"CurrentStamina={character.data.currentStamina:0.000}, " + $"ExtraStamina={character.data.extraStamina:0.000}, " + $"ClimbPos={character.data.climbPos}")); } } return true; } private static void ProcessControlledClimbInput(CharacterInput characterInput, Character character, InputState state, bool upHeld, bool leftHeld, bool rightHeld, bool downHeld) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) bool usePrimaryWasPressed = characterInput.usePrimaryWasPressed; bool useSecondaryWasPressed = characterInput.useSecondaryWasPressed; characterInput.movementInput = Vector2.zero; characterInput.usePrimaryWasReleased = false; if (((state.NextHand == ExpectedHand.Left) ? usePrimaryWasPressed : useSecondaryWasPressed) && TryGetHeldDirection(upHeld, leftHeld, rightHeld, downHeld, out var stepDirection) && TryApplyStep(character, stepDirection)) { state.NextHand = ((state.NextHand == ExpectedHand.Left) ? ExpectedHand.Right : ExpectedHand.Left); } characterInput.usePrimaryWasPressed = false; characterInput.usePrimaryIsPressed = false; characterInput.useSecondaryWasPressed = false; characterInput.useSecondaryIsPressed = false; characterInput.useSecondaryWasReleased = false; } } namespace Sapphire009.RealControlMod; internal static class AnimationFlow { private sealed class AnimationFlowState { public bool HasPositionSample; public bool HasDirectionalPose; public bool StepActive; public bool IsFrozen; public bool HasInputSampleSnapshot; public float StepStartedTime; public float TimeWithoutMovement; public Vector2 LastDirection = Vector2.zero; public Vector3 InputSampleStartClimbPosition = Vector3.zero; public Vector3 LastCharacterCenter = Vector3.zero; } [HarmonyPatch(typeof(CharacterInput), "Sample", new Type[] { typeof(bool) })] private static class CharacterInputStepDetectionPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(CharacterInput __instance) { //IL_0026: 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) Character val = ResolveCharacter((Component)(object)__instance); if (IsLocalNormalWallClimbing(val)) { AnimationFlowState orCreateState = GetOrCreateState(val); orCreateState.InputSampleStartClimbPosition = val.data.climbPos; orCreateState.HasInputSampleSnapshot = true; } } [HarmonyPostfix] [HarmonyPriority(-200)] private static void Postfix(CharacterInput __instance) { //IL_0040: 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_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_006c: 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) Character val = ResolveCharacter((Component)(object)__instance); if (!IsLocalNormalWallClimbing(val)) { return; } AnimationFlowState orCreateState = GetOrCreateState(val); if (orCreateState.HasInputSampleSnapshot) { orCreateState.HasInputSampleSnapshot = false; Vector3 worldMovement = val.data.climbPos - orCreateState.InputSampleStartClimbPosition; if (!(((Vector3)(ref worldMovement)).sqrMagnitude <= 1E-06f) && TryConvertWorldMovementToInput(val, worldMovement, out var inputDirection)) { BeginStepAnimation(val, orCreateState, inputDirection); } } } } [HarmonyPatch(typeof(CharacterAnimations), "Update")] private static class CharacterAnimationsFlowPatch { [HarmonyPostfix] [HarmonyPriority(-200)] private static void Postfix(CharacterAnimations __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (!((Object)(object)val == (Object)null) && val.IsLocal) { if (!IsLocalNormalWallClimbing(val)) { RestoreAndRemoveState(val); } else { UpdateAnimationFlow(val); } } } } [HarmonyPatch(typeof(CharacterClimbing), "StartClimbRpc", new Type[] { typeof(Vector3), typeof(Vector3) })] private static class StartClimbAnimationFlowPatch { [HarmonyPostfix] [HarmonyPriority(-200)] private static void Postfix(CharacterClimbing __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (!((Object)(object)val == (Object)null) && val.IsLocal) { RestoreAndRemoveState(val); } } } [HarmonyPatch(typeof(CharacterClimbing), "StopClimbingRpc", new Type[] { typeof(float) })] private static class StopClimbAnimationFlowPatch { [HarmonyPostfix] [HarmonyPriority(-200)] private static void Postfix(CharacterClimbing __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (!((Object)(object)val == (Object)null) && val.IsLocal) { RestoreAndRemoveState(val); } } } private const float AppliedStepSqrMagnitude = 1E-06f; private const float MinimumDirectionalMovement = 0.0005f; private const float MinimumStepAnimationTime = 0.12f; private const float MovementStopGraceTime = 0.08f; private const float MinimumDirectionSqrMagnitude = 0.0001f; private const float MinimumAnimationSpeed = 0.01f; private static readonly int ClimbJumpAnimationHash = Animator.StringToHash("Climb Jump"); private static readonly int InputXAnimationHash = Animator.StringToHash("Input X"); private static readonly int InputYAnimationHash = Animator.StringToHash("Input Y"); private static readonly Dictionary FlowStates = new Dictionary(); private static Character ResolveCharacter(Component component) { if ((Object)(object)component == (Object)null) { return null; } Character val = component.GetComponent(); if ((Object)(object)val == (Object)null) { val = component.GetComponentInParent(); } return val; } private static bool IsLocalNormalWallClimbing(Character character) { if ((Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)character.data == (Object)null || character.refs == null || (Object)(object)character.refs.climbing == (Object)null || (Object)(object)character.refs.animator == (Object)null) { return false; } return character.data.isClimbing && !character.data.isRopeClimbing && !character.data.isVineClimbing && (Object)(object)character.data.currentClimbHandle == (Object)null; } private static AnimationFlowState GetOrCreateState(Character character) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)character).GetInstanceID(); if (!FlowStates.TryGetValue(instanceID, out var value)) { value = new AnimationFlowState(); value.HasPositionSample = true; value.LastCharacterCenter = character.Center; FlowStates.Add(instanceID, value); } return value; } private static float GetClimbAnimationSpeed(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return 1f; } return Mathf.Max(0.01f, character.data.staminaMod); } private static bool TryGetWallAxes(Character character, out Vector3 wallUp, out Vector3 wallRight) { //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_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_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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_009f: 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_00a9: Unknown result type (might be due to invalid IL or missing references) wallUp = Vector3.zero; wallRight = Vector3.zero; if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return false; } Vector3 climbNormal = character.data.climbNormal; if (((Vector3)(ref climbNormal)).sqrMagnitude <= 0.0001f) { return false; } ((Vector3)(ref climbNormal)).Normalize(); wallUp = Vector3.ProjectOnPlane(Vector3.up, climbNormal); if (((Vector3)(ref wallUp)).sqrMagnitude <= 0.0001f) { return false; } ((Vector3)(ref wallUp)).Normalize(); wallRight = Vector3.Cross(climbNormal, wallUp); if (((Vector3)(ref wallRight)).sqrMagnitude <= 0.0001f) { return false; } ((Vector3)(ref wallRight)).Normalize(); return true; } private static bool TryConvertWorldMovementToInput(Character character, Vector3 worldMovement, out Vector2 inputDirection) { //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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) inputDirection = Vector2.zero; if (!TryGetWallAxes(character, out var wallUp, out var wallRight)) { return false; } inputDirection = new Vector2(Vector3.Dot(worldMovement, wallRight), Vector3.Dot(worldMovement, wallUp)); if (((Vector2)(ref inputDirection)).sqrMagnitude <= 0.0001f) { inputDirection = Vector2.zero; return false; } ((Vector2)(ref inputDirection)).Normalize(); return true; } private static bool TryConvertInputToWorldDirection(Character character, Vector2 inputDirection, out Vector3 worldDirection) { //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_003f: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0079: 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) worldDirection = Vector3.zero; if (((Vector2)(ref inputDirection)).sqrMagnitude <= 0.0001f) { return false; } if (!TryGetWallAxes(character, out var wallUp, out var wallRight)) { return false; } worldDirection = wallRight * inputDirection.x + wallUp * inputDirection.y; if (((Vector3)(ref worldDirection)).sqrMagnitude <= 0.0001f) { worldDirection = Vector3.zero; return false; } ((Vector3)(ref worldDirection)).Normalize(); return true; } private static Vector3 GetWallSurfaceMovement(Character character, Vector3 worldMovement) { //IL_001b: 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_001d: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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) //IL_003a: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!TryGetWallAxes(character, out var wallUp, out var wallRight)) { return Vector3.zero; } return wallRight * Vector3.Dot(worldMovement, wallRight) + wallUp * Vector3.Dot(worldMovement, wallUp); } private static void BeginStepAnimation(Character character, AnimationFlowState state, Vector2 direction) { //IL_0031: 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) if (!((Object)(object)character == (Object)null) && state != null && !(((Vector2)(ref direction)).sqrMagnitude <= 0.0001f)) { ((Vector2)(ref direction)).Normalize(); state.LastDirection = direction; state.HasDirectionalPose = true; state.StepActive = true; state.IsFrozen = false; state.StepStartedTime = Time.unscaledTime; state.TimeWithoutMovement = 0f; ResumeAnimation(character, state); } } private static void ApplyDirectionalAnimation(Animator animator, Vector2 direction) { //IL_0007: 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) animator.SetFloat(InputXAnimationHash, direction.x); animator.SetFloat(InputYAnimationHash, direction.y); } private static void ResumeAnimation(Character character, AnimationFlowState state) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Animator animator = character.refs.animator; animator.SetBool(ClimbJumpAnimationHash, false); ApplyDirectionalAnimation(animator, state.LastDirection); animator.speed = GetClimbAnimationSpeed(character); state.IsFrozen = false; } private static void FreezeAnimation(Character character, AnimationFlowState state) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Animator animator = character.refs.animator; animator.SetBool(ClimbJumpAnimationHash, false); ApplyDirectionalAnimation(animator, state.LastDirection); animator.speed = 0f; state.IsFrozen = true; state.StepActive = false; } private static void RestoreAndRemoveState(Character character) { if ((Object)(object)character == (Object)null) { return; } int instanceID = ((Object)character).GetInstanceID(); if (FlowStates.TryGetValue(instanceID, out var _)) { if (character.refs != null && (Object)(object)character.refs.animator != (Object)null) { character.refs.animator.speed = 1f; } FlowStates.Remove(instanceID); } } private static void UpdateAnimationFlow(Character character) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_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_004a: 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_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_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_0071: 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: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) Animator animator = character.refs.animator; AnimationFlowState orCreateState = GetOrCreateState(character); Vector3 center = character.Center; if (!orCreateState.HasPositionSample) { orCreateState.HasPositionSample = true; orCreateState.LastCharacterCenter = center; return; } Vector3 worldMovement = center - orCreateState.LastCharacterCenter; Vector3 wallSurfaceMovement = GetWallSurfaceMovement(character, worldMovement); bool flag = false; if (orCreateState.StepActive && orCreateState.HasDirectionalPose && TryConvertInputToWorldDirection(character, orCreateState.LastDirection, out var worldDirection)) { float num = Vector3.Dot(wallSurfaceMovement, worldDirection); flag = num > 0.0005f; } if (orCreateState.StepActive && orCreateState.HasDirectionalPose) { float num2 = Time.unscaledTime - orCreateState.StepStartedTime; if (flag || num2 < 0.12f) { orCreateState.TimeWithoutMovement = 0f; ResumeAnimation(character, orCreateState); } else { orCreateState.TimeWithoutMovement += Time.unscaledDeltaTime; if (orCreateState.TimeWithoutMovement >= 0.08f) { FreezeAnimation(character, orCreateState); } else { ResumeAnimation(character, orCreateState); } } } else if (orCreateState.HasDirectionalPose) { FreezeAnimation(character, orCreateState); } else { animator.SetBool(ClimbJumpAnimationHash, false); } orCreateState.LastCharacterCenter = center; } } internal static class NoDelay { private enum MouseHand { None, Left, Right } private sealed class MouseDelayState { public bool WasNormalWallClimbing; public MouseHand LastAcceptedHand = MouseHand.None; public float LastAcceptedClickTime = float.NegativeInfinity; public float LastPrimaryPressedTime = float.NegativeInfinity; } [HarmonyPatch(typeof(CharacterInput), "Sample", new Type[] { typeof(bool) })] private static class CharacterInputSampleShortDelayPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(CharacterInput __instance) { Character character = ResolveCharacter((Component)(object)__instance); UpdateShortDelay(character); } [HarmonyPostfix] [HarmonyPriority(800)] private static void Postfix(CharacterInput __instance) { if (!((Object)(object)__instance == (Object)null)) { Character character = ResolveCharacter((Component)(object)__instance); ApplyMouseAlternationDelay(__instance, character); } } } [HarmonyPatch(typeof(CharacterClimbing), "RPCA_ClimbJump")] private static class ClimbJumpShortDelayPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(CharacterClimbing __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (IsLocalNormalWallClimbing(val)) { val.data.sinceClimbJump = 0f; } } } [HarmonyPatch(typeof(CharacterClimbing), "StartClimbRpc", new Type[] { typeof(Vector3), typeof(Vector3) })] private static class StartClimbShortDelayPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(CharacterClimbing __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (IsLocalNormalWallClimbing(val)) { val.data.sinceClimbJump = 0f; MouseDelayState orCreateMouseDelayState = GetOrCreateMouseDelayState(val); BeginMouseAlternationForClimb(orCreateMouseDelayState); } } } private const float ShortClimbDelay = 0.5f; private const float ReadyClimbJumpTime = 2f; private const float MouseAlternationDelay = 0.5f; private const float NormalGrabDetectionWindow = 0.75f; private static readonly Dictionary MouseDelayStates = new Dictionary(); private static Character ResolveCharacter(Component component) { if ((Object)(object)component == (Object)null) { return null; } Character val = component.GetComponent(); if ((Object)(object)val == (Object)null) { val = component.GetComponentInParent(); } return val; } private static MouseDelayState GetOrCreateMouseDelayState(Character character) { int instanceID = ((Object)character).GetInstanceID(); if (!MouseDelayStates.TryGetValue(instanceID, out var value)) { value = new MouseDelayState(); MouseDelayStates.Add(instanceID, value); } return value; } private static bool IsLocalCharacter(Character character) { return (Object)(object)character != (Object)null && character.IsLocal && (Object)(object)character.data != (Object)null; } private static bool IsLocalNormalWallClimbing(Character character) { if (!IsLocalCharacter(character)) { return false; } return character.data.isClimbing && !character.data.isRopeClimbing && !character.data.isVineClimbing && (Object)(object)character.data.currentClimbHandle == (Object)null; } private static void UpdateShortDelay(Character character) { if (IsLocalNormalWallClimbing(character) && character.data.sinceClimbJump >= 0.5f && character.data.sinceClimbJump < 2f) { character.data.sinceClimbJump = 2f; } } private static void ResetMouseAlternation(MouseDelayState state) { state.LastAcceptedHand = MouseHand.None; state.LastAcceptedClickTime = float.NegativeInfinity; } private static void BeginMouseAlternationForClimb(MouseDelayState state) { if (Time.unscaledTime - state.LastPrimaryPressedTime <= 0.75f) { state.LastAcceptedHand = MouseHand.Left; state.LastAcceptedClickTime = state.LastPrimaryPressedTime; } else { ResetMouseAlternation(state); } state.WasNormalWallClimbing = true; } private static bool CanAcceptOppositeClick(MouseDelayState state, MouseHand pressedHand) { if (state.LastAcceptedHand == MouseHand.None) { return true; } if (state.LastAcceptedHand == pressedHand) { return false; } float num = Time.unscaledTime - state.LastAcceptedClickTime; return num >= 0.5f; } private static void AcceptClick(MouseDelayState state, MouseHand pressedHand) { state.LastAcceptedHand = pressedHand; state.LastAcceptedClickTime = Time.unscaledTime; } private static void SuppressPrimaryClick(CharacterInput characterInput) { characterInput.usePrimaryWasPressed = false; characterInput.usePrimaryIsPressed = false; } private static void SuppressSecondaryClick(CharacterInput characterInput) { characterInput.useSecondaryWasPressed = false; characterInput.useSecondaryIsPressed = false; } private static void ProcessPrimaryClick(CharacterInput characterInput, MouseDelayState state) { if (characterInput.usePrimaryWasPressed) { if (!CanAcceptOppositeClick(state, MouseHand.Left)) { SuppressPrimaryClick(characterInput); } else { AcceptClick(state, MouseHand.Left); } } } private static void ProcessSecondaryClick(CharacterInput characterInput, MouseDelayState state) { if (characterInput.useSecondaryWasPressed) { if (!CanAcceptOppositeClick(state, MouseHand.Right)) { SuppressSecondaryClick(characterInput); } else { AcceptClick(state, MouseHand.Right); } } } private static void ApplyMouseAlternationDelay(CharacterInput characterInput, Character character) { if (!IsLocalCharacter(character)) { return; } MouseDelayState orCreateMouseDelayState = GetOrCreateMouseDelayState(character); if (!IsLocalNormalWallClimbing(character)) { if (characterInput.usePrimaryWasPressed) { orCreateMouseDelayState.LastPrimaryPressedTime = Time.unscaledTime; } orCreateMouseDelayState.WasNormalWallClimbing = false; ResetMouseAlternation(orCreateMouseDelayState); return; } if (!orCreateMouseDelayState.WasNormalWallClimbing) { if (characterInput.usePrimaryWasPressed) { orCreateMouseDelayState.LastPrimaryPressedTime = Time.unscaledTime; } BeginMouseAlternationForClimb(orCreateMouseDelayState); } bool usePrimaryWasPressed = characterInput.usePrimaryWasPressed; bool useSecondaryWasPressed = characterInput.useSecondaryWasPressed; if (usePrimaryWasPressed && useSecondaryWasPressed) { if (orCreateMouseDelayState.LastAcceptedHand == MouseHand.Left) { SuppressPrimaryClick(characterInput); ProcessSecondaryClick(characterInput, orCreateMouseDelayState); } else if (orCreateMouseDelayState.LastAcceptedHand == MouseHand.Right) { SuppressSecondaryClick(characterInput); ProcessPrimaryClick(characterInput, orCreateMouseDelayState); } else { SuppressSecondaryClick(characterInput); ProcessPrimaryClick(characterInput, orCreateMouseDelayState); } } else { ProcessPrimaryClick(characterInput, orCreateMouseDelayState); ProcessSecondaryClick(characterInput, orCreateMouseDelayState); } } } internal static class NoWall { [HarmonyPatch(typeof(CharacterClimbing), "Update")] private static class CharacterClimbingUpdatePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(CharacterClimbing __instance) { Character val = ResolveCharacter((Component)(object)__instance); if (!IsLocalCharacter(val)) { return true; } KeepOrClearReleaseLock(val); if (!IsLocalNormalWallClimbing(val)) { return true; } if ((Object)(object)val.input != (Object)null) { val.input.jumpWasPressed = false; } if (!IsAtReleaseThreshold(val)) { return true; } StopClimbingImmediately(__instance, val); return false; } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(CharacterClimbing __instance) { Character character = ResolveCharacter((Component)(object)__instance); if (IsLocalNormalWallClimbing(character) && IsAtReleaseThreshold(character)) { StopClimbingImmediately(__instance, character); } } } [HarmonyPatch(typeof(CharacterClimbing), "TryToStartWallClimb")] private static class TryToStartWallClimbPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(CharacterClimbing __instance) { Character character = ResolveCharacter((Component)(object)__instance); if (!IsLocalCharacter(character)) { return true; } return !KeepOrClearReleaseLock(character); } } [HarmonyPatch(typeof(CharacterClimbing), "StartClimbRpc", new Type[] { typeof(Vector3), typeof(Vector3) })] private static class StartClimbRpcPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(CharacterClimbing __instance) { Character character = ResolveCharacter((Component)(object)__instance); if (!IsLocalCharacter(character)) { return true; } return !KeepOrClearReleaseLock(character); } } [HarmonyPatch(typeof(Character), "OnDestroy")] private static class CharacterOnDestroyPatch { [HarmonyPostfix] private static void Postfix(Character __instance) { RemoveReleaseLock(__instance); } } private const float ReleaseStaminaThreshold = 0.049f; private const float ComparisonEpsilon = 0.0005f; private static readonly HashSet ForcedReleaseLocks = new HashSet(); private static Character ResolveCharacter(Component component) { if ((Object)(object)component == (Object)null) { return null; } Character val = component.GetComponent(); if ((Object)(object)val == (Object)null) { val = component.GetComponentInParent(); } return val; } private static bool IsLocalCharacter(Character character) { return (Object)(object)character != (Object)null && character.refs != null && (Object)(object)character.refs.view != (Object)null && character.refs.view.IsMine; } private static bool IsNormalWallClimbing(Character character) { return (Object)(object)character != (Object)null && (Object)(object)character.data != (Object)null && character.refs != null && (Object)(object)character.refs.climbing != (Object)null && character.data.isClimbing && !character.data.isRopeClimbing && !character.data.isVineClimbing && (Object)(object)character.data.currentClimbHandle == (Object)null; } private static bool IsLocalNormalWallClimbing(Character character) { return IsLocalCharacter(character) && IsNormalWallClimbing(character); } private static float GetRemainingStamina(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return float.MaxValue; } return Mathf.Max(0f, character.data.currentStamina) + Mathf.Max(0f, character.data.extraStamina); } private static bool IsAtReleaseThreshold(Character character) { return GetRemainingStamina(character) <= 0.0495f; } private static bool IsPrimaryActuallyHeld(Character character) { if (CharacterInput.action_usePrimary != null) { return CharacterInput.action_usePrimary.IsPressed(); } return (Object)(object)character != (Object)null && (Object)(object)character.input != (Object)null && character.input.usePrimaryIsPressed; } private static bool HasReleaseLock(Character character) { return (Object)(object)character != (Object)null && ForcedReleaseLocks.Contains(((Object)character).GetInstanceID()); } private static void AddReleaseLock(Character character) { if (!((Object)(object)character == (Object)null)) { ForcedReleaseLocks.Add(((Object)character).GetInstanceID()); } } private static void RemoveReleaseLock(Character character) { if (!((Object)(object)character == (Object)null)) { ForcedReleaseLocks.Remove(((Object)character).GetInstanceID()); } } private static bool KeepOrClearReleaseLock(Character character) { if (!HasReleaseLock(character)) { return false; } bool flag = !IsAtReleaseThreshold(character); bool flag2 = !IsPrimaryActuallyHeld(character); if (flag && flag2) { RemoveReleaseLock(character); return false; } if ((Object)(object)character.data != (Object)null) { character.data.sincePressClimb = 10f; } return true; } private static void StopClimbingImmediately(CharacterClimbing climbing, Character character) { //IL_0062: 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) if (!((Object)(object)climbing == (Object)null) && !((Object)(object)character == (Object)null) && !((Object)(object)character.data == (Object)null)) { AddReleaseLock(character); if ((Object)(object)character.input != (Object)null) { character.input.jumpWasPressed = false; } character.data.sincePressClimb = 10f; climbing.playerSlide = Vector2.zero; if (character.data.isClimbing) { climbing.StopClimbingRpc(0f); } if (PhotonNetwork.InRoom && character.refs != null && (Object)(object)character.refs.view != (Object)null) { character.refs.view.RPC("StopClimbingRpc", (RpcTarget)1, new object[1] { 0f }); } } } } internal static class StopStamina { [HarmonyPatch(typeof(Character), "UseStamina", new Type[] { typeof(float), typeof(bool) })] private static class CharacterUseStaminaPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(Character __instance, float __0, ref bool __result) { if (!IsNormalWallClimbing(__instance)) { return true; } if (__0 <= 0f) { return true; } if (__0 >= 0.099f) { return true; } __instance.data.sinceUseStamina = 0f; __result = true; return false; } } private const float DashStaminaCost = 0.1f; private const float ComparisonEpsilon = 0.001f; private static bool IsNormalWallClimbing(Character character) { if ((Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)character.data == (Object)null) { return false; } return character.data.isClimbing && !character.data.isRopeClimbing && !character.data.isVineClimbing && (Object)(object)character.data.currentClimbHandle == (Object)null; } }