using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Peak.Afflictions; using Photon.Pun; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FatigueMod { [HarmonyPatch(typeof(Action_GiveExtraStamina), "RunAction")] internal static class Action_GiveExtraStamina_Patch { private static FieldInfo characterField; [HarmonyPostfix] private static void Postfix(Action_GiveExtraStamina __instance) { if (characterField == null) { characterField = typeof(ItemActionBase).GetField("character", BindingFlags.Instance | BindingFlags.NonPublic); } object? obj = characterField?.GetValue(__instance); Character val = (Character)((obj is Character) ? obj : null); if ((Object)(object)val == (Object)null || !val.IsLocal) { return; } FatigueTracker component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { return; } if (val.data.isSkeleton && !val.data.dead && !val.data.fullyPassedOut) { if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Skipping fatigue reduction - target is a living skeleton"); } return; } float amount = __instance.amount; float num = amount * FatiguePlugin.extraStaminaFatigueReductionRatio.Value; float fatigueValue = component.fatigueValue; component.fatigueValue = Mathf.Max(0f, component.fatigueValue - num); if (FatiguePlugin.debugMode.Value && num > 0.001f) { FatiguePlugin.Logger.LogInfo((object)($"Extra stamina ({amount:F2}) reduced fatigue by {num:F3} " + $"(from {fatigueValue:F3} to {component.fatigueValue:F3})")); } } } [HarmonyPatch(typeof(Action_ModifyStatus), "RunAction")] internal static class Action_ModifyStatus_Patch { private static readonly HashSet healingItemIDs = new HashSet { 24 }; private static FieldInfo itemField; private static FieldInfo characterField; private static PropertyInfo characterProperty; [HarmonyPostfix] private static void Postfix(Action_ModifyStatus __instance) { if (itemField == null) { itemField = typeof(ItemActionBase).GetField("item", BindingFlags.Instance | BindingFlags.NonPublic); } object? obj = itemField?.GetValue(__instance); Item val = (Item)((obj is Item) ? obj : null); if ((Object)(object)val == (Object)null || __instance.changeAmount >= 0f || !healingItemIDs.Contains(val.itemID)) { return; } if (characterProperty == null) { characterProperty = typeof(ItemActionBase).GetProperty("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } Character val2 = null; if (characterProperty != null) { object? value = characterProperty.GetValue(__instance); val2 = (Character)((value is Character) ? value : null); } if (val2.data.isSkeleton && !val2.data.dead && !val2.data.fullyPassedOut) { if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Skipping fatigue reduction - target is a living skeleton"); } return; } if ((Object)(object)val2 == (Object)null) { if (characterField == null) { characterField = typeof(ItemActionBase).GetField("character", BindingFlags.Instance | BindingFlags.NonPublic); } object? obj2 = characterField?.GetValue(__instance); val2 = (Character)((obj2 is Character) ? obj2 : null); } if (!((Object)(object)val2 == (Object)null) && val2.IsLocal) { FatigueTracker component = ((Component)val2).GetComponent(); if (!((Object)(object)component == (Object)null)) { float num = Mathf.Abs(__instance.changeAmount) * FatiguePlugin.fatigueReductionMultiplier.Value; component.fatigueValue = Mathf.Max(0f, component.fatigueValue - num); } } } } [HarmonyPatch(typeof(CharacterData), "set_isSkeleton")] internal static class CharacterData_SetSkeleton_Patch { [HarmonyPostfix] private static void Postfix(CharacterData __instance, bool value) { if (!value) { return; } Character component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsLocal) { return; } Component component2 = ((Component)component).GetComponent("FatigueMod.FatigueTracker"); if ((Object)(object)component2 != (Object)null) { FieldInfo field = ((object)component2).GetType().GetField("fatigueValue", BindingFlags.Instance | BindingFlags.Public); if (field != null) { field.SetValue(component2, 0f); FatiguePlugin.Logger.LogInfo((object)"Cleared fatigue on skeleton transformation"); } } } } [HarmonyPatch(typeof(BarAffliction), "ChangeAffliction")] internal static class BarAffliction_ChangeAffliction_Patch { [HarmonyPrefix] private static bool Prefix(BarAffliction __instance, StaminaBar bar) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (((Object)((Component)__instance).gameObject).name != "FatigueCustomBar") { return true; } Character observedCharacter = Character.observedCharacter; if ((Object)observedCharacter == (Object)null) { return false; } FatigueTracker component = ((Component)observedCharacter).GetComponent(); if ((Object)component == (Object)null) { return false; } float fatigueValue = component.fatigueValue; __instance.size = bar.fullBar.sizeDelta.x * fatigueValue; if (fatigueValue > 0.01f) { if (__instance.size < bar.minAfflictionWidth) { __instance.size = bar.minAfflictionWidth; } ((Component)__instance).gameObject.SetActive(true); } else { ((Component)__instance).gameObject.SetActive(false); } if (FatiguePlugin.debugMode.Value && Mathf.Abs(fatigueValue - FatiguePlugin._lastLoggedFatigue) > 0.05f) { FatiguePlugin._lastLoggedFatigue = fatigueValue; FatiguePlugin.Logger.LogInfo((object)$"Fatigue bar updated: value={fatigueValue:F3}, size={__instance.size:F1}"); } return false; } } [HarmonyPatch(typeof(BarAffliction), "UpdateAffliction")] internal static class BarAffliction_UpdateAffliction_Patch { [HarmonyPrefix] private static bool Prefix(BarAffliction __instance, StaminaBar bar) { if (((Object)((Component)__instance).gameObject).name != "FatigueCustomBar") { return true; } __instance.width = Mathf.Lerp(__instance.width, __instance.size, Mathf.Min(Time.deltaTime * 10f, 0.1f)); return false; } } [HarmonyPatch(typeof(CharacterAfflictions))] [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class CharacterAfflictions_statusSum_Patch { [HarmonyPostfix] private static void Postfix(CharacterAfflictions __instance, ref float __result) { if (!((Object)(object)__instance.character == (Object)null)) { FatigueTracker component = ((Component)__instance.character).GetComponent(); if (!((Object)(object)component == (Object)null)) { __result += component.fatigueValue; } } } } [HarmonyPatch(typeof(Character), "AddExtraStamina")] internal static class Character_AddExtraStamina_Patch { [HarmonyPostfix] private static void Postfix(Character __instance, float add) { if (!__instance.IsLocal || add <= 0f) { return; } FatigueTracker component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null)) { float num = add / 5f; float fatigueValue = component.fatigueValue; component.fatigueValue = Mathf.Max(0f, component.fatigueValue - num); if (FatiguePlugin.debugMode.Value && num > 0.001f) { FatiguePlugin.Logger.LogInfo((object)($"AddExtraStamina ({add:F2}) reduced fatigue by {num:F3} " + $"(from {fatigueValue:F3} to {component.fatigueValue:F3})")); } } } } [HarmonyPatch(typeof(Character), "Awake")] internal static class Character_Awake_Patch { [HarmonyPostfix] private static void Postfix(Character __instance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if ((Object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } if ((Object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch(typeof(Character), "HasMeaningfulTempStatuses")] internal static class Character_HasMeaningfulTempStatuses_Patch { [HarmonyPostfix] private static void Postfix(Character __instance, ref bool __result) { FatigueTracker component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && !__result && component.fatigueValue > 0.05f) { __result = true; } } } [HarmonyPatch(typeof(Character), "UseStamina")] public static class Character_UseStamina_Patch { [HarmonyPostfix] public static void Postfix(Character __instance, float usage, bool useBonusStamina, ref bool __result) { if (__result && !(usage <= 0f) && !FatiguePlugin.IsFatigueDisabled()) { FatigueTracker component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.OnStaminaUsed(usage); } } } } public static class FatigueBarInjector { public static BarAffliction injectedBar; private static bool injectionFailed; public static void TryInjectBar() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Invalid comparison between Unknown and I4 //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Expected O, but got Unknown //IL_0201: Expected O, but got Unknown //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) if ((Object)injectedBar != (Object)null || injectionFailed) { return; } try { if ((Object)GUIManager.instance == (Object)null) { return; } StaminaBar bar = GUIManager.instance.bar; if ((Object)bar == (Object)null || bar.afflictions == null || bar.afflictions.Length == 0) { return; } BarAffliction val = null; BarAffliction[] afflictions = bar.afflictions; BarAffliction[] array = afflictions; foreach (BarAffliction val2 in array) { if (!((Object)val2 == (Object)null) && (int)val2.afflictionType == 4) { val = val2; break; } } if ((Object)val == (Object)null) { FatiguePlugin.Logger.LogWarning((object)"Crab template bar not found in StaminaBar.afflictions"); injectionFailed = true; return; } GameObject val3 = Object.Instantiate(((Component)val).gameObject, ((Component)val).transform.parent); ((Object)val3).name = "FatigueCustomBar"; BarAffliction component = val3.GetComponent(); if ((Object)component == (Object)null) { FatiguePlugin.Logger.LogError((object)"Cloned GameObject has no BarAffliction component"); Object.Destroy((Object)val3); injectionFailed = true; return; } if ((Object)FatiguePlugin.FatigueIconSprite != (Object)null && (Object)component.icon != (Object)null) { component.icon.sprite = FatiguePlugin.FatigueIconSprite; ((Graphic)component.icon).color = Color.white; } Image[] componentsInChildren = ((Component)component).GetComponentsInChildren(true); Image[] array2 = componentsInChildren; foreach (Image val4 in array2) { if (!((Object)component.icon != (Object)null) || !((Object)val4 == (Object)component.icon)) { Color fatigueColor = FatiguePlugin.FatigueColor; fatigueColor.a = ((Graphic)val4).color.a; ((Graphic)val4).color = fatigueColor; } } component.size = 0f; val3.SetActive(false); FieldInfo field = typeof(StaminaBar).GetField("afflictions", BindingFlags.Instance | BindingFlags.Public); if (field == null) { FatiguePlugin.Logger.LogError((object)"StaminaBar.afflictions field not found"); Object.Destroy((Object)val3); injectionFailed = true; return; } BarAffliction[] array3 = (BarAffliction[])field.GetValue(bar); BarAffliction[] array4 = (BarAffliction[])(object)new BarAffliction[array3.Length + 1]; Array.Copy(array3, array4, array3.Length); array4[array3.Length] = component; field.SetValue(bar, array4); injectedBar = component; if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)$"Independent bar injected. New array size: {array4.Length}"); } } catch (Exception arg) { FatiguePlugin.Logger.LogError((object)$"Bar injection failed: {arg}"); injectionFailed = true; } } } [BepInPlugin("jill920.fatiguefromstamina", "Fatigue Mod", "1.0.1")] public class FatiguePlugin : BaseUnityPlugin { public const string MOD_GUID = "jill920.fatiguefromstamina"; public static float _lastLoggedFatigue = -1f; public const string MOD_NAME = "Fatigue Mod"; public const string MOD_VERSION = "1.0.1"; public static FatiguePlugin Instance; public static ManualLogSource Logger; public static ConfigEntry debugMode; public const float FATIGUE_PER_STAMINA = 0.04f; public static readonly Color FatigueColor = new Color(1f, 0.5f, 0f, 1f); public static Sprite FatigueIconSprite; public const string CUSTOM_BAR_NAME = "FatigueCustomBar"; public static ConfigEntry fatigueReductionMultiplier; public static ConfigEntry healingItemsConfig; public static ConfigEntry extraStaminaFatigueReductionRatio; public const float RECOVERY_START_DELAY = 6f; public const float RECOVERY_RAMP_UP_DURATION = 32f; public const float RECOVERY_POWER = 1.8f; public const float MAX_RECOVERY_RATE = 0.012f; public const float MIN_RECOVERY_RATE = 0.001f; public const float PLAY_DEAD_RECOVERY_MULTIPLIER = 2f; private void Awake() { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown fatigueReductionMultiplier = ((BaseUnityPlugin)this).Config.Bind("Healing", "FatigueReductionMultiplier", 0.07f, "Multiplier for fatigue reduction when using healing items.\n1.0 = remove same amount as status (e.g., 0.35 fatigue for 0.35 status removed)\n0.5 = remove half\n0 = disable"); healingItemsConfig = ((BaseUnityPlugin)this).Config.Bind("Healing", "HealingItemIDs", "24", "Comma-separated list of item IDs that reduce fatigue when used"); extraStaminaFatigueReductionRatio = ((BaseUnityPlugin)this).Config.Bind("Recovery", "ExtraStaminaFatigueReductionRatio", 0.4f, "Amount of fatigue removed per unit of extra stamina given.\nExample: 0.2 = remove 0.2 fatigue for every 1.0 extra stamina"); Instance = this; Logger = ((BaseUnityPlugin)this).Logger; debugMode = ((BaseUnityPlugin)this).Config.Bind("Debug", "EnableDebugKeys", false, "..."); LoadIconSprite(); try { Harmony val = new Harmony("jill920.fatiguefromstamina"); val.PatchAll(Assembly.GetExecutingAssembly()); IEnumerable patchedMethods = val.GetPatchedMethods(); foreach (MethodBase item in patchedMethods) { Logger.LogInfo((object)("Patched: " + item.DeclaringType?.Name + "." + item.Name)); } Logger.LogInfo((object)"[Fatigue From Stamina 1.0.1] Loaded."); } catch (Exception arg) { Logger.LogError((object)$"Harmony patch failed: {arg}"); } } private static void LoadIconSprite() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0084: 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) try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FatigueMod.Assets.fatigue_icon.png"); if (stream == null) { Logger.LogWarning((object)"Embedded icon not found in resources!"); return; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; if (ImageConversion.LoadImage(val, memoryStream.ToArray())) { FatigueIconSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); Logger.LogInfo((object)$"Icon loaded: {((Texture)val).width}x{((Texture)val).height}"); } } catch (Exception arg) { Logger.LogError((object)$"LoadIconSprite failed: {arg}"); } } public static float GetEtcDamageMultiplier() { try { return Ascents.etcDamageMultiplier; } catch { return 1f; } } public static bool IsFatigueDisabled() { return GetEtcDamageMultiplier() <= 0.001f; } public static float GetGainMultiplier() { float etcDamageMultiplier = GetEtcDamageMultiplier(); if (etcDamageMultiplier <= 0.001f) { return 0f; } if (etcDamageMultiplier <= 0.6f) { return 1f; } if (etcDamageMultiplier <= 1.5f) { return 3f; } return 5f; } private void Update() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown Character localCharacter = Character.localCharacter; if ((Object)localCharacter != (Object)null && (Object)((Component)localCharacter).GetComponent() == (Object)null) { ((Component)localCharacter).gameObject.AddComponent(); if ((Object)((Component)localCharacter).GetComponent() == (Object)null) { ((Component)localCharacter).gameObject.AddComponent(); } Logger.LogInfo((object)"FatigueTracker attached (fallback)."); } FatigueBarInjector.TryInjectBar(); if (debugMode.Value && (Object)localCharacter != (Object)null && Input.GetKeyDown((KeyCode)288)) { FatigueTracker component = ((Component)localCharacter).GetComponent(); if ((Object)component != (Object)null) { component.fatigueValue = 0.5f; Logger.LogInfo((object)"[F7] Forced fatigue = 0.5"); } } } private void OnGUI() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_009c: 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_0117: 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_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0247: 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_0261: Expected O, but got Unknown if (!debugMode.Value) { return; } Character localCharacter = Character.localCharacter; if ((Object)localCharacter == (Object)null) { return; } FatigueTracker component = ((Component)localCharacter).GetComponent(); if (!((Object)component == (Object)null)) { GUIStyle val = new GUIStyle(); val.fontSize = 18; val.normal.textColor = Color.white; float num = 0f; Rigidbody playerRigidbody = FatigueTracker.GetPlayerRigidbody(localCharacter); if ((Object)playerRigidbody != (Object)null) { Vector3 linearVelocity = playerRigidbody.linearVelocity; num = ((Vector3)(ref linearVelocity)).magnitude; } float etcDamageMultiplier = GetEtcDamageMultiplier(); float gainMultiplier = GetGainMultiplier(); string text = (IsFatigueDisabled() ? "DISABLED" : $"x{gainMultiplier:F0}"); float statusSum = localCharacter.refs.afflictions.statusSum; float maxStamina = localCharacter.GetMaxStamina(); float currentStamina = localCharacter.data.currentStamina; GUI.Label(new Rect(20f, 100f, 700f, 30f), $"Speed: {num:F1} Fatigue: {component.fatigueValue:F3} etcMult: {etcDamageMultiplier:F2} ({text})", val); GUI.Label(new Rect(20f, 122f, 700f, 30f), $"Ground: {component.isGrounded} Climb: {component.isClimbing} Resting: {component.IsResting} RestTimer: {component.restTimer:F2}/{12f}", val); GUI.Label(new Rect(20f, 144f, 700f, 30f), $"FullyOut: {localCharacter.data.fullyPassedOut} Sum: {statusSum:F2} MaxStam: {maxStamina:F2} CurStam: {currentStamina:F2}", val); GUI.Label(new Rect(20f, 166f, 700f, 30f), "CustomBar: " + (((Object)FatigueBarInjector.injectedBar != (Object)null) ? "OK" : "missing"), val); } } } public class FatigueRpcReceiver : MonoBehaviourPun { [PunRPC] public void Fatigue_RPC_Sync(float value) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown FatigueTracker component = ((Component)this).GetComponent(); if ((Object)component != (Object)null && !component.character.IsLocal) { component.fatigueValue = Mathf.Clamp01(value); } } } public class FatigueTracker : MonoBehaviour { public Character character; public float fatigueValue; public float restTimer; public bool isGrounded; public bool isClimbing; private float _playDeadEndTime = 0f; private const float PLAY_DEAD_DURATION = 4f; private float lastActionStaminaUseTime; private float pendingStaminaFromActions; private float lastSyncSent; private const float SYNC_INTERVAL = 0.2f; public bool IsPlayingDead => Time.time < _playDeadEndTime; public bool IsResting { get { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (isClimbing) { return false; } if (character.data.isSprinting) { return false; } Rigidbody playerRigidbody = GetPlayerRigidbody(character); if ((Object)(object)playerRigidbody == (Object)null) { return false; } Vector3 linearVelocity = playerRigidbody.linearVelocity; return ((Vector3)(ref linearVelocity)).magnitude < 4.7f; } } private bool IsInvincible() { if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.afflictions == (Object)null) { return false; } Affliction val = default(Affliction); if (character.refs.afflictions.HasAfflictionType((AfflictionType)14, ref val)) { return true; } if (character.refs.afflictions.HasAfflictionType((AfflictionType)16, ref val)) { return true; } return false; } private bool IsSkeleton() { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return false; } if (character.data.isSkeleton && !character.data.dead && !character.data.fullyPassedOut) { return true; } return false; } private float GetCurrentRecoveryRate() { float num = Time.time - lastActionStaminaUseTime - 6f; if (num <= 0f) { return 0f; } float num2 = num / 32f; num2 = Mathf.Clamp01(num2); float num3 = Mathf.Pow(num2, 1.8f); float num4 = Mathf.Lerp(0.001f, 0.012f, num3); if (Time.time < _playDeadEndTime) { float num5 = num4 * 2f; if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)$"Play Dead active - recovery rate: {num4:F4} → {num5:F4} (x{2f:F1})"); } return num5; } return num4; } private void Awake() { character = ((Component)this).GetComponent(); } public static Rigidbody GetPlayerRigidbody(Character c) { if ((Object)(object)c == (Object)null) { return null; } return ((Component)c).GetComponentInChildren(); } public void OnStaminaUsed(float usage) { lastActionStaminaUseTime = Time.time; if (!IsInvincible()) { pendingStaminaFromActions += usage; } restTimer = 0f; } public void SetPlayingDead(bool playing) { if (playing) { _playDeadEndTime = Time.time + 4f; if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)$"Play Dead emote started - Fatigue recovery boosted for {4f} seconds"); } } } private void Update() { if ((Object)(object)character == (Object)null || !character.IsLocal) { return; } isGrounded = character.data.isGrounded; isClimbing = character.data.isClimbing || character.data.isRopeClimbing || character.data.isVineClimbing; if (IsResting) { restTimer += Time.deltaTime; } else { restTimer = 0f; } if (!IsInvincible() && !IsSkeleton() && pendingStaminaFromActions >= 0.025f) { int num = Mathf.FloorToInt(pendingStaminaFromActions / 0.025f); float num2 = (float)num * 0.025f * 0.04f; fatigueValue = Mathf.Min(1f, fatigueValue + num2); pendingStaminaFromActions -= (float)num * 0.025f; } if (!IsSkeleton() && fatigueValue > 0f && Time.time - lastActionStaminaUseTime > 6f) { float currentRecoveryRate = GetCurrentRecoveryRate(); if (currentRecoveryRate > 0f) { fatigueValue = Mathf.Max(0f, fatigueValue - currentRecoveryRate * Time.deltaTime); } } MaybeSendSync(); } private void MaybeSendSync() { if (Time.time - lastSyncSent < 0.2f) { return; } lastSyncSent = Time.time; PhotonView component = ((Component)character).GetComponent(); if ((Object)(object)component == (Object)null || !PhotonNetwork.InRoom) { return; } try { component.RPC("Fatigue_RPC_Sync", (RpcTarget)1, new object[1] { fatigueValue }); } catch (Exception ex) { FatiguePlugin.Logger.LogWarning((object)("RPC send failed: " + ex.Message)); } } } } namespace FatigueMod.Patches { [HarmonyPatch(typeof(CharacterAnimations), "PlayEmote")] internal static class CharacterAnimations_PlayEmote_Patch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public FatigueTracker tracker; public float delay; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(delay); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)tracker != (Object)null && tracker.IsPlayingDead) { tracker.SetPlayingDead(playing: false); if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Play Dead emote timeout - resetting recovery rate"); } } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPrefix] private static void Prefix(CharacterAnimations __instance, string emoteName) { Character component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsLocal) { FatigueTracker component2 = ((Component)component).GetComponent(); if (!((Object)(object)component2 == (Object)null) && emoteName == "A_Scout_Emote_PlayDead") { component2.SetPlayingDead(playing: true); ((MonoBehaviour)__instance).StartCoroutine(ResetPlayDeadAfterDelay(component2, 5f)); } } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator ResetPlayDeadAfterDelay(FatigueTracker tracker, float delay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0) { tracker = tracker, delay = delay }; } } [HarmonyPatch(typeof(CharacterAnimations), "Update")] internal static class CharacterAnimations_Update_Patch { [HarmonyPostfix] private static void Postfix(CharacterAnimations __instance) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Character component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsLocal) { return; } FatigueTracker component2 = ((Component)component).GetComponent(); if ((Object)(object)component2 == (Object)null) { return; } Animator animator = component.refs.animator; if (!((Object)(object)animator != (Object)null)) { return; } AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); bool flag = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("A_Scout_Emote_PlayDead"); if (component2.IsPlayingDead != flag) { component2.SetPlayingDead(flag); if (FatiguePlugin.debugMode.Value && flag) { FatiguePlugin.Logger.LogInfo((object)"Play Dead emote detected via animator"); } } } } [HarmonyPatch(typeof(EmoteWheel), "Choose")] internal static class EmoteWheel_Choose_Patch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public FatigueTracker tracker; public float delay; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(delay); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)tracker != (Object)null && tracker.IsPlayingDead) { tracker.SetPlayingDead(playing: false); if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Play Dead emote timeout - resetting recovery rate"); } } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPrefix] private static void Prefix(EmoteWheel __instance) { FieldInfo field = typeof(EmoteWheel).GetField("chosenEmoteData", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { return; } object? value = field.GetValue(__instance); EmoteWheelData val = (EmoteWheelData)((value is EmoteWheelData) ? value : null); if ((Object)(object)val == (Object)null || !(val.anim == "A_Scout_Emote_PlayDead")) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } FatigueTracker component = ((Component)localCharacter).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.SetPlayingDead(playing: true); ((MonoBehaviour)__instance).StartCoroutine(ResetPlayDeadAfterDelay(component, 5f)); if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Play Dead emote detected via EmoteWheel.Choose()"); } } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator ResetPlayDeadAfterDelay(FatigueTracker tracker, float delay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0) { tracker = tracker, delay = delay }; } } [HarmonyPatch(typeof(EmoteWheel), "OnDisable")] internal static class EmoteWheel_OnDisable_Patch { [HarmonyPrefix] private static void Prefix(EmoteWheel __instance) { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } FatigueTracker component = ((Component)localCharacter).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsPlayingDead) { component.SetPlayingDead(playing: false); if (FatiguePlugin.debugMode.Value) { FatiguePlugin.Logger.LogInfo((object)"Play Dead reset - emote wheel closed"); } } } } }