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<Character>();
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<float> upStepDistance;
private static ConfigEntry<float> sideStepDistance;
private static ConfigEntry<float> downStepDistance;
private static ConfigEntry<bool> logSuccessfulSteps;
private static readonly Dictionary<int, InputState> InputStates = new Dictionary<int, InputState>();
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<float>("Climb Step", "UpStepDistance", 1.5f, "W를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 위로 이동할 거리입니다.");
sideStepDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Climb Step", "SideStepDistance", 1.5f, "A 또는 D를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 좌우로 이동할 거리입니다.");
downStepDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Climb Step", "DownStepDistance", 1.5f, "S를 누른 상태에서 올바른 좌클릭/우클릭 교대 입력 1회당 아래로 이동할 거리입니다.");
logSuccessfulSteps = ((BaseUnityPlugin)this).Config.Bind<bool>("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<int, AnimationFlowState> FlowStates = new Dictionary<int, AnimationFlowState>();
private static Character ResolveCharacter(Component component)
{
if ((Object)(object)component == (Object)null)
{
return null;
}
Character val = component.GetComponent<Character>();
if ((Object)(object)val == (Object)null)
{
val = component.GetComponentInParent<Character>();
}
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<int, MouseDelayState> MouseDelayStates = new Dictionary<int, MouseDelayState>();
private static Character ResolveCharacter(Component component)
{
if ((Object)(object)component == (Object)null)
{
return null;
}
Character val = component.GetComponent<Character>();
if ((Object)(object)val == (Object)null)
{
val = component.GetComponentInParent<Character>();
}
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<int> ForcedReleaseLocks = new HashSet<int>();
private static Character ResolveCharacter(Component component)
{
if ((Object)(object)component == (Object)null)
{
return null;
}
Character val = component.GetComponent<Character>();
if ((Object)(object)val == (Object)null)
{
val = component.GetComponentInParent<Character>();
}
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;
}
}