using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MoreUIPanel; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("RunBasedPassives")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RunBasedPassives")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d3524128-8d57-4661-a37a-6231cfd2059c")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace RunBasedPassives; [BepInPlugin("cn_xc.RunBasedPassives", "RunBasedPassives", "0.4.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private ConfigEntry enableJump; private ConfigEntry enableStrength; private ConfigEntry enableStamina; private ConfigEntry enableHealth; private ConfigEntry enableSprintSpeed; private ConfigEntry enableCrouchRestore; private ConfigEntry verboseLogging; private ConfigEntry sprintSpeedCap; public static int JumpLevel; public static int StrengthLevel; public static int StaminaLevel; public static int HealthLevel; public static int SprintSpeedLevel; public static int CrouchRestoreLevel; public static int JumpCount; public static int GrabCount; public static float MoveDistance; public static int HealCount; public static float SprintDistance; public static float CrouchRestoreAmount; public static float LastEnergyCurrent = -1f; private static int _lastLogJump; private static int _lastLogGrab; private static int _lastLogHeal; private static int _lastLogCrouch; private static float _lastLogMove; private static float _lastLogSprint; private Harmony harmony; private static FieldRef jumpExtraRef; private static FieldRef maxHealthRef; private static FieldRef steamIDRef; private static FieldRef isLocalRef; private static FieldRef healthPlayerAvatarRef; private static FieldRef>> dictOfDictsRef; private static FieldRef crouchRestActiveRef; private static FieldRef crouchRestRef; private static FieldRef saveFileCurrentRef; private bool _uiRegistered; private float _staminaBase = -1f; private float _originalSprintSpeed = -1f; private string _lastSaveFile = ""; private int GetJumpThreshold() { return 80 + JumpLevel * 40; } private int GetGrabThreshold() { return 50 + StrengthLevel * 10; } private float GetMoveThreshold() { return 500f + (float)StaminaLevel * 200f; } private int GetHealThreshold() { return 10 + HealthLevel * 3; } private float GetSprintThreshold() { return 300f + (float)SprintSpeedLevel * 100f; } private float GetCrouchThreshold() { return 80f + (float)CrouchRestoreLevel * 20f; } private void Awake() { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Expected O, but got Unknown //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Expected O, but got Unknown enableJump = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableJump", true, "启用跳跃升级"); enableStrength = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableStrength", true, "启用力量升级"); enableStamina = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableStamina", true, "启用耐力升级"); enableHealth = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableHealth", true, "启用生命升级"); enableSprintSpeed = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableSprintSpeed", true, "启用冲刺速度升级"); enableCrouchRestore = ((BaseUnityPlugin)this).Config.Bind("Toggles", "EnableCrouchRestore", true, "启用蹲伏恢复升级"); verboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", false, "详细日志输出(用于调试)"); sprintSpeedCap = ((BaseUnityPlugin)this).Config.Bind("Thresholds", "SprintSpeedCap", 0, new ConfigDescription("冲刺速度封顶等级(0=不封顶)", (AcceptableValueBase)(object)new AcceptableValueRange(0, int.MaxValue), Array.Empty())); jumpExtraRef = AccessTools.FieldRefAccess("JumpExtra"); maxHealthRef = AccessTools.FieldRefAccess("maxHealth"); steamIDRef = AccessTools.FieldRefAccess("steamID"); isLocalRef = AccessTools.FieldRefAccess("isLocal"); healthPlayerAvatarRef = AccessTools.FieldRefAccess("playerAvatar"); dictOfDictsRef = AccessTools.FieldRefAccess>>("dictionaryOfDictionaries"); crouchRestActiveRef = AccessTools.FieldRefAccess("upgradeCrouchRestActive"); crouchRestRef = AccessTools.FieldRefAccess("upgradeCrouchRest"); saveFileCurrentRef = AccessTools.FieldRefAccess("saveFileCurrent"); Patches.isLocalRef = isLocalRef; Patches.steamIDRef = steamIDRef; Patches.healthPlayerAvatarRef = healthPlayerAvatarRef; Patches.crouchRestActiveRef = crouchRestActiveRef; Patches.crouchRestRef = crouchRestRef; Patches.pluginLogger = ((BaseUnityPlugin)this).Logger; Patches.verboseLogging = verboseLogging; Harmony.DEBUG = true; harmony = new Harmony("cn_xc.RunBasedPassives"); harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerAvatar), "Jump", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "JumpPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObject), "GrabStarted", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "GrabStartedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerAvatar), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerAvatarUpdatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerHealth), "Heal", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HealPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); SceneManager.sceneLoaded += OnSceneLoaded; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Harmony 补丁总数: {harmony.GetPatchedMethods().Count()}"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RunBasedPassives v0.4.1 loaded"); if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"详细日志已开启(仅在经验变化时输出)"); } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; if (_uiRegistered) { Plugin.UnregisterLine("runbasedpassives_stats"); } harmony.UnpatchAll((string)null); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ResetCounters(); } private void ResetCounters() { string text = saveFileCurrentRef.Invoke(StatsManager.instance) ?? ""; if (_lastSaveFile != text) { _lastSaveFile = text; JumpCount = 0; GrabCount = 0; MoveDistance = 0f; HealCount = 0; SprintDistance = 0f; CrouchRestoreAmount = 0f; if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[场景加载] 检测到存档切换,经验已清零"); } } else { JumpCount %= GetJumpThreshold(); GrabCount %= GetGrabThreshold(); MoveDistance %= GetMoveThreshold(); HealCount %= GetHealThreshold(); SprintDistance %= GetSprintThreshold(); CrouchRestoreAmount %= GetCrouchThreshold(); if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[场景加载] 经验已继承: 跳跃:{JumpCount}/{GetJumpThreshold()} 力量:{GrabCount}/{GetGrabThreshold()} 耐力:{MoveDistance:F0}/{GetMoveThreshold()} 生命:{HealCount}/{GetHealThreshold()} 冲刺:{SprintDistance:F0}/{GetSprintThreshold()} 蹲伏:{CrouchRestoreAmount:F0}/{GetCrouchThreshold()}"); } } _staminaBase = -1f; _originalSprintSpeed = -1f; LastEnergyCurrent = -1f; Patches.ResetState(); } private void Update() { if ((Object)(object)PlayerAvatar.instance == (Object)null) { return; } bool flag = SemiFunc.RunIsLevel() || SemiFunc.RunIsShop(); if (flag && !_uiRegistered) { Plugin.RegisterLine("runbasedpassives_stats", (Func)BuildDisplayText); _uiRegistered = true; } else if (!flag && _uiRegistered) { Plugin.UnregisterLine("runbasedpassives_stats"); _uiRegistered = false; } if (!flag) { return; } string steamID = GetSteamID(); if (!string.IsNullOrEmpty(steamID)) { JumpLevel = ReadGameDict("playerUpgradeExtraJump", steamID); StrengthLevel = ReadGameDict("playerUpgradeStrength", steamID); StaminaLevel = ReadGameDict("playerUpgradeStamina", steamID); HealthLevel = ReadGameDict("playerUpgradeHealth", steamID); SprintSpeedLevel = ReadGameDict("playerUpgradeSpeed", steamID); CrouchRestoreLevel = ReadGameDict("playerUpgradeCrouchRest", steamID); } if (_staminaBase <= 0f && (Object)(object)PlayerController.instance != (Object)null) { _staminaBase = PlayerController.instance.EnergyStart; } CheckUpgrades(); ApplyStats(); if (verboseLogging.Value) { bool flag2 = (int)CrouchRestoreAmount != _lastLogCrouch; if (JumpCount != _lastLogJump || GrabCount != _lastLogGrab || (int)MoveDistance != (int)_lastLogMove || HealCount != _lastLogHeal || (int)SprintDistance != (int)_lastLogSprint || flag2) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[状态] 跳跃:{JumpCount}/{GetJumpThreshold()} Lv.{JumpLevel} | 力量:{GrabCount}/{GetGrabThreshold()} Lv.{StrengthLevel} | 耐力:{MoveDistance:F0}/{GetMoveThreshold()} Lv.{StaminaLevel} | 生命:{HealCount}/{GetHealThreshold()} Lv.{HealthLevel} | 冲刺:{SprintDistance:F0}/{GetSprintThreshold()} Lv.{SprintSpeedLevel} | 蹲伏:{CrouchRestoreAmount:F0}/{GetCrouchThreshold()} Lv.{CrouchRestoreLevel}"); _lastLogJump = JumpCount; _lastLogGrab = GrabCount; _lastLogMove = MoveDistance; _lastLogHeal = HealCount; _lastLogSprint = SprintDistance; _lastLogCrouch = (int)CrouchRestoreAmount; } } } private int ReadGameDict(string dictName, string sid) { SortedDictionary> sortedDictionary = dictOfDictsRef.Invoke(StatsManager.instance); if (sortedDictionary != null && sortedDictionary.TryGetValue(dictName, out var value) && value != null && value.TryGetValue(sid, out var value2)) { return value2; } return 0; } private void CheckUpgrades() { string steamID = GetSteamID(); if (string.IsNullOrEmpty(steamID)) { return; } bool flag = false; if (enableJump.Value && JumpCount >= GetJumpThreshold()) { JumpCount -= GetJumpThreshold(); JumpLevel++; StatsManager instance = StatsManager.instance; if (instance != null) { instance.DictionaryUpdateValue("playerUpgradeExtraJump", steamID, JumpLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 跳跃升级至 Lv.{JumpLevel}"); } flag = true; } if (enableStrength.Value && GrabCount >= GetGrabThreshold()) { GrabCount -= GetGrabThreshold(); StrengthLevel++; StatsManager instance2 = StatsManager.instance; if (instance2 != null) { instance2.DictionaryUpdateValue("playerUpgradeStrength", steamID, StrengthLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 力量升级至 Lv.{StrengthLevel}"); } flag = true; } if (enableStamina.Value && MoveDistance >= GetMoveThreshold()) { MoveDistance -= GetMoveThreshold(); StaminaLevel++; StatsManager instance3 = StatsManager.instance; if (instance3 != null) { instance3.DictionaryUpdateValue("playerUpgradeStamina", steamID, StaminaLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 耐力升级至 Lv.{StaminaLevel}"); } flag = true; } if (enableHealth.Value && HealCount >= GetHealThreshold()) { HealCount -= GetHealThreshold(); HealthLevel++; StatsManager instance4 = StatsManager.instance; if (instance4 != null) { instance4.DictionaryUpdateValue("playerUpgradeHealth", steamID, HealthLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 生命升级至 Lv.{HealthLevel}"); } flag = true; } if (enableSprintSpeed.Value && SprintDistance >= GetSprintThreshold()) { SprintDistance -= GetSprintThreshold(); SprintSpeedLevel++; StatsManager instance5 = StatsManager.instance; if (instance5 != null) { instance5.DictionaryUpdateValue("playerUpgradeSpeed", steamID, SprintSpeedLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 冲刺速度升级至 Lv.{SprintSpeedLevel}"); } flag = true; } if (enableCrouchRestore.Value && CrouchRestoreAmount >= GetCrouchThreshold()) { CrouchRestoreAmount -= GetCrouchThreshold(); CrouchRestoreLevel++; StatsManager instance6 = StatsManager.instance; if (instance6 != null) { instance6.DictionaryUpdateValue("playerUpgradeCrouchRest", steamID, CrouchRestoreLevel); } if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[升级] 蹲伏恢复升级至 Lv.{CrouchRestoreLevel}"); } flag = true; } if (flag && (Object)(object)StatsUI.instance != (Object)null) { StatsUI.instance.Fetch(); } } private void ApplyStats() { PlayerController instance = PlayerController.instance; PlayerAvatar instance2 = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return; } if (enableJump.Value) { jumpExtraRef.Invoke(instance) = JumpLevel; } if (enableStrength.Value) { PhysGrabber physGrabber = instance2.physGrabber; if ((Object)(object)physGrabber != (Object)null) { physGrabber.grabStrength = 1f + (float)StrengthLevel; } } if (enableStamina.Value) { float num = ((_staminaBase > 0f) ? _staminaBase : 100f); instance.EnergyStart = num + (float)(StaminaLevel * 10); if (instance.EnergyCurrent > instance.EnergyStart) { instance.EnergyCurrent = instance.EnergyStart; } } if (enableHealth.Value) { PlayerHealth playerHealth = instance2.playerHealth; if ((Object)(object)playerHealth != (Object)null) { maxHealthRef.Invoke(playerHealth) = 100 + HealthLevel * 20; } } if (enableSprintSpeed.Value) { if (_originalSprintSpeed < 0f) { _originalSprintSpeed = instance.SprintSpeed; } int num2 = SprintSpeedLevel; if (sprintSpeedCap.Value > 0 && num2 > sprintSpeedCap.Value) { num2 = sprintSpeedCap.Value; } instance.SprintSpeed = _originalSprintSpeed + (float)num2 * 1f; } } private string GetSteamID() { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return null; } return steamIDRef.Invoke(instance); } private string BuildDisplayText() { StringBuilder stringBuilder = new StringBuilder(); if (enableJump.Value) { stringBuilder.AppendLine($"跳跃 Lv.{JumpLevel} ({JumpCount}/{GetJumpThreshold()})"); } if (enableStrength.Value) { stringBuilder.AppendLine($"力量 Lv.{StrengthLevel} ({GrabCount}/{GetGrabThreshold()})"); } if (enableStamina.Value) { stringBuilder.AppendLine($"耐力 Lv.{StaminaLevel} ({MoveDistance:F0}/{GetMoveThreshold()})"); } if (enableHealth.Value) { stringBuilder.AppendLine($"生命 Lv.{HealthLevel} ({HealCount}/{GetHealThreshold()})"); } if (enableSprintSpeed.Value) { stringBuilder.AppendLine($"冲刺 Lv.{SprintSpeedLevel} ({SprintDistance:F0}/{GetSprintThreshold()})"); } if (enableCrouchRestore.Value) { stringBuilder.AppendLine($"蹲伏 Lv.{CrouchRestoreLevel} ({CrouchRestoreAmount:F0}/{GetCrouchThreshold()})"); } return stringBuilder.ToString().TrimEnd(Array.Empty()); } } public static class Patches { internal static FieldRef isLocalRef; internal static FieldRef steamIDRef; internal static FieldRef healthPlayerAvatarRef; internal static FieldRef crouchRestActiveRef; internal static FieldRef crouchRestRef; internal static ManualLogSource pluginLogger; internal static ConfigEntry verboseLogging; private static Vector3 lastPos; private static Vector3 lastSprintPos; private static bool lastPosInit; private static bool lastSprintPosInit; private static readonly HashSet grabbedThisLevel = new HashSet(); private static bool IsInGameplay => SemiFunc.RunIsLevel() || SemiFunc.RunIsShop(); public static void ResetState() { lastPosInit = (lastSprintPosInit = false); grabbedThisLevel.Clear(); } public static void JumpPrefix(PlayerAvatar __instance) { if (IsInGameplay && isLocalRef.Invoke(__instance)) { Plugin.JumpCount++; } } public static void GrabStartedPostfix(PhysGrabObject __instance) { if (!IsInGameplay || SemiFunc.RunIsShop()) { return; } int instanceID = ((Object)__instance).GetInstanceID(); if (grabbedThisLevel.Contains(instanceID)) { return; } List playerGrabbing = __instance.playerGrabbing; if (playerGrabbing == null) { return; } foreach (PhysGrabber item in playerGrabbing) { if ((Object)(object)item.playerAvatar != (Object)null && isLocalRef.Invoke(item.playerAvatar)) { grabbedThisLevel.Add(instanceID); Plugin.GrabCount++; break; } } } public static void PlayerAvatarUpdatePostfix(PlayerAvatar __instance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_004e: 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_0066: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00ad: 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) if (!IsInGameplay || !isLocalRef.Invoke(__instance)) { return; } Vector3 position = ((Component)__instance).transform.position; if (!lastPosInit) { lastPos = position; lastPosInit = true; return; } float num = Vector3.Distance(position, lastPos); Plugin.MoveDistance += num; lastPos = position; if ((Object)(object)PlayerController.instance != (Object)null && PlayerController.instance.sprinting) { if (!lastSprintPosInit) { lastSprintPos = position; lastSprintPosInit = true; return; } float num2 = Vector3.Distance(position, lastSprintPos); Plugin.SprintDistance += num2; } lastSprintPos = position; lastSprintPosInit = true; if (crouchRestActiveRef.Invoke(__instance)) { PlayerController instance = PlayerController.instance; if (!((Object)(object)instance != (Object)null)) { return; } float energyCurrent = instance.EnergyCurrent; if (Plugin.LastEnergyCurrent >= 0f) { float num3 = energyCurrent - Plugin.LastEnergyCurrent; if (num3 > 0f) { Plugin.CrouchRestoreAmount += num3; } } Plugin.LastEnergyCurrent = energyCurrent; } else { Plugin.LastEnergyCurrent = -1f; } } public static void HealPrefix(int healAmount, PlayerHealth __instance) { if (IsInGameplay && !SemiFunc.RunIsShop() && healAmount > 0) { PlayerAvatar val = healthPlayerAvatarRef.Invoke(__instance); if (!((Object)(object)val == (Object)null) && isLocalRef.Invoke(val)) { Plugin.HealCount++; } } } }