Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Stride v0.6.0
BepInEx\plugins\Stride.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; 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 UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Stride")] [assembly: AssemblyDescription("Invisible improvements to vanilla movement in Valheim")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Stride")] [assembly: AssemblyCopyright("Copyright (c) 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("77f1130e-4e67-4f2d-bd7e-d365e48f9120")] [assembly: AssemblyFileVersion("0.3.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.3.0.0")] namespace StrideMod; internal static class PluginInfo { internal const string Guid = "com.b0n3.stride"; internal const string Name = "Stride"; internal const string Version = "0.6.0"; } [BepInPlugin("com.b0n3.stride", "Stride", "0.6.0")] public class StridePlugin : BaseUnityPlugin { internal enum DebugLogFeature { CoyoteTime, JumpBuffering, WallJump, FallRoll, StaminaDebt } private sealed class FloatSetting { private readonly ConfigEntry<string> entry; private readonly float defaultValue; private readonly float minValue; private readonly float maxValue; internal float Value => GetFloat(entry, defaultValue, minValue, maxValue); internal FloatSetting(ConfigEntry<string> entry, float defaultValue, float minValue, float maxValue) { this.entry = entry; this.defaultValue = defaultValue; this.minValue = minValue; this.maxValue = maxValue; } } private const string LegacyConfigFileName = "com.b0n3.walljump.cfg"; private const string CoyoteTimeSection = "Coyote Time"; private const string JumpBufferingSection = "Jump Buffering"; private const string WallJumpSection = "Wall Jump"; private const string FallRollSection = "Fall Roll"; private const string StaminaDebtSection = "Stamina Debt"; private const string LoggingSection = "Logging"; internal static ManualLogSource Log; private static readonly Dictionary<string, string> LastInvalidConfigValues = new Dictionary<string, string>(); private Dictionary<string, string> existingConfigValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static FloatSetting coyoteTimeSeconds; private static FloatSetting jumpBufferSeconds; private static FloatSetting jumpWindowSeconds; private static FloatSetting wallMemorySeconds; private static FloatSetting wallCheckDistance; private static FloatSetting wallCheckRadius; private static FloatSetting wallJumpTriggerDistance; private static FloatSetting minimumWallJumpEntrySpeed; private static FloatSetting fallRollBufferSeconds; private static FloatSetting fallRollDamageReduction; private static FloatSetting horizontalBounceMinSpeed; private static FloatSetting outwardBounceMinSpeed; private static FloatSetting reflectionResponsiveness; private static FloatSetting inputBiasFactor; private static FloatSetting verticalPushForce; private static FloatSetting cooldownSeconds; internal static ConfigEntry<bool> CoyoteTimeEnabled; internal static ConfigEntry<bool> JumpBufferingEnabled; internal static ConfigEntry<bool> WallJumpEnabled; internal static ConfigEntry<bool> FallRollEnabled; internal static ConfigEntry<bool> StaminaDebtEnabled; internal static ConfigEntry<bool> RequireForwardPress; internal static ConfigEntry<bool> EnableVerboseLogging; internal static ConfigEntry<bool> VerboseAllFeatures; internal static ConfigEntry<bool> CoyoteTimeVerboseLogging; internal static ConfigEntry<bool> JumpBufferVerboseLogging; internal static ConfigEntry<bool> WallJumpVerboseLogging; internal static ConfigEntry<bool> FallRollVerboseLogging; internal static ConfigEntry<bool> StaminaDebtVerboseLogging; internal static float CoyoteTimeSeconds => coyoteTimeSeconds.Value; internal static float JumpBufferSeconds => jumpBufferSeconds.Value; internal static float JumpWindowSeconds => jumpWindowSeconds.Value; internal static float WallMemorySeconds => wallMemorySeconds.Value; internal static float WallCheckDistance => wallCheckDistance.Value; internal static float WallCheckRadius => wallCheckRadius.Value; internal static float WallJumpTriggerDistance => wallJumpTriggerDistance.Value; internal static float MinimumWallJumpEntrySpeed => minimumWallJumpEntrySpeed.Value; internal static float FallRollBufferSeconds => fallRollBufferSeconds.Value; internal static float FallRollDamageReduction => fallRollDamageReduction.Value; internal static float HorizontalBounceMinSpeed => horizontalBounceMinSpeed.Value; internal static float OutwardBounceMinSpeed => outwardBounceMinSpeed.Value; internal static float ReflectionResponsiveness => reflectionResponsiveness.Value; internal static float InputBiasFactor => inputBiasFactor.Value; internal static float VerticalPushForce => verticalPushForce.Value; internal static float CooldownSeconds => cooldownSeconds.Value; private void Awake() { //IL_052a: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; EnsureLegacyConfigMigrated(); existingConfigValues = ReadConfigValues(((BaseUnityPlugin)this).Config.ConfigFilePath); CoyoteTimeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Coyote Time", "Enabled", true, "If this is off, Stride does not allow late jumps after leaving a ledge."); coyoteTimeSeconds = BindFloatSetting("Coyote Time", "WindowSeconds", 0.1f, 0f, 1f, "How long you can still jump after running off a ledge.", "General", "CoyoteTimeSeconds"); JumpBufferingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Jump Buffering", "Enabled", true, "If this is off, Stride does not save early jump presses before landing."); jumpBufferSeconds = BindFloatSetting("Jump Buffering", "WindowSeconds", 0.1f, 0f, 1f, "How early a jump button press is saved before you land.", "General", "JumpBufferSeconds"); WallJumpEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Wall Jump", "Enabled", true, "If this is off, Stride does not allow wall jumps."); jumpWindowSeconds = BindFloatSetting("Wall Jump", "AirJumpWindowSeconds", 0.95f, 0f, 5f, "How long after a normal jump you are still allowed to trigger a wall jump.", "General", "JumpWindowSeconds"); wallMemorySeconds = BindFloatSetting("Wall Jump", "WallMemorySeconds", 0.05f, 0f, 1f, "How long a wall stays valid after you stop touching it.", "General", "WallMemorySeconds"); wallCheckDistance = BindFloatSetting("Wall Jump", "WallCheckDistance", 0.575f, 0.05f, 5f, "How far away Stride looks for a wall you can jump from.", "General", "WallCheckDistance"); wallCheckRadius = BindFloatSetting("Wall Jump", "WallCheckRadius", 0.225f, 0.05f, 3f, "How wide the wall search is around your character.", "General", "WallCheckRadius"); wallJumpTriggerDistance = BindFloatSetting("Wall Jump", "WallJumpTriggerDistance", 0.18f, 0.01f, 2f, "How close the chosen wall must be before the wall jump actually fires.", "General", "WallJumpTriggerDistance"); minimumWallJumpEntrySpeed = BindFloatSetting("Wall Jump", "MinimumEntrySpeed", 0.1f, 0f, 20f, "The minimum sideways movement speed that counts as a real wall approach.", "General", "MinimumWallJumpEntrySpeed"); horizontalBounceMinSpeed = BindFloatSetting("Wall Jump", "MinimumHorizontalBounceSpeed", 9.5f, 0f, 50f, "The minimum sideways speed you keep after bouncing off a wall.", "General", "HorizontalBounceMinSpeed"); outwardBounceMinSpeed = BindFloatSetting("Wall Jump", "MinimumOutwardBounceSpeed", 6.75f, 0f, 50f, "The minimum speed that pushes you away from the wall.", "General", "OutwardBounceMinSpeed"); reflectionResponsiveness = BindFloatSetting("Wall Jump", "ReflectionResponsiveness", 1f, 0f, 3f, "How much of your incoming wall approach speed carries into the bounce.", "General", "ReflectionResponsiveness"); inputBiasFactor = BindFloatSetting("Wall Jump", "InputBiasFactor", 0.12f, 0f, 1f, "How much your movement input can gently steer the wall jump along the wall.", "General", "InputBiasFactor"); verticalPushForce = BindFloatSetting("Wall Jump", "VerticalPushForce", 8.6f, 0f, 50f, "How much upward lift the wall jump gives you.", "General", "VerticalPushForce"); cooldownSeconds = BindFloatSetting("Wall Jump", "CooldownSeconds", 0.15f, 0f, 10f, "How long Stride waits before another wall jump can happen.", "General", "CooldownSeconds"); FallRollEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Fall Roll", "Enabled", true, "If this is off, Stride does not buffer fall rolls or reduce fall damage on landing."); fallRollBufferSeconds = BindFloatSetting("Fall Roll", "BufferSeconds", 0.9f, 0f, 2f, "How early you can press dodge before landing and still get a fall roll.", "General", "FallRollBufferSeconds"); fallRollDamageReduction = BindFloatSetting("Fall Roll", "DamageReduction", 0.5f, 0f, 1f, "How much fall damage a successful fall roll removes. 0.5 means 50% less damage.", "General", "FallRollDamageReduction"); RequireForwardPress = BindBoolSetting("Wall Jump", "RequireForwardPress", defaultValue: true, "If on, you must be pressing forward to trigger a wall jump.", "General", "RequireForwardPress"); StaminaDebtEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina Debt", "Enabled", true, "If this is off, Stride leaves stamina spending and the stamina bar alone."); EnableVerboseLogging = BindBoolSetting("Logging", "EnableVerboseLogging", defaultValue: false, "Main on/off switch for verbose logs. If this is off, Stride prints no verbose logs at all.", "Debug", "VerboseLogging"); VerboseAllFeatures = BindBoolSetting("Logging", "VerboseAllFeatures", defaultValue: false, "Turns on verbose logs for every feature at once. This only works while EnableVerboseLogging is on.", "Debug", "VerboseLogging"); CoyoteTimeVerboseLogging = BindBoolSetting("Coyote Time", "VerboseLogging", defaultValue: false, "Extra logs just for coyote time. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging"); JumpBufferVerboseLogging = BindBoolSetting("Jump Buffering", "VerboseLogging", defaultValue: false, "Extra logs just for jump buffering. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging"); WallJumpVerboseLogging = BindBoolSetting("Wall Jump", "VerboseLogging", defaultValue: false, "Extra logs for wall detection and wall jumping. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseWallJumpLogging"); FallRollVerboseLogging = BindBoolSetting("Fall Roll", "VerboseLogging", defaultValue: false, "Extra logs just for fall rolls. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging"); StaminaDebtVerboseLogging = BindBoolSetting("Stamina Debt", "VerboseLogging", defaultValue: false, "Extra logs just for stamina debt. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseStaminaDebtLogging"); MigrateLegacyWallDetectionVerboseSetting(); new Harmony("com.b0n3.stride").PatchAll(); Log.LogInfo((object)"Stride loaded."); } private void EnsureLegacyConfigMigrated() { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (!File.Exists(configFilePath)) { string text = Path.Combine(Paths.ConfigPath, "com.b0n3.walljump.cfg"); if (File.Exists(text)) { File.Copy(text, configFilePath); Log.LogInfo((object)("Migrated config from 'com.b0n3.walljump.cfg' to '" + Path.GetFileName(configFilePath) + "'.")); } } } private FloatSetting BindFloatSetting(string section, string key, float defaultValue, float minValue, float maxValue, string description, string legacySection, string legacyKey) { ConfigEntry<string> entry = ((BaseUnityPlugin)this).Config.Bind<string>(section, key, FormatFloat(defaultValue), description + " Range: " + FormatFloat(minValue) + " to " + FormatFloat(maxValue) + ". Invalid values are reset automatically."); MigrateEntryValue<string>(entry, legacySection, legacyKey); return new FloatSetting(entry, defaultValue, minValue, maxValue); } private ConfigEntry<bool> BindBoolSetting(string section, string key, bool defaultValue, string description, string legacySection, string legacyKey) { ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>(section, key, defaultValue, description); MigrateEntryValue<bool>(val, legacySection, legacyKey); return val; } private void MigrateEntryValue<T>(ConfigEntry<T> entry, string legacySection, string legacyKey) { if (entry == null || string.IsNullOrEmpty(legacySection) || string.IsNullOrEmpty(legacyKey)) { return; } string key = BuildCompositeKey(((ConfigEntryBase)entry).Definition.Section, ((ConfigEntryBase)entry).Definition.Key); if (existingConfigValues.ContainsKey(key)) { return; } string key2 = BuildCompositeKey(legacySection, legacyKey); if (!existingConfigValues.TryGetValue(key2, out var value)) { return; } try { T val = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); if (!EqualityComparer<T>.Default.Equals(entry.Value, val)) { entry.Value = val; ((ConfigEntryBase)entry).ConfigFile.Save(); } } catch { } } private void MigrateLegacyWallDetectionVerboseSetting() { string key = BuildCompositeKey("Wall Jump", "VerboseLogging"); if (!existingConfigValues.ContainsKey(key)) { string key2 = BuildCompositeKey("Debug", "VerboseWallDetectionLogging"); if (existingConfigValues.TryGetValue(key2, out var value) && bool.TryParse(value, out var result) && result && !WallJumpVerboseLogging.Value) { WallJumpVerboseLogging.Value = true; ((ConfigEntryBase)WallJumpVerboseLogging).ConfigFile.Save(); } } } private static Dictionary<string, string> ReadConfigValues(string configFilePath) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath)) { return dictionary; } string section = string.Empty; string[] array = File.ReadAllLines(configFilePath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (string.IsNullOrEmpty(text) || text.StartsWith("#")) { continue; } if (text.StartsWith("[") && text.EndsWith("]")) { section = text.Substring(1, text.Length - 2).Trim(); continue; } int num = text.IndexOf('='); if (num > 0) { string key = text.Substring(0, num).Trim(); string value = text.Substring(num + 1).Trim(); dictionary[BuildCompositeKey(section, key)] = value; } } return dictionary; } private static string BuildCompositeKey(string section, string key) { return section + "::" + key; } private static float GetFloat(ConfigEntry<string> entry, float defaultValue, float minValue, float maxValue) { string text = entry.Value?.Trim(); if (string.IsNullOrEmpty(text)) { return WarnAndFallback(entry, text, defaultValue); } if (!TryParseFloat(text, out var value)) { return WarnAndFallback(entry, text, defaultValue); } if (value < minValue) { return WarnAndFallback(entry, text, minValue); } if (value > maxValue) { return WarnAndFallback(entry, text, maxValue); } LastInvalidConfigValues.Remove(((ConfigEntryBase)entry).Definition.Key); return value; } private static float WarnAndFallback(ConfigEntry<string> entry, string raw, float fallbackValue) { string key = ((ConfigEntryBase)entry).Definition.Key; string text = raw ?? "<empty>"; if (!LastInvalidConfigValues.TryGetValue(key, out var value) || value != text) { LastInvalidConfigValues[key] = text; Log.LogWarning((object)("Config value for " + key + " was invalid ('" + text + "'). Using " + FormatFloat(fallbackValue) + " instead.")); } string text2 = FormatFloat(fallbackValue); if (entry.Value != text2) { entry.Value = text2; ((ConfigEntryBase)entry).ConfigFile.Save(); } return fallbackValue; } private static bool TryParseFloat(string raw, out float value) { if (!float.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value)) { return float.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out value); } return true; } private static string FormatFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } internal static bool IsDebugEnabled(DebugLogFeature feature) { if (EnableVerboseLogging == null || !EnableVerboseLogging.Value) { return false; } if (VerboseAllFeatures != null && VerboseAllFeatures.Value) { return true; } switch (feature) { case DebugLogFeature.CoyoteTime: if (CoyoteTimeVerboseLogging != null) { return CoyoteTimeVerboseLogging.Value; } return false; case DebugLogFeature.JumpBuffering: if (JumpBufferVerboseLogging != null) { return JumpBufferVerboseLogging.Value; } return false; case DebugLogFeature.WallJump: if (WallJumpVerboseLogging != null) { return WallJumpVerboseLogging.Value; } return false; case DebugLogFeature.FallRoll: if (FallRollVerboseLogging != null) { return FallRollVerboseLogging.Value; } return false; case DebugLogFeature.StaminaDebt: if (StaminaDebtVerboseLogging != null) { return StaminaDebtVerboseLogging.Value; } return false; default: return false; } } } [HarmonyPatch(typeof(Character), "Jump")] internal static class CharacterJumpPatch { private static void Prefix(Character __instance, ref bool __state) { __state = IsTrackedPlayer(__instance) && __instance.IsOnGround(); } private static void Postfix(Character __instance, bool __state) { if (__state && IsTrackedPlayer(__instance)) { StrideState.RecordGroundJump(); } } private static bool IsTrackedPlayer(Character character) { if ((Object)(object)character != (Object)null) { return (Object)(object)character == (Object)(object)Player.m_localPlayer; } return false; } } [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerUpdateWallJumpPatch { private struct WallCandidate { internal Vector3 Normal; internal Vector3 Point; internal float Distance; internal float PlaneDepth; internal float BaseScore; internal float VelocityFacingScore; internal float CastFacingScore; internal int PieceRootId; } private struct WallCluster { internal Vector3 Normal; internal Vector3 AveragePoint; internal float PlaneDepth; internal float Score; internal float BestCandidateScore; internal float BestVelocityFacingScore; internal float NearestDistance; internal float PlayerPlaneOffset; internal float ForwardFlowScore; internal float FrontFacePenalty; internal float Weight; internal bool RejectedForDistance; internal int HitCount; internal int PieceRootId; } internal struct WallDetectionResult { internal Vector3 Normal; internal float NearestDistance; } private struct MovementInputSnapshot { internal Vector2 RawLocalInput; internal Vector2 ClampedLocalInput; internal Vector3 WorldDirection; internal float Magnitude; internal float ForwardAmount; internal bool HasDirectionalInput { get { if (Magnitude >= 0.1f) { return ((Vector3)(ref WorldDirection)).sqrMagnitude >= 0.01f; } return false; } } } private const float MaxWallNormalY = 0.25f; private const float MinimumFacingDot = 0.15f; private const float MinimumMoveInputMagnitude = 0.1f; private const int WallCastDirectionCount = 8; private const int MaxWallHitsPerCast = 32; private const float WallClusterNormalDot = 0.92f; private const float WallClusterPlaneTolerance = 0.2f; private const float WallClusterFrontFaceTolerance = 0.18f; private const float WallClusterPointNeighborhood = 1.35f; private const float MinimumWallClusterConfidenceLead = 0.2f; private const float ForwardFlowBiasWeight = 6f; private const float AlternateFlowPreferenceMargin = 8.5f; private const float AlternateFlowPreferenceLead = 0.2f; private const float AlternateFlowMinimum = 0.45f; private const float AlternateFlowMinimumFacing = 0.05f; private const float MinimumForwardStickY = 0.35f; private const float MinimumVelocityScoreMagnitude = 0.35f; private const float SingleHitClusterPenalty = 1.1f; private const float StrongSingleHitFacing = 0.9f; private const float StrongSingleHitDistance = 0.06f; private const float MinimumBounceFloorScale = 0.1f; private const float MaximumRememberedApproachBoostRatio = 1.25f; private const float MaximumOutwardFloorBoost = 1.5f; private const float MaximumHorizontalFloorBoost = 2f; internal const float StickyWallNormalDot = 0.8f; internal const float StickyWallDistanceTolerance = 0.12f; internal const float MinimumApproachVelocityDot = 0.05f; internal const float MinimumRememberedWallApproachDot = 0.05f; private const float MaximumInputBiasFraction = 0.35f; private static readonly FieldRef<Character, Rigidbody> CharacterBodyRef = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body"); private static readonly FieldRef<Character, SEMan> CharacterSeManRef = AccessTools.FieldRefAccess<Character, SEMan>("m_seman"); private static readonly FieldRef<Character, float> CharacterSpeedRef = AccessTools.FieldRefAccess<Character, float>("m_speed"); private static readonly FieldRef<Character, float> LastGroundTouchRef = AccessTools.FieldRefAccess<Character, float>("m_lastGroundTouch"); private static readonly FieldRef<Character, float> MaxAirAltitudeRef = AccessTools.FieldRefAccess<Character, float>("m_maxAirAltitude"); private static readonly FieldRef<Character, float> JumpStaminaUsageRef = AccessTools.FieldRefAccess<Character, float>("m_jumpStaminaUsage"); private static readonly FieldRef<Character, float> JumpForceTiredFactorRef = AccessTools.FieldRefAccess<Character, float>("m_jumpForceTiredFactor"); private static Player trackedPlayer; private static bool lastJumpButtonPressed; private static bool wasGroundedLastFrame; private static readonly List<WallCandidate> WallCandidateBuffer = new List<WallCandidate>(16); private static readonly List<WallCluster> WallClusterBuffer = new List<WallCluster>(16); private static readonly Vector3[] WallCastDirectionBuffer = (Vector3[])(object)new Vector3[8]; private static readonly RaycastHit[] WallHitBuffer = (RaycastHit[])(object)new RaycastHit[32]; private static void Postfix(Player __instance) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if ((Object)(object)__instance != (Object)(object)trackedPlayer) { trackedPlayer = __instance; lastJumpButtonPressed = false; wasGroundedLastFrame = false; StrideState.ResetAll(); StaminaDebt.ResetTracking(); } bool flag = ((Character)__instance).IsOnGround(); bool flag2 = ConsumeJumpPressed(); if (flag || ((Character)__instance).IsSwimming() || ((Character)__instance).IsDead()) { if (flag) { StrideState.RecordGrounded(); if (!wasGroundedLastFrame && StrideState.TryConsumeBufferedGroundJump()) { LogDebug(StridePlugin.DebugLogFeature.JumpBuffering, "Jump buffer triggered."); ((Character)__instance).Jump(false); } } wasGroundedLastFrame = flag; StrideState.ResetAirborneState(); return; } wasGroundedLastFrame = false; Rigidbody val = TryGetBody((Character)(object)__instance); Vector3 horizontalVelocity = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero); MovementInputSnapshot movementInput = SampleMovementInput(__instance); int num; if (StridePlugin.WallJumpEnabled != null) { num = (StridePlugin.WallJumpEnabled.Value ? 1 : 0); if (num != 0) { goto IL_00e6; } } else { num = 0; } StrideState.ClearWallJumpTracking(); goto IL_00e6; IL_00e6: WallDetectionResult wallDetection = default(WallDetectionResult); bool flag3 = num != 0 && TryFindWall(__instance, flag2, out wallDetection); if (flag3) { StrideState.RecordWallContact(wallDetection.Normal, horizontalVelocity, wallDetection.NearestDistance); } if (flag2 && StrideState.UsesBufferedJumpTracking()) { StrideState.RecordJumpPressed(); } if (!flag2) { return; } if (StrideState.TryConsumeCoyoteJump()) { LastGroundTouchRef.Invoke((Character)(object)__instance) = 0f; StrideState.RecordCoyoteJump(); LogDebug(StridePlugin.DebugLogFeature.CoyoteTime, "Coyote jump triggered."); ((Character)__instance).Jump(false); } else { if (!StrideState.CanWallJump(__instance) || (StridePlugin.RequireForwardPress.Value && !IsPressingForward(movementInput))) { return; } if (!StrideState.TryGetWallJumpContact(__instance, flag3, wallDetection, out var wallContact, out var usingRememberedWall)) { LogDebug(StridePlugin.DebugLogFeature.WallJump, "Jump pressed in air, but no valid wall was found."); return; } LogDebug(StridePlugin.DebugLogFeature.WallJump, "Wall jump using " + (usingRememberedWall ? "remembered" : "current") + " wall normal " + FormatVector(wallContact.Normal) + "."); if (!(((Vector3)(ref wallContact.Normal)).sqrMagnitude < 0.01f)) { PerformWallJump(__instance, wallContact, movementInput); } } } private static bool IsPressingForward(MovementInputSnapshot movementInput) { return movementInput.ForwardAmount >= 0.35f; } private static bool ConsumeJumpPressed() { bool button = ZInput.GetButton("Jump"); bool result = (button && !lastJumpButtonPressed) || ZInput.GetButtonDown("JoyJump"); lastJumpButtonPressed = button; return result; } private static bool TryFindWall(Player player, bool logDetails, out WallDetectionResult wallDetection) { //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_0014: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_005c: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) wallDetection = default(WallDetectionResult); Vector3 centerPoint = ((Character)player).GetCenterPoint(); Vector3 forward = ((Component)player).transform.forward; forward.y = 0f; ((Vector3)(ref forward)).Normalize(); Vector3 right = ((Component)player).transform.right; right.y = 0f; ((Vector3)(ref right)).Normalize(); Rigidbody val = TryGetBody((Character)(object)player); Vector3 val2 = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero); Vector3 velocityDirection = val2; velocityDirection.y = 0f; if (((Vector3)(ref velocityDirection)).sqrMagnitude > 0.01f) { ((Vector3)(ref velocityDirection)).Normalize(); } PopulateWallCastDirections(forward, right, velocityDirection, WallCastDirectionBuffer); WallCandidateBuffer.Clear(); float num = Mathf.Max(0.05f, StridePlugin.WallCheckRadius); for (int i = 0; i < WallCastDirectionBuffer.Length; i++) { Vector3 val3 = WallCastDirectionBuffer[i]; if (((Vector3)(ref val3)).sqrMagnitude < 0.01f) { continue; } int num2 = Physics.SphereCastNonAlloc(centerPoint, num, val3, WallHitBuffer, StridePlugin.WallCheckDistance, -1, (QueryTriggerInteraction)1); if (num2 > 0) { if (num2 == WallHitBuffer.Length && logDetails) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall cast hit buffer filled at {WallHitBuffer.Length} hits; some wall candidates may be skipped."); } AddWallCandidates(player, val3, val2, WallHitBuffer, num2, WallCandidateBuffer, logDetails); } } if (WallCandidateBuffer.Count == 0) { return false; } return TryGetSupportedWallNormal(player, WallCandidateBuffer, logDetails, out wallDetection); } private static void AddWallCandidates(Player player, Vector3 castDirection, Vector3 horizontalVelocity, RaycastHit[] hits, int hitCount, List<WallCandidate> candidates, bool logDetails) { //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_0019: 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_002f: 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_0031: 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_0094: 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_00b6: 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) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (hits == null || hitCount <= 0) { return; } Vector3 normalized = ((Vector3)(ref castDirection)).normalized; for (int i = 0; i < hitCount; i++) { if (TryGetWallHitNormal(player, normalized, hits[i], logDetails, out var wallNormal)) { float num = Vector3.Dot(normalized, -wallNormal); float num2 = 0f; if (((Vector3)(ref horizontalVelocity)).sqrMagnitude >= 0.122499995f) { num2 = Vector3.Dot(((Vector3)(ref horizontalVelocity)).normalized, -wallNormal); } float num3 = Mathf.Clamp01(((RaycastHit)(ref hits[i])).distance / Mathf.Max(0.01f, StridePlugin.WallCheckDistance)); Vector3 point = ((RaycastHit)(ref hits[i])).point; Piece componentInParent = ((Component)((RaycastHit)(ref hits[i])).collider).GetComponentInParent<Piece>(); candidates.Add(new WallCandidate { Normal = wallNormal, Point = point, Distance = ((RaycastHit)(ref hits[i])).distance, PlaneDepth = Vector3.Dot(wallNormal, point), BaseScore = num2 * 1.75f + num * 0.35f - num3 * 0.75f, VelocityFacingScore = num2, CastFacingScore = num, PieceRootId = GetPieceRootId(componentInParent, ((RaycastHit)(ref hits[i])).collider) }); } } } private static void PopulateWallCastDirections(Vector3 fallbackDirection, Vector3 rightDirection, Vector3 velocityDirection, Vector3[] directions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //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_0021: 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_0027: 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) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_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) //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_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_008a: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_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) directions[0] = fallbackDirection; directions[1] = velocityDirection; directions[2] = Quaternion.Euler(0f, 30f, 0f) * fallbackDirection; directions[3] = Quaternion.Euler(0f, -30f, 0f) * fallbackDirection; directions[4] = Quaternion.Euler(0f, 60f, 0f) * fallbackDirection; directions[5] = Quaternion.Euler(0f, -60f, 0f) * fallbackDirection; directions[6] = rightDirection; directions[7] = -rightDirection; } private static bool TryGetSupportedWallNormal(Player player, List<WallCandidate> candidates, bool logDetails, out WallDetectionResult wallDetection) { //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) wallDetection = default(WallDetectionResult); BuildWallClusters(player, candidates, WallClusterBuffer); if (WallClusterBuffer.Count == 0) { return false; } int num = -1; float num2 = float.MinValue; float num3 = float.MinValue; for (int i = 0; i < WallClusterBuffer.Count; i++) { WallCluster wallCluster = WallClusterBuffer[i]; if (wallCluster.FrontFacePenalty > 0f) { continue; } float score = wallCluster.Score; if (score <= num2) { if (score > num3) { num3 = score; } } else { num3 = num2; num2 = score; num = i; } } if (num < 0) { return false; } WallCluster wallCluster2 = WallClusterBuffer[num]; int num4 = FindPreferredFlowCluster(WallClusterBuffer, num, num2); bool flag = false; if (num4 >= 0) { WallCluster wallCluster3 = WallClusterBuffer[num4]; if (logDetails) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Switching wall cluster from {num + 1} to {num4 + 1} to preserve along-wall flow. Original flow {wallCluster2.ForwardFlowScore:0.###}, preferred flow {wallCluster3.ForwardFlowScore:0.###}."); } num = num4; num2 = wallCluster3.Score; wallCluster2 = wallCluster3; flag = true; } float num5 = num2 - num3; if (!flag && WallClusterBuffer.Count > 1 && num5 < 0.2f) { if (logDetails) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Rejected wall cluster with weak confidence lead {num5:0.###}. Best score {num2:0.###}, runner-up {num3:0.###}."); LogWallClusters(WallClusterBuffer); } return false; } if (flag && logDetails) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Accepted flow-preserving wall cluster despite confidence lead {num5:0.###}."); } wallDetection = new WallDetectionResult { Normal = wallCluster2.Normal, NearestDistance = wallCluster2.NearestDistance }; if (logDetails) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Selected wall normal {FormatVector(wallDetection.Normal)} from cluster {num + 1}/{WallClusterBuffer.Count} with {wallCluster2.HitCount} hit(s), nearest {wallCluster2.NearestDistance:0.###}, flow {wallCluster2.ForwardFlowScore:0.###}, score {wallCluster2.Score:0.###}."); } return true; } private static int FindPreferredFlowCluster(List<WallCluster> clusters, int bestIndex, float bestScore) { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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) if (bestIndex < 0 || bestIndex >= clusters.Count) { return -1; } WallCluster wallCluster = clusters[bestIndex]; if (wallCluster.ForwardFlowScore >= 0.45f || wallCluster.BestVelocityFacingScore < 0.75f) { return -1; } int result = -1; float num = float.MinValue; for (int i = 0; i < clusters.Count; i++) { if (i == bestIndex) { continue; } WallCluster wallCluster2 = clusters[i]; if (wallCluster2.Score < bestScore - 8.5f || wallCluster2.ForwardFlowScore < 0.45f || wallCluster2.ForwardFlowScore < wallCluster.ForwardFlowScore + 0.2f || wallCluster2.BestVelocityFacingScore < 0.05f) { continue; } bool num2 = wallCluster2.PieceRootId != 0 && wallCluster2.PieceRootId == wallCluster.PieceRootId; Vector3 val = wallCluster2.AveragePoint - wallCluster.AveragePoint; bool flag = ((Vector3)(ref val)).sqrMagnitude <= 7.2900004f; if (num2 || flag) { float num3 = wallCluster2.Score + wallCluster2.ForwardFlowScore * 3f + wallCluster2.BestVelocityFacingScore; if (!(num3 <= num)) { num = num3; result = i; } } } return result; } private static void BuildWallClusters(Player player, List<WallCandidate> candidates, List<WallCluster> clusters) { //IL_000c: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) clusters.Clear(); Vector3 position = ((Component)player).transform.position; Rigidbody val = TryGetBody((Character)(object)player); Vector3 horizontalVelocity = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero); for (int i = 0; i < candidates.Count; i++) { WallCandidate candidate = candidates[i]; int num = -1; for (int j = 0; j < clusters.Count; j++) { if (CanJoinCluster(candidate, clusters[j])) { num = j; break; } } if (num < 0) { clusters.Add(new WallCluster { Normal = candidate.Normal, PlaneDepth = candidate.PlaneDepth, AveragePoint = candidate.Point, Score = candidate.BaseScore, BestCandidateScore = candidate.BaseScore, BestVelocityFacingScore = candidate.VelocityFacingScore, NearestDistance = candidate.Distance, Weight = 1f, HitCount = 1, PieceRootId = candidate.PieceRootId }); continue; } WallCluster value = clusters[num]; float num2 = Mathf.Max(0.1f, candidate.BaseScore + 2f); float num3 = value.Weight + num2; Vector3 val2 = value.Normal * value.Weight + candidate.Normal * num2; value.Normal = ((Vector3)(ref val2)).normalized; value.PlaneDepth = (value.PlaneDepth * value.Weight + candidate.PlaneDepth * num2) / num3; value.AveragePoint = (value.AveragePoint * value.Weight + candidate.Point * num2) / num3; value.Weight = num3; value.Score += candidate.BaseScore; value.HitCount++; value.NearestDistance = Mathf.Min(value.NearestDistance, candidate.Distance); if (candidate.BaseScore > value.BestCandidateScore) { value.BestCandidateScore = candidate.BaseScore; value.BestVelocityFacingScore = candidate.VelocityFacingScore; value.PieceRootId = candidate.PieceRootId; } clusters[num] = value; } for (int k = 0; k < clusters.Count; k++) { WallCluster value2 = clusters[k]; if (value2.NearestDistance > StridePlugin.WallJumpTriggerDistance) { value2.RejectedForDistance = true; value2.Score = float.MinValue; clusters[k] = value2; } else { value2.Score += (float)value2.HitCount * 0.45f; value2.PlayerPlaneOffset = value2.PlaneDepth - Vector3.Dot(value2.Normal, position); value2.ForwardFlowScore = CalculateForwardFlowScore(horizontalVelocity, value2.Normal); clusters[k] = value2; } } for (int l = 0; l < clusters.Count; l++) { WallCluster value3 = clusters[l]; if (value3.RejectedForDistance) { continue; } float num4 = 0f; for (int m = 0; m < clusters.Count; m++) { if (l == m) { continue; } WallCluster wallCluster = clusters[m]; if (!(Vector3.Dot(value3.Normal, wallCluster.Normal) < 0.92f)) { Vector3 val3 = wallCluster.AveragePoint - value3.AveragePoint; if (!(((Vector3)(ref val3)).sqrMagnitude > 1.8225001f) && wallCluster.PlayerPlaneOffset > 0f && value3.PlayerPlaneOffset > 0f && wallCluster.PlayerPlaneOffset + 0.18f < value3.PlayerPlaneOffset) { num4 = Mathf.Max(num4, value3.PlayerPlaneOffset - wallCluster.PlayerPlaneOffset); } } } value3.FrontFacePenalty = num4; value3.Score -= num4 * 8f; value3.Score += Mathf.Clamp01(1f - value3.NearestDistance / Mathf.Max(0.01f, StridePlugin.WallCheckDistance)) * 0.6f; value3.Score += (float)Mathf.Min(value3.HitCount, 4) * 0.15f; if (value3.HitCount == 1 && (value3.BestVelocityFacingScore < 0.9f || value3.NearestDistance > 0.06f)) { value3.Score -= 1.1f; } value3.Score += value3.ForwardFlowScore * 6f; clusters[l] = value3; } } private static bool CanJoinCluster(WallCandidate candidate, WallCluster cluster) { //IL_0001: 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) if (Vector3.Dot(candidate.Normal, cluster.Normal) < 0.92f) { return false; } if (Mathf.Abs(candidate.PlaneDepth - cluster.PlaneDepth) > 0.2f) { return false; } return true; } private static float CalculateForwardFlowScore(Vector3 horizontalVelocity, Vector3 wallNormal) { //IL_0014: 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) //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_004a: 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_005c: 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_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_007f: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref horizontalVelocity)).sqrMagnitude < 0.122499995f) { return 0f; } Vector3 val = wallNormal; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { return 0f; } ((Vector3)(ref val)).Normalize(); Vector3 normalized = ((Vector3)(ref horizontalVelocity)).normalized; float num = Mathf.Max(0f, Vector3.Dot(normalized, -val)); Vector3 val2 = Vector3.ProjectOnPlane(normalized, val); if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { return 0f; } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.Reflect(normalized, val); val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f) { return 0f; } ((Vector3)(ref val3)).Normalize(); float num2 = Vector3.Dot(val3, val2); float num3 = Vector3.Dot(val3, normalized); return num2 * 0.75f + num3 * 0.25f - num * 0.15f; } private static bool TryGetWallHitNormal(Player player, Vector3 castDirection, RaycastHit hit, bool logDetails, out Vector3 wallNormal) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_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_005e: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) wallNormal = default(Vector3); if ((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null) { return false; } Character componentInParent = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Character>(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent == (Object)(object)player) { return false; } Vector3 normal = ((RaycastHit)(ref hit)).normal; Vector3 normalized = ((Vector3)(ref normal)).normalized; if (Mathf.Abs(normalized.y) > 0.25f) { return false; } if (Vector3.Dot(castDirection, -normalized) < 0.15f) { return false; } wallNormal = normalized; return true; } private static void PerformWallJump(Player player, StrideState.WallContactSnapshot wallContact, MovementInputSnapshot movementInput) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_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_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: 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_0107: 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_0113: 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_0169: 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_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = TryGetBody((Character)(object)player); if ((Object)(object)val == (Object)null) { return; } Vector3 away = wallContact.Normal; away.y = 0f; ((Vector3)(ref away)).Normalize(); if (((Vector3)(ref away)).sqrMagnitude < 0.01f) { away = -((Component)player).transform.forward; away.y = 0f; ((Vector3)(ref away)).Normalize(); } Vector3 linearVelocity = val.linearVelocity; Vector3 val2 = Vector3.ProjectOnPlane(linearVelocity, Vector3.up); bool flag = false; if (((Vector3)(ref wallContact.ApproachVelocity)).sqrMagnitude > 0.01f && wallContact.ApproachTime > -100f && Time.time - wallContact.ApproachTime <= StridePlugin.CoyoteTimeSeconds && ((Vector3)(ref wallContact.ApproachVelocity)).magnitude > ((Vector3)(ref val2)).magnitude) { float num = Mathf.Max(0f, Time.time - wallContact.ApproachTime); float num2 = Mathf.Clamp01(1f - num / Mathf.Max(0.01f, StridePlugin.CoyoteTimeSeconds)); num2 *= num2; Vector3 val3 = Vector3.Lerp(val2, wallContact.ApproachVelocity, num2); float magnitude = ((Vector3)(ref val2)).magnitude; float num3 = ((magnitude > 0.01f) ? (magnitude * 1.25f) : ((Vector3)(ref wallContact.ApproachVelocity)).magnitude); float magnitude2 = ((Vector3)(ref val3)).magnitude; if (magnitude2 > num3 && magnitude2 > 0.01f) { val3 = ((Vector3)(ref val3)).normalized * num3; } val2 = val3; flag = true; } float magnitude3 = ((Vector3)(ref val2)).magnitude; if (magnitude3 < StridePlugin.MinimumWallJumpEntrySpeed) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Allowing wall jump despite low incoming horizontal speed {magnitude3:0.###} because wall contact is valid."); } bool isTired = IsVanillaTiredJump(player, linearVelocity); Vector3 currentVelocity = default(Vector3); ((Vector3)(ref currentVelocity))..ctor(val2.x, linearVelocity.y, val2.z); Vector3 val4 = BuildWallJumpVelocity(player, currentVelocity, away, isTired, movementInput); LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump incoming velocity {FormatVector(linearVelocity)} speed {((Vector3)(ref linearVelocity)).magnitude:0.###} direction {FormatDirection(linearVelocity)}."); if (flag) { LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump used decayed stored wall approach horizontal {FormatVector(val2)} speed {magnitude3:0.###}."); } LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump launch input local {FormatVector2(movementInput.RawLocalInput)} clamped {FormatVector2(movementInput.ClampedLocalInput)} magnitude {movementInput.Magnitude:0.###} forward {movementInput.ForwardAmount:0.###} world {FormatVector(movementInput.WorldDirection)}."); LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump outgoing velocity {FormatVector(val4)} speed {((Vector3)(ref val4)).magnitude:0.###} direction {FormatDirection(val4)}."); LogDebug(StridePlugin.DebugLogFeature.WallJump, BuildWallJumpLaunchReport(wallContact, away, linearVelocity, val2, val4, movementInput, flag)); MaxAirAltitudeRef.Invoke((Character)(object)player) = ((Component)player).transform.position.y; ((Character)player).ForceJump(val4, true); StrideState.RecordWallJump(); LogDebug(StridePlugin.DebugLogFeature.WallJump, "Wall jump triggered."); } private static bool IsVanillaTiredJump(Player player, Vector3 currentVelocity) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) float num = JumpStaminaUsageRef.Invoke((Character)(object)player); bool num2 = !((Character)player).HaveStamina(num); if (num2 && ((Character)player).IsPlayer()) { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } float num3 = CharacterSpeedRef.Invoke((Character)(object)player); SEMan val = CharacterSeManRef.Invoke((Character)(object)player); if (val != null) { val.ApplyStatusEffectSpeedMods(ref num3, currentVelocity); } if (!num2) { return num3 <= 0f; } return true; } private static Vector3 BuildWallJumpVelocity(Player player, Vector3 currentVelocity, Vector3 away, bool isTired, MovementInputSnapshot movementInput) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0014: 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) //IL_0036: 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_0039: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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) //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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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) //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_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: 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) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01af: 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_021d: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(currentVelocity, Vector3.up); float magnitude = ((Vector3)(ref val)).magnitude; float num = Vector3.Dot(val, away); float num2 = Mathf.Max(0f, 0f - num); float num3 = Mathf.Max(0f, num); Vector3 val2 = val - away * num + away * (num3 + num2 * StridePlugin.ReflectionResponsiveness); if (movementInput.HasDirectionalInput) { Vector3 worldDirection = movementInput.WorldDirection; float magnitude2 = movementInput.Magnitude; Vector3 val3 = Vector3.ProjectOnPlane(val2, away); Vector3 val4 = Vector3.ProjectOnPlane(worldDirection, away); if (((Vector3)(ref val4)).sqrMagnitude > 0.01f && (((Vector3)(ref val3)).sqrMagnitude < 0.01f || Vector3.Dot(((Vector3)(ref val3)).normalized, ((Vector3)(ref val4)).normalized) >= -0.1f)) { ((Vector3)(ref val4)).Normalize(); float num4 = Mathf.Max(0.1f, Mathf.Min(StridePlugin.HorizontalBounceMinSpeed * StridePlugin.InputBiasFactor, ((Vector3)(ref val2)).magnitude * 0.35f)); val2 += val4 * (num4 * magnitude2); } } float num5 = Mathf.Lerp(0.1f, 1f, Mathf.Clamp01(magnitude / Mathf.Max(0.01f, StridePlugin.HorizontalBounceMinSpeed))); float num6 = Mathf.Max(num2, StridePlugin.OutwardBounceMinSpeed * num5); float num7 = Mathf.Max(magnitude, StridePlugin.HorizontalBounceMinSpeed * num5); float num8 = Mathf.Min(num6, Mathf.Max(num2, magnitude + 1.5f)); float num9 = Mathf.Min(num7, magnitude + 2f); float num10 = Vector3.Dot(val2, away); if (num10 < num8) { val2 += away * (num8 - num10); } float magnitude3 = ((Vector3)(ref val2)).magnitude; if (magnitude3 < num9) { val2 = ((magnitude3 > 0.01f) ? (val2 / magnitude3) : away) * num9; } float num11 = Mathf.Max(currentVelocity.y * 0.25f, StridePlugin.VerticalPushForce); Vector3 val5 = val2 + Vector3.up * num11; if (isTired) { val5 *= JumpForceTiredFactorRef.Invoke((Character)(object)player); } SEMan val6 = CharacterSeManRef.Invoke((Character)(object)player); if (val6 != null) { val6.ApplyStatusEffectJumpMods(ref val5); } return val5; } private static MovementInputSnapshot SampleMovementInput(Player player) { //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_00a3: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) MovementInputSnapshot result = default(MovementInputSnapshot); Vector2 val = Vector2.zero; if (ZInput.GetButton("Forward")) { val.y += 1f; } if (ZInput.GetButton("Backward")) { val.y -= 1f; } if (ZInput.GetButton("Right")) { val.x += 1f; } if (ZInput.GetButton("Left")) { val.x -= 1f; } val.x += ZInput.GetJoyLeftStickX(true); val.y += 0f - ZInput.GetJoyLeftStickY(true); result.RawLocalInput = val; result.ForwardAmount = Mathf.Clamp01(Mathf.Max(val.y, 0f)); result.Magnitude = Mathf.Clamp01(((Vector2)(ref val)).magnitude); if (result.Magnitude < 0.1f) { return result; } val = (result.ClampedLocalInput = Vector2.ClampMagnitude(val, 1f)); Transform obj = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : ((Component)player).transform); Vector3 forward = obj.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { forward = ((Component)player).transform.forward; forward.y = 0f; } ((Vector3)(ref forward)).Normalize(); Vector3 right = obj.right; right.y = 0f; if (((Vector3)(ref right)).sqrMagnitude < 0.01f) { right = ((Component)player).transform.right; right.y = 0f; } ((Vector3)(ref right)).Normalize(); Vector3 val2 = forward * val.y + right * val.x; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { result.Magnitude = 0f; result.ForwardAmount = 0f; result.ClampedLocalInput = Vector2.zero; result.WorldDirection = Vector3.zero; return result; } result.WorldDirection = ((Vector3)(ref val2)).normalized; return result; } private static void LogDebug(StridePlugin.DebugLogFeature feature, string message) { if (StridePlugin.IsDebugEnabled(feature)) { StridePlugin.Log.LogInfo((object)message); } } private static void LogWallClusters(List<WallCluster> clusters) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < clusters.Count; i++) { WallCluster wallCluster = clusters[i]; LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Cluster {i + 1}: normal {FormatVector(wallCluster.Normal)}, hits {wallCluster.HitCount}, plane offset {wallCluster.PlayerPlaneOffset:0.###}, nearest {wallCluster.NearestDistance:0.###}, score {wallCluster.Score:0.###}, best facing {wallCluster.BestVelocityFacingScore:0.###}, flow {wallCluster.ForwardFlowScore:0.###}, penalty {wallCluster.FrontFacePenalty:0.###}, rejectedDistance {wallCluster.RejectedForDistance}, piece {wallCluster.PieceRootId}."); } } private static int GetPieceRootId(Piece piece, Collider collider) { if ((Object)(object)piece != (Object)null) { return ((Object)((Component)piece).gameObject).GetInstanceID(); } if ((Object)(object)collider != (Object)null) { return ((Object)((Component)((Component)collider).transform.root).gameObject).GetInstanceID(); } return 0; } internal static string FormatVector(Vector3 value) { //IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references) return $"({value.x:0.###}, {value.y:0.###}, {value.z:0.###})"; } internal static string FormatVector2(Vector2 value) { //IL_0005: 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) return $"({value.x:0.###}, {value.y:0.###})"; } private static string BuildWallJumpLaunchReport(StrideState.WallContactSnapshot wallContact, Vector3 away, Vector3 currentVelocity, Vector3 launchHorizontalVelocity, Vector3 jumpVelocity, MovementInputSnapshot movementInput, bool usedSnapshotApproachVelocity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_000e: 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_0019: 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_0021: 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_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_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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0045: 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_0068: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(currentVelocity, Vector3.up); Vector3 val2 = Vector3.ProjectOnPlane(jumpVelocity, Vector3.up); float num = Vector3.Dot(val, away); float num2 = Vector3.Dot(launchHorizontalVelocity, away); float num3 = Vector3.Dot(val2, away); float num4 = CalculateTangentialSpeed(val, away); float num5 = CalculateTangentialSpeed(launchHorizontalVelocity, away); float num6 = CalculateTangentialSpeed(val2, away); float y = jumpVelocity.y; float num7 = (movementInput.HasDirectionalInput ? Vector3.Dot(movementInput.WorldDirection, away) : 0f); return string.Format("Wall jump report | wall {0} near {1:0.###} | input raw {2} clamp {3} mag {4:0.###} fwd {5:0.###} align {6:0.###} world {7} | inH {8} outDot {9:0.###} tan {10:0.###} | launchH {11} outDot {12:0.###} tan {13:0.###} mem {14} | outH {15} outDot {16:0.###} tan {17:0.###} up {18:0.###}", FormatVector(wallContact.Normal), wallContact.NearestDistance, FormatVector2(movementInput.RawLocalInput), FormatVector2(movementInput.ClampedLocalInput), movementInput.Magnitude, movementInput.ForwardAmount, num7, FormatVector(movementInput.WorldDirection), FormatVector(val), num, num4, FormatVector(launchHorizontalVelocity), num2, num5, usedSnapshotApproachVelocity ? "yes" : "no", FormatVector(val2), num3, num6, y); } private static float CalculateTangentialSpeed(Vector3 velocity, Vector3 away) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0013: Unknown result type (might be due to invalid IL or missing references) Vector3 val = velocity - away * Vector3.Dot(velocity, away); return ((Vector3)(ref val)).magnitude; } private static string FormatDirection(Vector3 value) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref value)).sqrMagnitude < 0.0001f) { return "(0, 0, 0)"; } return FormatVector(((Vector3)(ref value)).normalized); } internal static Rigidbody TryGetBody(Character character) { if (!((Object)(object)character == (Object)null)) { return CharacterBodyRef.Invoke(character); } return null; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class PlayerSetControlsFallRollPatch { private static readonly FieldRef<Player, bool> CrouchToggledRef = AccessTools.FieldRefAccess<Player, bool>("m_crouchToggled"); private static Player trackedPlayer; private static bool lastDodgeIntent; private static void Prefix(Player __instance, bool blockHold, bool jump, bool dodge) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0100: 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) if ((Object)(object)__instance != (Object)(object)trackedPlayer) { trackedPlayer = __instance; lastDodgeIntent = false; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || ((Character)__instance).IsSwimming()) { lastDodgeIntent = false; return; } if (StridePlugin.FallRollEnabled == null || !StridePlugin.FallRollEnabled.Value) { lastDodgeIntent = false; return; } Rigidbody val = PlayerUpdateWallJumpPatch.TryGetBody((Character)(object)__instance); if ((Object)(object)val == (Object)null || ((Character)__instance).IsOnGround() || val.linearVelocity.y >= -0.1f) { lastDodgeIntent = false; return; } bool flag = false; if ((int)ZInput.InputLayout == 0 || !ZInput.IsGamepadActive()) { bool flag2 = ((Character)__instance).IsCrouching() || CrouchToggledRef.Invoke(__instance); flag = jump && (blockHold || flag2); } else { bool flag3 = ((Character)__instance).IsCrouching() || CrouchToggledRef.Invoke(__instance); flag = dodge && (blockHold || flag3); } if (!flag) { lastDodgeIntent = false; } else if (!lastDodgeIntent) { lastDodgeIntent = true; StrideState.RecordFallRollAttempt(0f - val.linearVelocity.y); LogDebug(StridePlugin.DebugLogFeature.FallRoll, $"Buffered fall roll attempt at downward speed {0f - val.linearVelocity.y:0.###}."); } } private static void LogDebug(StridePlugin.DebugLogFeature feature, string message) { if (StridePlugin.IsDebugEnabled(feature)) { StridePlugin.Log.LogInfo((object)message); } } } [HarmonyPatch(typeof(SEMan), "ModifyFallDamage")] internal static class SEManModifyFallDamagePatch { private static readonly FieldRef<SEMan, Character> OwnerRef = AccessTools.FieldRefAccess<SEMan, Character>("m_character"); private static readonly FieldRef<Player, EffectList> PerfectDodgeEffectsRef = AccessTools.FieldRefAccess<Player, EffectList>("m_perfectDodgeEffects"); private static void Prefix(SEMan __instance, float baseDamage, ref float damage) { if (StridePlugin.FallRollEnabled != null && StridePlugin.FallRollEnabled.Value) { Character val = ((__instance != null) ? OwnerRef.Invoke(__instance) : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { LogFallRollResult($"HOOK: baseDamage={baseDamage:0.###} damageBeforeEffects={damage:0.###} hasBufferedAttempt={StrideState.HasPendingFallRollAttemptForDebug()}"); } } } private static void Postfix(SEMan __instance, float baseDamage, ref float damage) { //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) if (StridePlugin.FallRollEnabled == null || !StridePlugin.FallRollEnabled.Value || __instance == null) { return; } Character val = OwnerRef.Invoke(__instance); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !(damage <= 0f) && StrideState.TryConsumeFallRollDamageReduction(out var reduction, out var bufferedAge, out var downwardSpeed)) { float num = Mathf.Clamp01(reduction); float num2 = damage; damage *= 1f - num; float num3 = Mathf.Max(0f, num2 - damage); Player val2 = (Player)(object)((val is Player) ? val : null); if (val2 != null) { PerfectDodgeEffectsRef.Invoke(val2).Create(((Component)val2).transform.position, Quaternion.identity, ((Component)val2).transform, 1f, -1); ((Character)val2).Message((MessageType)2, "Perfect landing", 0, (Sprite)null); } LogFallRollResult($"SUCCESS: bufferedAge={bufferedAge:0.###}s downwardSpeed={downwardSpeed:0.###} baseDamage={baseDamage:0.###} originalDamage={num2:0.###} savedDamage={num3:0.###} finalDamage={damage:0.###} reduction={num * 100f:0.#}%"); LogDebug(StridePlugin.DebugLogFeature.FallRoll, $"Applied buffered fall roll mitigation. Reduced fall damage by {num * 100f:0.#}% after {bufferedAge:0.###} s buffer at downward speed {downwardSpeed:0.###}."); } } private static void LogDebug(StridePlugin.DebugLogFeature feature, string message) { if (StridePlugin.IsDebugEnabled(feature)) { StridePlugin.Log.LogInfo((object)message); } } private static void LogFallRollResult(string message) { if (StridePlugin.IsDebugEnabled(StridePlugin.DebugLogFeature.FallRoll)) { ManualLogSource log = StridePlugin.Log; if (log != null) { log.LogInfo((object)("[FallRoll] " + message)); } } } } [HarmonyPatch(typeof(Player), "HaveStamina")] internal static class PlayerHaveStaminaDebtPatch { private static bool Prefix(Player __instance, float amount, ref bool __result) { if (!StaminaDebt.ShouldManage(__instance)) { return true; } if (StaminaDebt.IsInDebt(__instance)) { StaminaDebt.LogBlockedSpend(__instance, amount); __result = false; return false; } if (StaminaDebt.CanBorrowForChunkedUse(__instance, amount)) { __result = true; return false; } return true; } } [HarmonyPatch(typeof(Player), "RPC_UseStamina")] internal static class PlayerRpcUseStaminaDebtPatch { private static bool Prefix(Player __instance, float v) { if (!StaminaDebt.ShouldManage(__instance)) { return true; } if (!StaminaDebt.IsFinite(v) || v == 0f) { return false; } if (StaminaDebt.IsInDebt(__instance)) { StaminaDebt.LogBlockedSpend(__instance, v); return false; } if (!StaminaDebt.CanBorrowForChunkedUse(__instance, v)) { return true; } StaminaDebt.ApplyDebt(__instance, v); return false; } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class PlayerUpdateStatsDebtPatch { private static void Prefix(Player __instance, out float __state) { __state = StaminaDebt.GetCurrentStamina(__instance); if (StaminaDebt.ShouldManage(__instance) && StaminaDebt.IsInDebt(__instance)) { StaminaDebt.ClearRegenDelay(__instance); } } private static void Postfix(Player __instance, float __state) { StaminaDebt.LogUpdate(__instance, __state); } } [HarmonyPatch(typeof(Hud), "UpdateStamina")] internal static class HudStaminaDebtPatch { private static void Postfix(Hud __instance, Player player) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { if (StaminaDebt.IsInDebt(player)) { StaminaDebt.ApplyHudDebtVisuals(__instance, player); } else { StaminaDebt.RestoreHudVisuals(__instance); } } } } [HarmonyPatch(typeof(Player), "UpdateFood", new Type[] { typeof(float), typeof(bool) })] internal static class PlayerUpdateFoodDebtPatch { private static void Prefix(Player __instance, out float __state) { __state = (StaminaDebt.ShouldManage(__instance) ? StaminaDebt.GetCurrentStamina(__instance) : 0f); } private static void Postfix(Player __instance, float __state) { if (StaminaDebt.ShouldManage(__instance) && !(__state >= 0f)) { StaminaDebt.RestoreDebtAfterFoodUpdateClamp(__instance, __state); } } } internal static class StaminaDebt { private const float MinimumChunkedDebtUse = 5f; private const float MaxDebtVisualRatio = 1.25f; private static readonly Color DebtBarColor = new Color(1f, 0.2f, 0.2f, 1f); private static readonly FieldRef<Character, ZNetView> CharacterNViewRef = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview"); private static readonly FieldRef<GuiBar, RectTransform> GuiBarRectRef = AccessTools.FieldRefAccess<GuiBar, RectTransform>("m_bar"); private static readonly FieldRef<GuiBar, float> GuiBarWidthRef = AccessTools.FieldRefAccess<GuiBar, float>("m_width"); private static readonly FieldRef<Player, float> PlayerStaminaRef = AccessTools.FieldRefAccess<Player, float>("m_stamina"); private static readonly FieldRef<Player, float> PlayerStaminaRegenTimerRef = AccessTools.FieldRefAccess<Player, float>("m_staminaRegenTimer"); private static bool wasDebtActiveLastUpdate; private static bool wasDebtHudActiveLastUpdate; private static int lastLoggedDebtWholeValue = int.MinValue; private static int lastLoggedBlockedSpendWholeValue = int.MinValue; private static float lastLoggedBlockedSpendAmount = float.NaN; internal static void ResetTracking() { wasDebtActiveLastUpdate = false; wasDebtHudActiveLastUpdate = false; lastLoggedDebtWholeValue = int.MinValue; lastLoggedBlockedSpendWholeValue = int.MinValue; lastLoggedBlockedSpendAmount = float.NaN; } internal static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } internal static bool ShouldManage(Player player) { if (StridePlugin.StaminaDebtEnabled == null || !StridePlugin.StaminaDebtEnabled.Value) { return false; } if ((Object)(object)player == (Object)null) { return false; } ZNetView val = CharacterNViewRef.Invoke((Character)(object)player); if ((Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)val != (Object)null && val.IsValid()) { return val.IsOwner(); } return false; } internal static float GetCurrentStamina(Player player) { if (!((Object)(object)player != (Object)null)) { return 0f; } return PlayerStaminaRef.Invoke(player); } internal static bool IsInDebt(Player player) { if ((Object)(object)player != (Object)null) { return PlayerStaminaRef.Invoke(player) < 0f; } return false; } internal static bool CanBorrowForChunkedUse(Player player, float amount) { if ((Object)(object)player == (Object)null || !IsFinite(amount) || amount < 5f) { return false; } float num = PlayerStaminaRef.Invoke(player); if (num > 0f) { return amount > num; } return false; } internal static void ApplyDebt(Player player, float amount) { if (!((Object)(object)player == (Object)null) && IsFinite(amount) && !(amount <= 0f)) { float num = PlayerStaminaRef.Invoke(player); PlayerStaminaRef.Invoke(player) -= amount; PlayerStaminaRegenTimerRef.Invoke(player) = 0f; Log($"Borrowed stamina debt. Cost {amount:0.###}, stamina {num:0.###} -> {PlayerStaminaRef.Invoke(player):0.###}."); } } internal static void ClearRegenDelay(Player player) { PlayerStaminaRegenTimerRef.Invoke(player) = 0f; } internal static void ApplyHudDebtVisuals(Hud hud, Player player) { float num = PlayerStaminaRef.Invoke(player); float num2 = Mathf.Max(1f, ((Character)player).GetMaxStamina()); float debtRatio = Mathf.Min(1.25f, (0f - num) / num2); hud.m_staminaText.text = StringExtensionMethods.ToFastString(Mathf.CeilToInt(num)); SetDebtBar(hud.m_staminaBar2Fast, debtRatio); SetDebtBar(hud.m_staminaBar2Slow, debtRatio); wasDebtHudActiveLastUpdate = true; } internal static void RestoreHudVisuals(Hud hud) { if (wasDebtHudActiveLastUpdate) { RestoreBar(hud.m_staminaBar2Fast); RestoreBar(hud.m_staminaBar2Slow); wasDebtHudActiveLastUpdate = false; } } private static void SetDebtBar(GuiBar bar, float debtRatio) { //IL_001a: 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) //IL_0040: 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_0055: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)bar == (Object)null)) { if (((Behaviour)bar).enabled) { ((Behaviour)bar).enabled = false; } bar.SetColor(DebtBarColor); RectTransform val = GuiBarRectRef.Invoke(bar); if ((Object)(object)val != (Object)null) { Vector3 localScale = ((Transform)val).localScale; localScale.x = 0f - Mathf.Abs(localScale.x); ((Transform)val).localScale = localScale; val.SetSizeWithCurrentAnchors((Axis)0, GuiBarWidthRef.Invoke(bar) * debtRatio); } } } private static void RestoreBar(GuiBar bar) { //IL_0027: 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) //IL_002f: 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) if (!((Object)(object)bar == (Object)null)) { bar.ResetColor(); RectTransform val = GuiBarRectRef.Invoke(bar); if ((Object)(object)val != (Object)null) { Vector3 localScale = ((Transform)val).localScale; localScale.x = Mathf.Abs(localScale.x); ((Transform)val).localScale = localScale; } if (!((Behaviour)bar).enabled) { ((Behaviour)bar).enabled = true; } } } internal static void LogUpdate(Player player, float previousStamina) { if (!ShouldManage(player)) { return; } float num = PlayerStaminaRef.Invoke(player); bool num2 = num < 0f; if (num2) { int num3 = Mathf.CeilToInt(num); if (!wasDebtActiveLastUpdate) { Log($"Debt active. Stamina crossed below zero: {previousStamina:0.###} -> {num:0.###}."); } else if (num3 != lastLoggedDebtWholeValue) { Log($"Debt repaying. Stamina {previousStamina:0.###} -> {num:0.###}, regen timer {PlayerStaminaRegenTimerRef.Invoke(player):0.###}."); } lastLoggedDebtWholeValue = num3; } else if (wasDebtActiveLastUpdate) { Log($"Debt cleared. Stamina {previousStamina:0.###} -> {num:0.###}."); lastLoggedDebtWholeValue = int.MinValue; lastLoggedBlockedSpendWholeValue = int.MinValue; lastLoggedBlockedSpendAmount = float.NaN; } wasDebtActiveLastUpdate = num2; } internal static void LogBlockedSpend(Player player, float amount) { if (!((Object)(object)player == (Object)null)) { float num = PlayerStaminaRef.Invoke(player); int num2 = Mathf.CeilToInt(num); if (num2 != lastLoggedBlockedSpendWholeValue || !(Mathf.Abs(amount - lastLoggedBlockedSpendAmount) < 0.01f)) { lastLoggedBlockedSpendWholeValue = num2; lastLoggedBlockedSpendAmount = amount; Log($"Blocked stamina spend while in debt. Cost {amount:0.###}, current stamina {num:0.###}."); } } } internal static void RestoreDebtAfterFoodUpdateClamp(Player player, float debtStamina) { float num = PlayerStaminaRef.Invoke(player); if (num == 0f) { PlayerStaminaRef.Invoke(player) = debtStamina; Log($"Restored stamina debt after food update clamp. Stamina {num:0.###} -> {debtStamina:0.###}."); } } private static void Log(string message) { if (StridePlugin.IsDebugEnabled(StridePlugin.DebugLogFeature.StaminaDebt)) { ManualLogSource log = StridePlugin.Log; if (log != null) { log.LogInfo((object)("[StaminaDebt] " + message)); } } } } internal static class StrideState { internal struct WallContactSnapshot { internal Vector3 Normal; internal Vector3 ApproachVelocity; internal float ContactTime; internal float ApproachTime; internal float NearestDistance; internal static WallContactSnapshot Invalid { get { //IL_000a: 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_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) WallContactSnapshot result = default(WallContactSnapshot); result.Normal = Vector3.zero; result.ApproachVelocity = Vector3.zero; result.ContactTime = -999f; result.ApproachTime = -999f; result.NearestDistance = float.MaxValue; return result; } } } private static float lastGroundedTime = -999f; private static float lastGroundJumpTime = -999f; private static float lastJumpPressedTime = -999f; private static float lastWallJumpTime = -999f; private static float lastFallRollAttemptTime = -999f; private static float lastFallRollDownwardSpeed; private static bool suppressNextGroundJumpLog; private static bool usedGroundJumpSinceLastGrounded; private static bool usedWallJumpThisAirborneState; private static WallContactSnapshot lastWallContact = WallContactSnapshot.Invalid; internal static void RecordJumpPressed() { if (UsesBufferedJumpTracking()) { lastJumpPressedTime = Time.time; } } internal static void RecordGrounded() { lastGroundedTime = Time.time; usedGroundJumpSinceLastGrounded = false; lastWallContact = WallContactSnapshot.Invalid; } internal static void RecordGroundJump() { lastGroundJumpTime = Time.time; usedGroundJumpSinceLastGrounded = true; lastJumpPressedTime = -999f; usedWallJumpThisAirborneState = false; lastWallContact = WallContactSnapshot.Invalid; if (suppressNextGroundJumpLog) { suppressNextGroundJumpLog = false; } else { LogJumpTiming("Recorded regular jump."); } } internal static void RecordWallJump() { lastWallJumpTime = Time.time; lastJumpPressedTime = -999f; usedWallJumpThisAirborneState = true; lastWallContact = WallContactSnapshot.Invalid; } internal static void RecordCoyoteJump() { lastGroundJumpTime = Time.time; usedGroundJumpSinceLastGrounded = true; usedWallJumpThisAirborneState = false; lastWallContact = WallContactSnapshot.Invalid; } internal static void RecordWallContact(Vector3 wallNormal, Vector3 horizontalVelocity, float nearestDistance) { //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_0047: 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_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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (StridePlugin.WallJumpEnabled == null || !StridePlugin.WallJumpEnabled.Value) { lastWallContact = WallContactSnapshot.Invalid; } else if (!(((Vector3)(ref wallNormal)).sqrMagnitude < 0.01f)) { float time = Time.time; Vector3 normalized = ((Vector3)(ref wallNormal)).normalized; if (!IsWallContactRecent() || !(Vector3.Dot(lastWallContact.Normal, normalized) >= 0.8f) || !(nearestDistance <= lastWallContact.NearestDistance + 0.12f)) { WallContactSnapshot wallContactSnapshot = default(WallContactSnapshot); wallContactSnapshot.Normal = normalized; wallContactSnapshot.NearestDistance = nearestDistance; wallContactSnapshot.ContactTime = time; wallContactSnapshot.ApproachTime = -999f; wallContactSnapshot.ApproachVelocity = Vector3.zero; lastWallContact = wallContactSnapshot; } else { lastWallContact.ContactTime = time; lastWallContact.Normal = normalized; lastWallContact.NearestDistance = Mathf.Min(lastWallContact.NearestDistance, nearestDistance); } float magnitude = ((Vector3)(ref horizontalVelocity)).magnitude; float num = ((magnitude > 0.01f) ? Vector3.Dot(horizontalVelocity / magnitude, -normalized) : 0f); if (magnitude >= StridePlugin.MinimumWallJumpEntrySpeed && num >= 0.05f && magnitude > ((Vector3)(ref lastWallContact.ApproachVelocity)).magnitude) { lastWallContact.ApproachTime = time; lastWallContact.ApproachVelocity = horizontalVelocity; } } } internal static void RecordFallRollAttempt(float downwardSpeed) { if (StridePlugin.FallRollEnabled != null && StridePlugin.FallRollEnabled.Value) { lastFallRollAttemptTime = Time.time; lastFallRollDownwardSpeed = Mathf.Max(0f, downwardSpeed); } } private static void ClearFallRollAttempt() { lastFallRollAttemptTime = -999f; lastFallRollDownwardSpeed = 0f; } internal static void ResetAirborneState() { usedWallJumpThisAirborneState = false; lastWallContact = WallContactSnapshot.Invalid; ClearFallRollAttempt(); } internal static void ResetAll() { lastGroundedTime = -999f; lastGroundJumpTime = -999f; lastJumpPressedTime = -999f; lastWallJumpTime = -999f; ClearFallRollAttempt(); lastWallContact = WallContactSnapshot.Invalid; suppressNextGroundJumpLog = false; usedGroundJumpSinceLastGrounded = false; usedWallJumpThisAirborneState = false; } internal static bool TryConsumeFallRollDamageReduction(out float reduction, out float bufferedAge, out float downwardSpeed) { if (StridePlugin.FallRollEnabled == null || !StridePlugin.FallRollEnabled.Value) { reduction = 0f; bufferedAge = -1f; downwardSpeed = 0f; return false; } bufferedAge = Time.time - lastFallRollAttemptTime; downwardSpeed = lastFallRollDownwardSpeed; reduction = 0f; if (bufferedAge < 0f) { return false; } if (bufferedAge > StridePlugin.FallRollBufferSeconds) { ClearFallRollAttempt(); return false; } reduction = StridePlugin.FallRollDamageReduction; ClearFallRollAttempt(); return reduction > 0f; } private static bool HasPendingFallRollAttempt() { return lastFallRollAttemptTime > -100f; } internal static bool HasPendingFallRollAttemptForDebug() { return HasPendingFallRollAttempt(); } private static void LogFallRollResult(string message) { if (StridePlugin.IsDebugEnabled(StridePlugin.DebugLogFeature.FallRoll)) { ManualLogSource log = StridePlugin.Log; if (log != null) { log.LogInfo((object)("[FallRoll] " + message)); } } } internal static bool TryGetWallJumpContact(Player player, bool hasCurrentWall, PlayerUpdateWallJumpPatch.WallDetectionResult currentWall, out WallContactSnapshot wallContact, out bool usingRememberedWall) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00ac: 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_00d4: 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_00e3: 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) wallContact = default(WallContactSnapshot); usingRememberedWall = false; if (StridePlugin.WallJumpEnabled == null || !StridePlugin.WallJumpEna