using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] namespace YoureTired; [BepInPlugin("com.marccunningham.yourtired", "You're Tired", "0.3.0")] [BepInProcess("valheim.exe")] public sealed class YoureTiredPlugin : BaseUnityPlugin { private enum HudResizeHandle { None, TopLeft, TopRight, BottomLeft, BottomRight } public const string PluginGuid = "com.marccunningham.yourtired"; public const string PluginName = "You're Tired"; public const string PluginVersion = "0.3.0"; private const float ReferenceScreenWidth = 2048f; private const float ReferenceScreenHeight = 1152f; private const float DefaultStackRightAtReference = 470f; private const float DefaultStackTopAtReference = 982f; private const float DefaultStackBottomAtReference = 1111f; private const float PreviousGapAtReference = 12f; private const float DefaultGapAtReference = 26f; private const float DefaultMeterWidthAtReference = 30f; private const float DefaultFrameThicknessAtReference = 2f; private const float MinimumTickInterval = 0.1f; private const float MaximumElapsedMultiplier = 3f; private const float MinimumRestingSpeed = 0.25f; private const float ActionActivityWindowSeconds = 0.8f; private const float CombatActivityWindowSeconds = 1.4f; private const float ZdoMissingEnergy = -1f; private const string ZdoEnergy = "YoureTired_Energy"; internal static YoureTiredPlugin Instance; private ConfigEntry modEnabled; private ConfigEntry diagnosticKey; private ConfigEntry sessionToggleKey; private ConfigEntry tickInterval; private ConfigEntry startingEnergy; private ConfigEntry saveInterval; private ConfigEntry verboseLogging; private ConfigEntry difficultyPreset; private ConfigEntry lastAppliedDifficultyPreset; private ConfigEntry minimumStaminaRegenMultiplier; private ConfigEntry showHud; private ConfigEntry hideHudWithCtrlF3; private ConfigEntry stackRightAtReference; private ConfigEntry stackTopAtReference; private ConfigEntry stackBottomAtReference; private ConfigEntry gapAtReference; private ConfigEntry meterWidthAtReference; private ConfigEntry frameThicknessAtReference; private ConfigEntry hudLayoutEditorKey; private ConfigEntry enableYawnAudio; private ConfigEntry yawnEnergyThreshold; private ConfigEntry yawnRearmMargin; private ConfigEntry yawnRepeatAtThresholdSeconds; private ConfigEntry yawnRepeatAtZeroSeconds; private ConfigEntry yawnRepeatRandomnessSeconds; private ConfigEntry yawnAudioVolume; private ConfigEntry yawnAudioFile; private ConfigEntry awakeDrainPerSecond; private ConfigEntry sprintDrainPerSecond; private ConfigEntry swimmingDrainPerSecond; private ConfigEntry actionDrainPerSecond; private ConfigEntry coldDrainPerSecond; private ConfigEntry freezingExtraDrainPerSecond; private ConfigEntry lowFoodDrainPerSecond; private ConfigEntry injuryDrainAtMaximumPerSecond; private ConfigEntry shelterRestRecoveryPerSecond; private ConfigEntry warmRecoveryPerSecond; private ConfigEntry foodRecoveryPerSecond; private ConfigEntry sleepingRecoveryPerSecond; private ConfigEntry enableTestKeys; private ConfigEntry testDrainKey; private ConfigEntry testRestoreKey; private Harmony harmony; private bool runtimeEnabled = true; private bool stateLoaded; private float energy; private float tickTimer; private float saveTimer; private float actionActivityRemaining; private float combatActivityRemaining; private FatigueSnapshot lastSnapshot; private float lastEnergyDeltaPerSecond; private bool hasSnapshot; private bool staminaRegenPatchInstalled; private bool runDrainPatchInstalled; private bool attackCostPatchInstalled; private bool jogSpeedPatchInstalled; private bool runSpeedPatchInstalled; private FieldInfo foodListField; private FieldInfo currentAttackField; private MethodInfo healthPercentageMethod; private Type injuryPluginType; private FieldInfo injuryPluginInstanceField; private MethodInfo injuryIsActiveForMethod; private MethodInfo injuryRunSpeedMultiplierMethod; private bool injuryReflectionResolved; private bool injuryReflectionUsable; private Texture2D pixelTexture; private GUIStyle headerStyle; private GUIStyle valueStyle; private GUIStyle stateStyle; private bool guiFailureLogged; private bool hudLayoutEditorActive; private bool hudLayoutDragging; private HudResizeHandle hudResizeHandle; private Vector2 hudEditStartMouse; private Rect hudEditStartRect; private CursorLockMode hudPreviousCursorLockMode; private bool hudPreviousCursorVisible; private bool hudCursorStateCaptured; private bool hudHiddenByCtrlF3; private string pluginDirectory; private Component yawnAudioSource; private object yawnAudioClip; private float lastYawnAudioTime = -99999f; private float nextYawnAudioTime; private void Awake() { Instance = this; pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired 0.3.0 loading."); BindConfig(); MigrateLegacyDefaultsToNormal(); ApplyDifficultyPresetIfChanged(); ((MonoBehaviour)this).StartCoroutine(LoadYawnAudio()); MigrateHudSpacingFromPrototype(); ResolveVanillaReflection(); CreatePixelTexture(); if (!FatigueRules.RunSelfTests(out var failure)) { ((BaseUnityPlugin)this).Logger.LogError((object)("You're Tired rule self-test failed: " + failure)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired rule self-test passed."); } InstallPatches(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired 0.3.0 loaded. F4 writes diagnostics; F1 safely toggles the system for this session. Yawns now repeat gently at low Energy, becoming more frequent only near exhaustion. HUD spacing moved right to " + 26f.ToString("0") + " px at 2048x1152.")); } private void OnDestroy() { RestoreHudCursor(); TrySaveCurrentPlayer(); if (harmony != null) { harmony.UnpatchSelf(); } DestroyYawnAudio(); if ((Object)(object)pixelTexture != (Object)null) { Object.Destroy((Object)(object)pixelTexture); pixelTexture = null; } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { //IL_0032: 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_0077: 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_01ca: Unknown result type (might be due to invalid IL or missing references) if (TryToggleHudWithCtrlF3()) { return; } if (hudLayoutEditorActive && (!showHud.Value || IsHudHiddenByCtrlF3())) { CloseHudLayoutEditorForHiddenHud(); } if (Input.GetKeyDown(hudLayoutEditorKey.Value) && showHud.Value && !IsHudHiddenByCtrlF3()) { ToggleHudLayoutEditor(); } if (Input.GetKeyDown(diagnosticKey.Value)) { WriteDiagnostic(); } if (Input.GetKeyDown(sessionToggleKey.Value)) { runtimeEnabled = !runtimeEnabled; ShowMessage("You're Tired: " + (runtimeEnabled ? "ON" : "OFF")); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired session toggle: " + (runtimeEnabled ? "ON" : "OFF") + ".")); } if (!runtimeEnabled || !modEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return; } EnsureStateLoaded(localPlayer); if (stateLoaded) { float deltaTime = Time.deltaTime; actionActivityRemaining = Mathf.Max(0f, actionActivityRemaining - deltaTime); combatActivityRemaining = Mathf.Max(0f, combatActivityRemaining - deltaTime); if (enableTestKeys.Value && Input.GetKeyDown(testDrainKey.Value)) { energy = FatigueRules.ClampEnergy(energy - 10f); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired test drain: Energy=" + energy.ToString("0") + "%.")); } if (enableTestKeys.Value && Input.GetKeyDown(testRestoreKey.Value)) { energy = FatigueRules.ClampEnergy(energy + 10f); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired test recovery: Energy=" + energy.ToString("0") + "%.")); } float num = Mathf.Max(0.1f, tickInterval.Value); tickTimer += deltaTime; saveTimer += deltaTime; if (tickTimer >= num) { float elapsedSeconds = Mathf.Min(tickTimer, num * 3f); tickTimer = 0f; TickEnergy(localPlayer, elapsedSeconds); } if (saveTimer >= Mathf.Max(1f, saveInterval.Value)) { saveTimer = 0f; SaveState(localPlayer); } } } private void TickEnergy(Player player, float elapsedSeconds) { try { FatigueSnapshot snapshot = ReadSnapshot(player); FatigueSettings settings = ReadSettings(); float num = FatigueRules.CalculateEnergyDelta(snapshot, elapsedSeconds, settings); energy = FatigueRules.ClampEnergy(energy + num); lastSnapshot = snapshot; lastEnergyDeltaPerSecond = ((elapsedSeconds > 0f) ? (num / elapsedSeconds) : 0f); hasSnapshot = true; UpdateYawnAudio(); if (verboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Energy=" + energy.ToString("0.0") + ", rate=" + lastEnergyDeltaPerSecond.ToString("0.000") + "/s, " + SnapshotToDiagnosticText(snapshot))); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("You're Tired energy update failed: " + ex)); } } private void UpdateYawnAudio() { if (enableYawnAudio == null || !enableYawnAudio.Value) { return; } float num = Mathf.Clamp(yawnEnergyThreshold.Value, 1f, 99f); float num2 = Mathf.Min(100f, num + Mathf.Clamp(yawnRearmMargin.Value, 1f, 50f)); if (energy >= num2) { lastYawnAudioTime = -99999f; nextYawnAudioTime = 0f; } else if (!(energy > num) && yawnAudioClip != null) { float unscaledTime = Time.unscaledTime; float currentYawnRepeatInterval = GetCurrentYawnRepeatInterval(num); if (lastYawnAudioTime > -1000f) { nextYawnAudioTime = Mathf.Min(nextYawnAudioTime, lastYawnAudioTime + currentYawnRepeatInterval); } if (!(unscaledTime < nextYawnAudioTime) && TryPlayYawnOneShot(yawnAudioClip, Mathf.Clamp01(yawnAudioVolume.Value))) { lastYawnAudioTime = unscaledTime; nextYawnAudioTime = unscaledTime + GetCurrentYawnRepeatInterval(num); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired: yawn played at Energy " + energy.ToString("0") + "%. Next possible yawn in " + (nextYawnAudioTime - unscaledTime).ToString("0") + " seconds.")); } } } private float GetCurrentYawnRepeatInterval(float threshold) { float num = Mathf.Clamp(yawnRepeatAtThresholdSeconds.Value, 45f, 600f); float num2 = Mathf.Clamp(yawnRepeatAtZeroSeconds.Value, 35f, num); float num3 = Mathf.Clamp01((threshold - Mathf.Clamp(energy, 0f, threshold)) / threshold); float num4 = Mathf.Lerp(num, num2, num3); float num5 = Mathf.Min(Mathf.Max(0f, yawnRepeatRandomnessSeconds.Value), num4 * 0.2f); return Mathf.Max(35f, num4 + Random.Range(0f - num5, num5)); } private IEnumerator LoadYawnAudio() { string path = ((yawnAudioFile != null && !string.IsNullOrWhiteSpace(yawnAudioFile.Value)) ? yawnAudioFile.Value.Trim() : "yawn.mp3"); string path2 = Path.Combine(pluginDirectory ?? string.Empty, "assets", "audio", path); yield return ((MonoBehaviour)this).StartCoroutine(LoadExternalMp3(path2, delegate(object clip) { yawnAudioClip = clip; }, "yawn")); } private IEnumerator LoadExternalMp3(string path, Action onLoaded, string label) { object request; object operation; if (string.IsNullOrEmpty(path) || !File.Exists(path)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: " + label + " audio file was not found at " + path)); } else if (TryBeginExternalMp3Request(path, label, out request, out operation)) { yield return operation; CompleteExternalMp3Request(request, onLoaded, label); } } private bool TryBeginExternalMp3Request(string path, string label, out object request, out object operation) { request = null; operation = null; try { Type type = FindAudioRuntimeType("UnityEngine.Networking.UnityWebRequestMultimedia"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: UnityWebRequestMultimedia was unavailable; could not load " + label + " audio.")); return false; } MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "GetAudioClip" && methodInfo2.GetParameters().Length == 2) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: no compatible GetAudioClip method was found for " + label + " audio.")); return false; } object obj = Enum.Parse(methodInfo.GetParameters()[1].ParameterType, "MPEG"); request = methodInfo.Invoke(null, new object[2] { new Uri(path).AbsoluteUri, obj }); if (request == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: could not create the request for " + label + " audio.")); return false; } MethodInfo method = request.GetType().GetMethod("SendWebRequest", Type.EmptyTypes); if (method == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: no SendWebRequest method was found for " + label + " audio.")); DisposeAudioRequest(request); request = null; return false; } operation = method.Invoke(request, null); if (operation == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: SendWebRequest returned no operation for " + label + " audio.")); DisposeAudioRequest(request); request = null; return false; } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: could not start loading " + label + " audio. " + ex.Message)); DisposeAudioRequest(request); request = null; operation = null; return false; } } private void CompleteExternalMp3Request(object request, Action onLoaded, string label) { try { if (request == null) { return; } PropertyInfo property = request.GetType().GetProperty("error", BindingFlags.Instance | BindingFlags.Public); string text = ((property != null) ? (property.GetValue(request, null) as string) : null); if (!string.IsNullOrEmpty(text)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: failed to load " + label + " audio. " + text)); return; } PropertyInfo property2 = request.GetType().GetProperty("downloadHandler", BindingFlags.Instance | BindingFlags.Public); object obj = ((property2 != null) ? property2.GetValue(request, null) : null); PropertyInfo propertyInfo = obj?.GetType().GetProperty("audioClip", BindingFlags.Instance | BindingFlags.Public); object obj2 = ((propertyInfo != null) ? propertyInfo.GetValue(obj, null) : null); if (obj2 == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: the " + label + " MP3 decoded without an AudioClip.")); return; } onLoaded?.Invoke(obj2); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired: loaded " + label + " audio from " + Path.GetFileName(GetAudioRequestUrl(request)) + ".")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: could not finish loading " + label + " audio. " + ex.Message)); } finally { DisposeAudioRequest(request); } } private bool TryPlayYawnOneShot(object clip, float volume) { if (clip == null) { return false; } Component val = EnsureYawnAudioSource(); if ((Object)(object)val == (Object)null) { return false; } try { MethodInfo[] methods = ((object)val).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "PlayOneShot" && methodInfo.GetParameters().Length == 2) { methodInfo.Invoke(val, new object[2] { clip, volume }); return true; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: failed to play yawn audio. " + ex.Message)); } return false; } private Component EnsureYawnAudioSource() { if ((Object)(object)yawnAudioSource != (Object)null) { return yawnAudioSource; } Type type = FindAudioRuntimeType("UnityEngine.AudioSource"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"You're Tired: Unity AudioSource was unavailable."); return null; } yawnAudioSource = ((Component)this).gameObject.GetComponent(type); if ((Object)(object)yawnAudioSource == (Object)null) { yawnAudioSource = ((Component)this).gameObject.AddComponent(type); } if ((Object)(object)yawnAudioSource != (Object)null) { SetAudioProperty(yawnAudioSource, "playOnAwake", false); SetAudioProperty(yawnAudioSource, "spatialBlend", 0f); SetAudioProperty(yawnAudioSource, "loop", false); } return yawnAudioSource; } private void DestroyYawnAudio() { object obj = yawnAudioClip; Object val = (Object)((obj is Object) ? obj : null); if (val != (Object)null) { Object.Destroy(val); } yawnAudioClip = null; yawnAudioSource = null; } private static void SetAudioProperty(Component source, string name, object value) { if (!((Object)(object)source == (Object)null)) { PropertyInfo property = ((object)source).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public); if (property != null && property.CanWrite) { property.SetValue(source, value, null); } } } private static Type FindAudioRuntimeType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(fullName, throwOnError: false); if (type != null) { return type; } } return null; } private static string GetAudioRequestUrl(object request) { if (request == null) { return string.Empty; } try { PropertyInfo property = request.GetType().GetProperty("url", BindingFlags.Instance | BindingFlags.Public); string text = ((property != null) ? (property.GetValue(request, null) as string) : null); return string.IsNullOrEmpty(text) ? string.Empty : text; } catch { return string.Empty; } } private static void DisposeAudioRequest(object request) { if (request is IDisposable disposable) { disposable.Dispose(); } } private FatigueSnapshot ReadSnapshot(Player player) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) SEMan sEMan = ((Character)player).GetSEMan(); bool flag = sEMan != null && sEMan.HaveStatusEffect(SEMan.s_statusEffectWet); bool flag2 = sEMan != null && sEMan.HaveStatusEffect(SEMan.s_statusEffectCampFire); bool flag3 = (Object)(object)EffectArea.IsPointInsideArea(((Component)player).transform.position, (Type)1, 0f) != (Object)null; bool flag4 = player.InShelter(); bool flag5 = ((Character)player).InBed(); bool flag6 = ((Character)player).IsSwimming(); bool isSprinting = ((Character)player).IsRunning() && !flag6; bool flag7 = EnvMan.IsCold(); bool flag8 = EnvMan.IsFreezing(); bool flag9 = IsCurrentAttackActive(player); FatigueSnapshot result = new FatigueSnapshot { IsSleeping = (flag5 && flag4 && !flag), IsSprinting = isSprinting, IsSwimming = flag6, IsActionActive = (actionActivityRemaining > 0f || combatActivityRemaining > 0f || flag9), IsSheltered = flag4 }; Vector3 velocity = ((Character)player).GetVelocity(); result.IsRestingStill = ((Vector3)(ref velocity)).magnitude <= 0.25f; result.IsWarm = flag2 || flag3; result.IsDry = !flag; result.IsCold = flag7 || flag8; result.IsFreezing = flag8; result.FoodCount = GetFoodCount(player); result.InjurySeverity = GetInjurySeverity(player); return result; } private FatigueSettings ReadSettings() { return new FatigueSettings { AwakeDrainPerSecond = Mathf.Max(0f, awakeDrainPerSecond.Value), SprintDrainPerSecond = Mathf.Max(0f, sprintDrainPerSecond.Value), SwimmingDrainPerSecond = Mathf.Max(0f, swimmingDrainPerSecond.Value), ActionDrainPerSecond = Mathf.Max(0f, actionDrainPerSecond.Value), ColdDrainPerSecond = Mathf.Max(0f, coldDrainPerSecond.Value), FreezingExtraDrainPerSecond = Mathf.Max(0f, freezingExtraDrainPerSecond.Value), LowFoodDrainPerSecond = Mathf.Max(0f, lowFoodDrainPerSecond.Value), InjuryDrainAtMaximumPerSecond = Mathf.Max(0f, injuryDrainAtMaximumPerSecond.Value), ShelterRestRecoveryPerSecond = Mathf.Max(0f, shelterRestRecoveryPerSecond.Value), WarmRecoveryPerSecond = Mathf.Max(0f, warmRecoveryPerSecond.Value), FoodRecoveryPerSecond = Mathf.Max(0f, foodRecoveryPerSecond.Value), SleepingRecoveryPerSecond = Mathf.Max(0f, sleepingRecoveryPerSecond.Value) }; } internal bool IsActiveFor(Player player) { if (runtimeEnabled && modEnabled.Value && stateLoaded && (Object)(object)player != (Object)null) { return (Object)(object)player == (Object)(object)Player.m_localPlayer; } return false; } internal float GetStaminaRegenMultiplier() { return Mathf.Max(FatigueRules.GetEffects(energy).StaminaRegenMultiplier, Mathf.Clamp(minimumStaminaRegenMultiplier.Value, 0.1f, 1f)); } internal float GetRunStaminaMultiplier() { return FatigueRules.GetEffects(energy).SprintStaminaMultiplier; } internal float GetAttackStaminaMultiplier() { return FatigueRules.GetEffects(energy).ToolStaminaMultiplier; } internal float GetMovementMultiplier() { return FatigueRules.GetEffects(energy).MovementMultiplier; } internal void NotifyStaminaUsingAction(Player player) { if (IsActiveFor(player)) { actionActivityRemaining = Mathf.Max(actionActivityRemaining, 0.8f); } } internal void NotifyDamageTaken(Player player) { if (IsActiveFor(player)) { combatActivityRemaining = Mathf.Max(combatActivityRemaining, 1.4f); } } private void EnsureStateLoaded(Player player) { if (!stateLoaded) { ZDO ownedPlayerZdo = GetOwnedPlayerZdo(player); if (ownedPlayerZdo != null) { float num = ownedPlayerZdo.GetFloat("YoureTired_Energy", -1f); energy = ((num < 0f) ? FatigueRules.ClampEnergy(startingEnergy.Value) : FatigueRules.ClampEnergy(num)); stateLoaded = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired loaded Energy=" + energy.ToString("0.0") + "%.")); } } } private void SaveState(Player player) { if (stateLoaded && !((Object)(object)player == (Object)null)) { ZDO ownedPlayerZdo = GetOwnedPlayerZdo(player); if (ownedPlayerZdo != null) { ownedPlayerZdo.Set("YoureTired_Energy", FatigueRules.ClampEnergy(energy)); } } } private void TrySaveCurrentPlayer() { try { SaveState(Player.m_localPlayer); } catch { } } private static ZDO GetOwnedPlayerZdo(Player player) { ZNetView component = ((Component)player).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return null; } return component.GetZDO(); } private int GetFoodCount(Player player) { if (foodListField == null || (Object)(object)player == (Object)null) { return 0; } try { return (foodListField.GetValue(player) is ICollection collection) ? collection.Count : 0; } catch { return 0; } } private bool IsCurrentAttackActive(Player player) { if (currentAttackField == null || (Object)(object)player == (Object)null) { return false; } try { return currentAttackField.GetValue(player) != null; } catch { return false; } } private float GetInjurySeverity(Player player) { float num = 0f; if (healthPercentageMethod != null) { try { float num2 = Mathf.Clamp01((float)healthPercentageMethod.Invoke(player, null)); num = (1f - num2) * 35f; } catch { num = 0f; } } ResolveInjuryReflection(); if (!injuryReflectionUsable) { return num; } try { object value = injuryPluginInstanceField.GetValue(null); if (value == null || !(bool)injuryIsActiveForMethod.Invoke(value, new object[1] { player })) { return num; } float num3 = Mathf.Clamp((float)injuryRunSpeedMultiplierMethod.Invoke(value, null), 0.3f, 1f); float num4 = (1f - num3) * 100f; return Mathf.Max(num, num4); } catch { injuryReflectionUsable = false; return num; } } private void ResolveVanillaReflection() { foodListField = FindInstanceField(typeof(Player), "m_foods"); currentAttackField = FindInstanceField(typeof(Player), "m_currentAttack"); healthPercentageMethod = FindInstanceMethod(typeof(Player), "GetHealthPercentage", Type.EmptyTypes); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired reflection: foods=" + (foodListField != null) + ", currentAttack=" + (currentAttackField != null) + ", healthPercent=" + (healthPercentageMethod != null) + ".")); } private void ResolveInjuryReflection() { if (injuryReflectionResolved) { return; } injuryReflectionResolved = true; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("YoureInjured.YoureInjuredPlugin"); if (type != null) { injuryPluginType = type; break; } } if (!(injuryPluginType == null)) { injuryPluginInstanceField = injuryPluginType.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); injuryIsActiveForMethod = injuryPluginType.GetMethod("IsActiveFor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Player) }, null); injuryRunSpeedMultiplierMethod = injuryPluginType.GetMethod("GetRunSpeedMultiplier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); injuryReflectionUsable = injuryPluginInstanceField != null && injuryIsActiveForMethod != null && injuryRunSpeedMultiplierMethod != null; ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired injury compatibility=" + injuryReflectionUsable + ".")); } } private void InstallPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown harmony = new Harmony("com.marccunningham.yourtired"); staminaRegenPatchInstalled = TryPatch(FindInstanceMethod(typeof(SEMan), "ModifyStaminaRegen", null), null, new HarmonyMethod(typeof(YoureTiredPatches), "SEManModifyStaminaRegenPostfix", (Type[])null), "SEMan.ModifyStaminaRegen(...)"); runDrainPatchInstalled = TryPatch(FindInstanceMethod(typeof(SEMan), "ModifyRunStaminaDrain", null), null, new HarmonyMethod(typeof(YoureTiredPatches), "SEManModifyRunStaminaDrainPostfix", (Type[])null), "SEMan.ModifyRunStaminaDrain(...)"); attackCostPatchInstalled = TryPatch(FindInstanceMethod(typeof(SEMan), "ModifyAttackStaminaUsage", null), null, new HarmonyMethod(typeof(YoureTiredPatches), "SEManModifyAttackStaminaUsagePostfix", (Type[])null), "SEMan.ModifyAttackStaminaUsage(...)"); jogSpeedPatchInstalled = TryPatch(FindInstanceMethod(typeof(Character), "GetJogSpeedFactor", Type.EmptyTypes), null, new HarmonyMethod(typeof(YoureTiredPatches), "CharacterGetJogSpeedFactorPostfix", (Type[])null), "Character.GetJogSpeedFactor()"); runSpeedPatchInstalled = TryPatch(FindInstanceMethod(typeof(Character), "GetRunSpeedFactor", Type.EmptyTypes), null, new HarmonyMethod(typeof(YoureTiredPatches), "CharacterGetRunSpeedFactorPostfix", (Type[])null), "Character.GetRunSpeedFactor()"); TryPatch(FindInstanceMethod(typeof(Character), "ApplyDamage", null), null, new HarmonyMethod(typeof(YoureTiredPatches), "CharacterApplyDamagePostfix", (Type[])null), "Character.ApplyDamage(...)"); } private bool TryPatch(MethodInfo target, HarmonyMethod prefix, HarmonyMethod postfix, string targetName) { if (target == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("You're Tired: could not find " + targetName + ". That one feature is disabled.")); return false; } try { harmony.Patch((MethodBase)target, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired: patched " + targetName + ".")); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("You're Tired: failed to patch " + targetName + ": " + ex)); return false; } } private static FieldInfo FindInstanceField(Type type, string fieldName) { Type type2 = type; while (type2 != null) { FieldInfo field = type2.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type2 = type2.BaseType; } return null; } private static MethodInfo FindInstanceMethod(Type type, string methodName, Type[] parameterTypes) { if (parameterTypes != null) { return type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { if (methods[i].Name == methodName) { return methods[i]; } } return null; } private void BindConfig() { FatigueSettings fatigueSettings = FatigueRules.CreateBeginnerFriendlyDefaults(); modEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Set false to disable the entire tiredness system, including Energy changes and fatigue penalties."); diagnosticKey = ((BaseUnityPlugin)this).Config.Bind("General", "DiagnosticKey", (KeyCode)285, "Writes current Energy, environment and patch information to BepInEx/LogOutput.log."); sessionToggleKey = ((BaseUnityPlugin)this).Config.Bind("General", "SessionToggleKey", (KeyCode)282, "Emergency in-game toggle for You're Tired. This avoids the F2-F10 keys already used by your other mods."); tickInterval = ((BaseUnityPlugin)this).Config.Bind("General", "TickIntervalSeconds", 0.5f, "How often Energy is recalculated. Lower is smoother but uses slightly more CPU."); startingEnergy = ((BaseUnityPlugin)this).Config.Bind("General", "StartingEnergyForNewCharacters", 100f, "Energy used only when this character has no saved You're Tired state yet."); saveInterval = ((BaseUnityPlugin)this).Config.Bind("General", "SaveIntervalSeconds", 5f, "How often Energy is saved to the owned player data."); verboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", false, "Writes one fatigue line every tick. Leave off during normal play."); difficultyPreset = ((BaseUnityPlugin)this).Config.Bind("Difficulty", "Preset", "Normal", "Choose Easy, Normal, Hard, or Extreme. Change this setting, then restart Valheim to apply that mod's matching survival balance."); lastAppliedDifficultyPreset = ((BaseUnityPlugin)this).Config.Bind("Difficulty", "LastAppliedPreset", "Normal", "Internal preset tracking. Leave this alone; it records the last difficulty preset applied."); minimumStaminaRegenMultiplier = ((BaseUnityPlugin)this).Config.Bind("Difficulty", "MinimumStaminaRegenMultiplier", 0.85f, "Safety floor for this mod's own Energy penalty. It prevents exhaustion from reducing stamina recovery into a no-escape state when other Scavengers mods are also active."); showHud = ((BaseUnityPlugin)this).Config.Bind("HUD", "ShowTiredMeter", true, "Set false to hide only the Tired HUD while the tiredness system continues running."); hideHudWithCtrlF3 = ((BaseUnityPlugin)this).Config.Bind("HUD", "HideHudWithCtrlF3", true, "When enabled, Ctrl+F3 hides or restores this mod's HUD along with Valheim's HUD toggle. The tiredness system keeps running."); stackRightAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "ExistingStackRightAt2048x1152", 470f, "Right edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot."); stackTopAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "ExistingStackTopAt2048x1152", 982f, "Top edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot."); stackBottomAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "ExistingStackBottomAt2048x1152", 1111f, "Bottom edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot."); gapAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "GapRightOfExistingStackAt2048x1152", 26f, "Clear horizontal gap before the Tired meter. V0.2 default is 26 pixels at 2048x1152, moved right from V0.1's 12 pixels."); meterWidthAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "MeterWidthAt2048x1152", 30f, "Width of the Tired meter including its frame."); frameThicknessAtReference = ((BaseUnityPlugin)this).Config.Bind("HUD Layout", "FrameThicknessAt2048x1152", 2f, "Frame thickness of the Tired meter."); hudLayoutEditorKey = ((BaseUnityPlugin)this).Config.Bind("HUD Layout Editor", "ToggleTiredLayoutEditorKey", (KeyCode)108, "Press L to enter or leave Tired HUD layout mode. In layout mode, drag the meter and drag a gold corner handle to resize it."); enableYawnAudio = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "Enabled", true, "Plays the supplied yawn sound when Energy first reaches the tired threshold, then occasionally while Energy remains low."); yawnEnergyThreshold = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "EnergyThreshold", 45f, "Energy percentage at or below which the first yawn plays. 45 means the player yawns when Energy first reaches 45 or lower."); yawnRearmMargin = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "RearmMarginAboveThreshold", 5f, "Energy that must be regained above the yawn threshold before the next tired spell can play an immediate yawn."); yawnRepeatAtThresholdSeconds = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "RepeatSecondsAtThreshold", 150f, "Approximate seconds between yawns while Energy stays near the tired threshold. Higher values are less frequent."); yawnRepeatAtZeroSeconds = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "RepeatSecondsAtZeroEnergy", 60f, "Approximate seconds between yawns at 0 Energy. The interval smoothly shortens as Energy falls from the threshold to zero."); yawnRepeatRandomnessSeconds = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "RepeatRandomnessSeconds", 12f, "Small random variation around repeat timing so yawns do not sound mechanical. This is safety-capped to avoid annoyance."); yawnAudioVolume = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "Volume", 0.62f, "Volume for the externally loaded yawn sound."); yawnAudioFile = ((BaseUnityPlugin)this).Config.Bind("Yawn Audio", "YawnFile", "yawn.mp3", "MP3 filename expected in assets/audio beside the You're Tired DLL."); awakeDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "AwakeDrainPerSecond", fatigueSettings.AwakeDrainPerSecond, "Gentle Energy drain while awake. It is intentionally low so ordinary exploration is fair."); sprintDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "SprintDrainPerSecond", fatigueSettings.SprintDrainPerSecond, "Extra Energy drain while sprinting."); swimmingDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "SwimmingDrainPerSecond", fatigueSettings.SwimmingDrainPerSecond, "Extra Energy drain while swimming. Swimming replaces sprint drain rather than stacking with it."); actionDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "CombatAndWorkDrainPerSecond", fatigueSettings.ActionDrainPerSecond, "Extra Energy drain during stamina-using attacks, including combat, chopping and mining."); coldDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "ColdDrainPerSecond", fatigueSettings.ColdDrainPerSecond, "Extra Energy drain during cold conditions."); freezingExtraDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "FreezingExtraDrainPerSecond", fatigueSettings.FreezingExtraDrainPerSecond, "Additional Energy drain during freezing conditions on top of ordinary cold."); lowFoodDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "LowFoodDrainPerSecond", fatigueSettings.LowFoodDrainPerSecond, "Extra drain when fewer than two Valheim food slots are active."); injuryDrainAtMaximumPerSecond = ((BaseUnityPlugin)this).Config.Bind("Drain", "InjuryDrainAtMaximumPerSecond", fatigueSettings.InjuryDrainAtMaximumPerSecond, "Maximum extra drain from severe injury/low health. It scales gradually and never deals damage."); shelterRestRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind("Recovery", "DryShelterRestRecoveryPerSecond", fatigueSettings.ShelterRestRecoveryPerSecond, "Energy restored while dry, sheltered and nearly still."); warmRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind("Recovery", "WarmthRecoveryPerSecond", fatigueSettings.WarmRecoveryPerSecond, "Extra Energy restored while dry near a campfire or heat area."); foodRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind("Recovery", "FoodRecoveryPerSecond", fatigueSettings.FoodRecoveryPerSecond, "Extra Energy restored with two or more active Valheim food slots."); sleepingRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind("Recovery", "SleepingRecoveryPerSecond", fatigueSettings.SleepingRecoveryPerSecond, "Strong Energy restoration while in a dry sheltered bed."); enableTestKeys = ((BaseUnityPlugin)this).Config.Bind("Debug", "EnableTestKeys", false, "Enables F5 to remove 10 Energy and F11 to restore 10 Energy. Keep false for normal play."); testDrainKey = ((BaseUnityPlugin)this).Config.Bind("Debug", "TestDrainKey", (KeyCode)286, "Debug-only key that removes 10 Energy when EnableTestKeys is true."); testRestoreKey = ((BaseUnityPlugin)this).Config.Bind("Debug", "TestRestoreKey", (KeyCode)292, "Debug-only key that restores 10 Energy when EnableTestKeys is true."); } private void MigrateLegacyDefaultsToNormal() { if (Mathf.Abs(awakeDrainPerSecond.Value - 0.018f) < 0.0001f && Mathf.Abs(sprintDrainPerSecond.Value - 0.065f) < 0.0001f && Mathf.Abs(swimmingDrainPerSecond.Value - 0.095f) < 0.0001f && Mathf.Abs(actionDrainPerSecond.Value - 0.038f) < 0.0001f && Mathf.Abs(coldDrainPerSecond.Value - 0.022f) < 0.0001f && Mathf.Abs(freezingExtraDrainPerSecond.Value - 0.042f) < 0.0001f && Mathf.Abs(lowFoodDrainPerSecond.Value - 0.02f) < 0.0001f && Mathf.Abs(injuryDrainAtMaximumPerSecond.Value - 0.06f) < 0.0001f) { ApplyDifficultyPreset("Normal"); ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired: migrated untouched legacy fatigue defaults to the balanced Normal preset."); } } private void ApplyDifficultyPresetIfChanged() { string text = NormalizeDifficultyPreset(difficultyPreset.Value); if (!string.Equals(difficultyPreset.Value, text, StringComparison.OrdinalIgnoreCase)) { difficultyPreset.Value = text; } if (!string.Equals(lastAppliedDifficultyPreset.Value, text, StringComparison.OrdinalIgnoreCase)) { ApplyDifficultyPreset(text); lastAppliedDifficultyPreset.Value = text; ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired: applied " + text + " difficulty preset.")); } } private static string NormalizeDifficultyPreset(string value) { if (string.Equals(value, "Easy", StringComparison.OrdinalIgnoreCase)) { return "Easy"; } if (string.Equals(value, "Hard", StringComparison.OrdinalIgnoreCase)) { return "Hard"; } if (string.Equals(value, "Extreme", StringComparison.OrdinalIgnoreCase)) { return "Extreme"; } return "Normal"; } private void ApplyDifficultyPreset(string preset) { switch (preset) { case "Easy": awakeDrainPerSecond.Value = 0.01f; sprintDrainPerSecond.Value = 0.035f; swimmingDrainPerSecond.Value = 0.055f; actionDrainPerSecond.Value = 0.02f; coldDrainPerSecond.Value = 0.01f; freezingExtraDrainPerSecond.Value = 0.018f; lowFoodDrainPerSecond.Value = 0.01f; injuryDrainAtMaximumPerSecond.Value = 0.025f; shelterRestRecoveryPerSecond.Value = 0.1f; warmRecoveryPerSecond.Value = 0.07f; foodRecoveryPerSecond.Value = 0.035f; sleepingRecoveryPerSecond.Value = 0.42f; minimumStaminaRegenMultiplier.Value = 0.92f; break; case "Hard": awakeDrainPerSecond.Value = 0.02f; sprintDrainPerSecond.Value = 0.075f; swimmingDrainPerSecond.Value = 0.11f; actionDrainPerSecond.Value = 0.045f; coldDrainPerSecond.Value = 0.028f; freezingExtraDrainPerSecond.Value = 0.05f; lowFoodDrainPerSecond.Value = 0.025f; injuryDrainAtMaximumPerSecond.Value = 0.075f; shelterRestRecoveryPerSecond.Value = 0.06f; warmRecoveryPerSecond.Value = 0.035f; foodRecoveryPerSecond.Value = 0.014f; sleepingRecoveryPerSecond.Value = 0.29f; minimumStaminaRegenMultiplier.Value = 0.78f; break; case "Extreme": awakeDrainPerSecond.Value = 0.028f; sprintDrainPerSecond.Value = 0.1f; swimmingDrainPerSecond.Value = 0.14f; actionDrainPerSecond.Value = 0.06f; coldDrainPerSecond.Value = 0.04f; freezingExtraDrainPerSecond.Value = 0.07f; lowFoodDrainPerSecond.Value = 0.035f; injuryDrainAtMaximumPerSecond.Value = 0.1f; shelterRestRecoveryPerSecond.Value = 0.045f; warmRecoveryPerSecond.Value = 0.025f; foodRecoveryPerSecond.Value = 0.01f; sleepingRecoveryPerSecond.Value = 0.22f; minimumStaminaRegenMultiplier.Value = 0.72f; break; default: awakeDrainPerSecond.Value = 0.014f; sprintDrainPerSecond.Value = 0.05f; swimmingDrainPerSecond.Value = 0.075f; actionDrainPerSecond.Value = 0.03f; coldDrainPerSecond.Value = 0.015f; freezingExtraDrainPerSecond.Value = 0.028f; lowFoodDrainPerSecond.Value = 0.014f; injuryDrainAtMaximumPerSecond.Value = 0.04f; shelterRestRecoveryPerSecond.Value = 0.08f; warmRecoveryPerSecond.Value = 0.055f; foodRecoveryPerSecond.Value = 0.025f; sleepingRecoveryPerSecond.Value = 0.36f; minimumStaminaRegenMultiplier.Value = 0.85f; break; } } private void MigrateHudSpacingFromPrototype() { if (Mathf.Abs(gapAtReference.Value - 12f) < 0.01f) { gapAtReference.Value = 26f; ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired: migrated V0.1 HUD gap from 12 to 26 pixels."); } } private void CreatePixelTexture() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) pixelTexture = new Texture2D(1, 1, (TextureFormat)4, false); ((Object)pixelTexture).name = "YoureTired_HudPixel"; pixelTexture.SetPixel(0, 0, Color.white); pixelTexture.Apply(); ((Object)pixelTexture).hideFlags = (HideFlags)61; } private void OnGUI() { if (!runtimeEnabled || !modEnabled.Value || !showHud.Value || IsHudHiddenByCtrlF3() || (Object)(object)pixelTexture == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } try { EnsureGuiStyles(); int depth = GUI.depth; GUI.depth = -1000; DrawEnergyMeter(); GUI.depth = depth; } catch (Exception ex) { if (!guiFailureLogged) { guiFailureLogged = true; ((BaseUnityPlugin)this).Logger.LogError((object)("You're Tired HUD draw failed: " + ex)); } } } private void EnsureGuiStyles() { //IL_0026: 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_0044: Expected O, but got Unknown //IL_0088: 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_009e: Expected O, but got Unknown //IL_00ce: 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_00e4: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) if (headerStyle == null || valueStyle == null || stateStyle == null) { GUIStyle val = (GUIStyle)(((Object)(object)GUI.skin != (Object)null) ? ((object)GUI.skin.label) : ((object)new GUIStyle())); headerStyle = new GUIStyle(val); headerStyle.alignment = (TextAnchor)4; headerStyle.fontStyle = (FontStyle)1; headerStyle.fontSize = 11; headerStyle.normal.textColor = new Color(0.96f, 0.79f, 0.35f, 1f); valueStyle = new GUIStyle(val); valueStyle.alignment = (TextAnchor)4; valueStyle.fontStyle = (FontStyle)1; valueStyle.fontSize = 10; valueStyle.normal.textColor = Color.white; stateStyle = new GUIStyle(val); stateStyle.alignment = (TextAnchor)4; stateStyle.fontStyle = (FontStyle)0; stateStyle.fontSize = 9; stateStyle.normal.textColor = new Color(0.9f, 0.9f, 0.84f, 0.95f); } } private void DrawEnergyMeter() { //IL_008e: 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_0099: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0353: 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_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) float hudScale = GetHudScale(); float num = (stackRightAtReference.Value + gapAtReference.Value) * hudScale; float num2 = stackTopAtReference.Value * hudScale; float num3 = Mathf.Max(70f * hudScale, (stackBottomAtReference.Value - stackTopAtReference.Value) * hudScale); float num4 = Mathf.Max(22f * hudScale, meterWidthAtReference.Value * hudScale); float num5 = Mathf.Clamp(frameThicknessAtReference.Value * hudScale, 1f, 6f); Rect val = ClampMeterRect(new Rect(num, num2, num4, num3), hudScale); float num6 = 129f * hudScale; float num7 = 30f * hudScale; float num8 = Mathf.Clamp(Mathf.Min(((Rect)(ref val)).width / num7, ((Rect)(ref val)).height / num6), 0.55f, 3f); headerStyle.fontSize = Mathf.Max(8, Mathf.RoundToInt(11f * num8)); valueStyle.fontSize = Mathf.Max(8, Mathf.RoundToInt(10f * num8)); stateStyle.fontSize = Mathf.Max(7, Mathf.RoundToInt(9f * num8)); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x - 10f * hudScale, ((Rect)(ref val)).y - 17f * hudScale, ((Rect)(ref val)).width + 20f * hudScale, 15f * hudScale); Rect val3 = new Rect(((Rect)(ref val)).x - 13f * hudScale, ((Rect)(ref val)).yMax + 1f * hudScale, ((Rect)(ref val)).width + 26f * hudScale, 14f * hudScale); DrawRect(val, new Color(0.025f, 0.04f, 0.03f, 0.88f)); Color colour = default(Color); ((Color)(ref colour))..ctor(0.45f, 0.35f, 0.16f, 0.92f); DrawRect(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, num5), colour); DrawRect(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).yMax - num5, ((Rect)(ref val)).width, num5), colour); DrawRect(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, num5, ((Rect)(ref val)).height), colour); DrawRect(new Rect(((Rect)(ref val)).xMax - num5, ((Rect)(ref val)).y, num5, ((Rect)(ref val)).height), colour); float num9 = ((Rect)(ref val)).x + num5 + 2f * hudScale; float num10 = ((Rect)(ref val)).y + num5 + 2f * hudScale; float num11 = Mathf.Max(1f, ((Rect)(ref val)).width - 2f * num5 - 4f * hudScale); float num12 = Mathf.Max(1f, ((Rect)(ref val)).height - 2f * num5 - 4f * hudScale); Rect rect = default(Rect); ((Rect)(ref rect))..ctor(num9, num10, num11, num12); DrawRect(rect, new Color(0f, 0f, 0f, 0.72f)); float num13 = energy / 100f; float num14 = ((Rect)(ref rect)).height * num13; Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - num14, ((Rect)(ref rect)).width, num14); DrawRect(rect2, GetEnergyColour(energy)); GUI.Label(val2, "TIRED", headerStyle); GUI.Label(val, energy.ToString("0"), valueStyle); GUI.Label(val3, FatigueRules.GetStage(energy).ToString().ToUpperInvariant(), stateStyle); DrawHudLayoutEditor(val, hudScale); } private void DrawRect(Rect rect, Color colour) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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) Color color = GUI.color; GUI.color = colour; GUI.DrawTexture(rect, (Texture)(object)pixelTexture); GUI.color = color; } private static float GetHudScale() { float num = Mathf.Min((float)Screen.width / 2048f, (float)Screen.height / 1152f); return Mathf.Max(0.25f, num); } private static Rect ClampMeterRect(Rect rect, float scale) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) float num = 22f * scale; float num2 = 70f * scale; float num3 = Mathf.Clamp(((Rect)(ref rect)).width, num, Mathf.Max(num, (float)Screen.width - 4f)); float num4 = Mathf.Clamp(((Rect)(ref rect)).height, num2, Mathf.Max(num2, (float)Screen.height - 4f)); float num5 = Mathf.Clamp(((Rect)(ref rect)).x, 0f, Mathf.Max(0f, (float)Screen.width - num3)); float num6 = Mathf.Clamp(((Rect)(ref rect)).y, 0f, Mathf.Max(0f, (float)Screen.height - num4)); return new Rect(num5, num6, num3, num4); } private bool IsHudHiddenByCtrlF3() { if (hideHudWithCtrlF3 != null && hideHudWithCtrlF3.Value) { return hudHiddenByCtrlF3; } return false; } private bool TryToggleHudWithCtrlF3() { if (!IsCtrlF3Pressed()) { return false; } if (hideHudWithCtrlF3 == null || !hideHudWithCtrlF3.Value) { return true; } hudHiddenByCtrlF3 = !hudHiddenByCtrlF3; if (hudHiddenByCtrlF3) { CloseHudLayoutEditorForHiddenHud(); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired: Ctrl+F3 HUD " + (hudHiddenByCtrlF3 ? "hidden." : "restored."))); return true; } private static bool IsCtrlF3Pressed() { if (Input.GetKeyDown((KeyCode)284)) { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } return false; } private void CloseHudLayoutEditorForHiddenHud() { if (hudLayoutEditorActive) { hudLayoutEditorActive = false; hudLayoutDragging = false; hudResizeHandle = HudResizeHandle.None; RestoreHudCursor(); ((BaseUnityPlugin)this).Config.Save(); } } private void ToggleHudLayoutEditor() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) hudLayoutEditorActive = !hudLayoutEditorActive; hudLayoutDragging = false; hudResizeHandle = HudResizeHandle.None; if (hudLayoutEditorActive) { hudPreviousCursorLockMode = Cursor.lockState; hudPreviousCursorVisible = Cursor.visible; hudCursorStateCaptured = true; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; ShowMessage("Tired layout mode: drag meter or a gold corner. Press L again to save."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired: Tired HUD layout editor enabled."); } else { RestoreHudCursor(); ((BaseUnityPlugin)this).Config.Save(); ShowMessage("Tired layout saved."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"You're Tired: Tired HUD layout editor saved and closed."); } } private void RestoreHudCursor() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (hudCursorStateCaptured) { Cursor.lockState = hudPreviousCursorLockMode; Cursor.visible = hudPreviousCursorVisible; hudCursorStateCaptured = false; } } private void DrawHudLayoutEditor(Rect meterRect, float scale) { //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0198: 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_01cf: 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_0027: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Invalid comparison between Unknown and I4 //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Invalid comparison between Unknown and I4 //IL_0038: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_013e: 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) if (!hudLayoutEditorActive) { return; } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; Event current = Event.current; if (current != null) { Vector2 mousePosition = current.mousePosition; if ((int)current.type == 0 && current.button == 0) { HudResizeHandle resizeHandle = GetResizeHandle(meterRect, mousePosition); if (resizeHandle != HudResizeHandle.None) { hudResizeHandle = resizeHandle; hudLayoutDragging = false; hudEditStartMouse = mousePosition; hudEditStartRect = meterRect; current.Use(); } else if (((Rect)(ref meterRect)).Contains(mousePosition)) { hudLayoutDragging = true; hudResizeHandle = HudResizeHandle.None; hudEditStartMouse = mousePosition; hudEditStartRect = meterRect; current.Use(); } } else if ((int)current.type == 3 && current.button == 0 && (hudLayoutDragging || hudResizeHandle != HudResizeHandle.None)) { Vector2 val = mousePosition - hudEditStartMouse; Rect rect = (Rect)(hudLayoutDragging ? new Rect(((Rect)(ref hudEditStartRect)).x + val.x, ((Rect)(ref hudEditStartRect)).y + val.y, ((Rect)(ref hudEditStartRect)).width, ((Rect)(ref hudEditStartRect)).height) : ResizeRect(hudEditStartRect, val, hudResizeHandle, 22f * scale, 70f * scale)); StoreMeterRect(rect, scale); current.Use(); } else if ((int)current.type == 1 && current.button == 0 && (hudLayoutDragging || hudResizeHandle != HudResizeHandle.None)) { hudLayoutDragging = false; hudResizeHandle = HudResizeHandle.None; ((BaseUnityPlugin)this).Config.Save(); current.Use(); } } DrawEditorOutline(meterRect); Color color = GUI.color; GUI.color = new Color(1f, 0.78f, 0.22f, 1f); GUI.Label(new Rect(12f, 60f, 820f, 24f), "L — Tired layout mode: drag meter; drag any gold corner to resize; press L again to save."); GUI.color = color; } private void StoreMeterRect(Rect rect, float scale) { //IL_0000: 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_0007: Unknown result type (might be due to invalid IL or missing references) Rect val = ClampMeterRect(rect, scale); float num = Mathf.Max(0.01f, scale); stackRightAtReference.Value = Mathf.Max(0f, ((Rect)(ref val)).x / num - gapAtReference.Value); stackTopAtReference.Value = Mathf.Max(0f, ((Rect)(ref val)).y / num); stackBottomAtReference.Value = stackTopAtReference.Value + Mathf.Max(70f, ((Rect)(ref val)).height / num); meterWidthAtReference.Value = Mathf.Max(22f, ((Rect)(ref val)).width / num); } private static HudResizeHandle GetResizeHandle(Rect rect, Vector2 mouse) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0050: 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_007d: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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) Rect val = new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 14f, 14f); if (((Rect)(ref val)).Contains(mouse)) { return HudResizeHandle.TopLeft; } val = new Rect(((Rect)(ref rect)).xMax - 14f, ((Rect)(ref rect)).y, 14f, 14f); if (((Rect)(ref val)).Contains(mouse)) { return HudResizeHandle.TopRight; } val = new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 14f, 14f, 14f); if (((Rect)(ref val)).Contains(mouse)) { return HudResizeHandle.BottomLeft; } val = new Rect(((Rect)(ref rect)).xMax - 14f, ((Rect)(ref rect)).yMax - 14f, 14f, 14f); if (((Rect)(ref val)).Contains(mouse)) { return HudResizeHandle.BottomRight; } return HudResizeHandle.None; } private static Rect ResizeRect(Rect start, Vector2 delta, HudResizeHandle handle, float minimumWidth, float minimumHeight) { //IL_0029: 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_0056: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_00bb: Unknown result type (might be due to invalid IL or missing references) float num = ((Rect)(ref start)).x; float num2 = ((Rect)(ref start)).y; float num3 = ((Rect)(ref start)).width; float num4 = ((Rect)(ref start)).height; switch (handle) { case HudResizeHandle.TopLeft: case HudResizeHandle.BottomLeft: num += delta.x; num3 -= delta.x; break; case HudResizeHandle.TopRight: case HudResizeHandle.BottomRight: num3 += delta.x; break; } switch (handle) { case HudResizeHandle.TopLeft: case HudResizeHandle.TopRight: num2 += delta.y; num4 -= delta.y; break; case HudResizeHandle.BottomLeft: case HudResizeHandle.BottomRight: num4 += delta.y; break; } if (num3 < minimumWidth) { if (handle == HudResizeHandle.TopLeft || handle == HudResizeHandle.BottomLeft) { num = ((Rect)(ref start)).xMax - minimumWidth; } num3 = minimumWidth; } if (num4 < minimumHeight) { if (handle == HudResizeHandle.TopLeft || handle == HudResizeHandle.TopRight) { num2 = ((Rect)(ref start)).yMax - minimumHeight; } num4 = minimumHeight; } return ClampMeterRect(new Rect(num, num2, num3, num4), GetHudScale()); } private void DrawEditorOutline(Rect rect) { //IL_0000: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00cd: 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_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0171: 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_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(1f, 0.72f, 0.15f, 0.95f); DrawRect(new Rect(((Rect)(ref rect)).x - 1f, ((Rect)(ref rect)).y - 1f, ((Rect)(ref rect)).width + 2f, 2f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).x - 1f, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width + 2f, 2f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).x - 1f, ((Rect)(ref rect)).y - 1f, 2f, ((Rect)(ref rect)).height + 2f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).xMax - 1f, ((Rect)(ref rect)).y - 1f, 2f, ((Rect)(ref rect)).height + 2f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).x - 2f, ((Rect)(ref rect)).y - 2f, 8f, 8f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).xMax - 6f, ((Rect)(ref rect)).y - 2f, 8f, 8f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).x - 2f, ((Rect)(ref rect)).yMax - 6f, 8f, 8f), GUI.color); DrawRect(new Rect(((Rect)(ref rect)).xMax - 6f, ((Rect)(ref rect)).yMax - 6f, 8f, 8f), GUI.color); GUI.color = color; } private static Color GetEnergyColour(float currentEnergy) { //IL_002f: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) return (Color)(FatigueRules.GetStage(currentEnergy) switch { FatigueStage.Fresh => new Color(0.3f, 0.78f, 0.38f, 0.95f), FatigueStage.Worn => new Color(0.9f, 0.72f, 0.22f, 0.95f), FatigueStage.Tired => new Color(0.92f, 0.48f, 0.16f, 0.95f), _ => new Color(0.76f, 0.18f, 0.15f, 0.95f), }); } private void WriteDiagnostic() { float num = Mathf.Min((float)Screen.width / 2048f, (float)Screen.height / 1152f); num = Mathf.Max(0.25f, num); float num2 = (stackRightAtReference.Value + gapAtReference.Value) * num; float num3 = stackTopAtReference.Value * num; float num4 = Mathf.Max(18f, meterWidthAtReference.Value * num); float num5 = Mathf.Max(24f, (stackBottomAtReference.Value - stackTopAtReference.Value) * num); string text = (hasSnapshot ? SnapshotToDiagnosticText(lastSnapshot) : "no snapshot yet"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("You're Tired diagnostic: enabled=" + runtimeEnabled + ", loaded=" + stateLoaded + ", energy=" + energy.ToString("0.0") + ", stage=" + FatigueRules.GetStage(energy).ToString() + ", rate=" + lastEnergyDeltaPerSecond.ToString("0.000") + "/s, patches=[regen=" + staminaRegenPatchInstalled + ", runDrain=" + runDrainPatchInstalled + ", attack=" + attackCostPatchInstalled + ", jog=" + jogSpeedPatchInstalled + ", run=" + runSpeedPatchInstalled + "], meter=(x=" + num2.ToString("0.0") + ", y=" + num3.ToString("0.0") + ", w=" + num4.ToString("0.0") + ", h=" + num5.ToString("0.0") + "), " + text + ".")); } private static string SnapshotToDiagnosticText(FatigueSnapshot snapshot) { return "snapshot=[sleep=" + snapshot.IsSleeping + ", sprint=" + snapshot.IsSprinting + ", swim=" + snapshot.IsSwimming + ", action=" + snapshot.IsActionActive + ", shelter=" + snapshot.IsSheltered + ", still=" + snapshot.IsRestingStill + ", warm=" + snapshot.IsWarm + ", dry=" + snapshot.IsDry + ", cold=" + snapshot.IsCold + ", freezing=" + snapshot.IsFreezing + ", food=" + snapshot.FoodCount + ", injury=" + snapshot.InjurySeverity.ToString("0.0") + "]"; } private static void ShowMessage(string text) { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false); } } } internal enum FatigueStage { Fresh, Worn, Tired, Exhausted } internal struct FatigueSnapshot { public bool IsSleeping; public bool IsSprinting; public bool IsSwimming; public bool IsActionActive; public bool IsSheltered; public bool IsRestingStill; public bool IsWarm; public bool IsDry; public bool IsCold; public bool IsFreezing; public int FoodCount; public float InjurySeverity; } internal struct FatigueSettings { public float AwakeDrainPerSecond; public float SprintDrainPerSecond; public float SwimmingDrainPerSecond; public float ActionDrainPerSecond; public float ColdDrainPerSecond; public float FreezingExtraDrainPerSecond; public float LowFoodDrainPerSecond; public float InjuryDrainAtMaximumPerSecond; public float ShelterRestRecoveryPerSecond; public float WarmRecoveryPerSecond; public float FoodRecoveryPerSecond; public float SleepingRecoveryPerSecond; } internal struct FatigueEffects { public float StaminaRegenMultiplier; public float SprintStaminaMultiplier; public float ToolStaminaMultiplier; public float MovementMultiplier; } internal static class FatigueRules { public const float MinimumEnergy = 0f; public const float MaximumEnergy = 100f; internal static FatigueSettings CreateBeginnerFriendlyDefaults() { return new FatigueSettings { AwakeDrainPerSecond = 0.014f, SprintDrainPerSecond = 0.05f, SwimmingDrainPerSecond = 0.075f, ActionDrainPerSecond = 0.03f, ColdDrainPerSecond = 0.015f, FreezingExtraDrainPerSecond = 0.028f, LowFoodDrainPerSecond = 0.014f, InjuryDrainAtMaximumPerSecond = 0.04f, ShelterRestRecoveryPerSecond = 0.08f, WarmRecoveryPerSecond = 0.055f, FoodRecoveryPerSecond = 0.025f, SleepingRecoveryPerSecond = 0.36f }; } internal static float ClampEnergy(float value) { return Mathf.Clamp(value, 0f, 100f); } internal static FatigueStage GetStage(float currentEnergy) { float num = ClampEnergy(currentEnergy); if (num >= 75f) { return FatigueStage.Fresh; } if (num >= 50f) { return FatigueStage.Worn; } if (num >= 25f) { return FatigueStage.Tired; } return FatigueStage.Exhausted; } internal static FatigueEffects GetEffects(float currentEnergy) { return GetStage(currentEnergy) switch { FatigueStage.Fresh => new FatigueEffects { StaminaRegenMultiplier = 1f, SprintStaminaMultiplier = 1f, ToolStaminaMultiplier = 1f, MovementMultiplier = 1f }, FatigueStage.Worn => new FatigueEffects { StaminaRegenMultiplier = 0.94f, SprintStaminaMultiplier = 1.05f, ToolStaminaMultiplier = 1.03f, MovementMultiplier = 0.98f }, FatigueStage.Tired => new FatigueEffects { StaminaRegenMultiplier = 0.82f, SprintStaminaMultiplier = 1.14f, ToolStaminaMultiplier = 1.1f, MovementMultiplier = 0.92f }, _ => new FatigueEffects { StaminaRegenMultiplier = 0.65f, SprintStaminaMultiplier = 1.28f, ToolStaminaMultiplier = 1.2f, MovementMultiplier = 0.8f }, }; } internal static float CalculateEnergyDelta(FatigueSnapshot snapshot, float elapsedSeconds, FatigueSettings settings) { float num = Mathf.Max(0f, elapsedSeconds); if (num <= 0f) { return 0f; } float num2 = 0f; if (snapshot.IsSleeping) { num2 += Mathf.Max(0f, settings.SleepingRecoveryPerSecond); return num2 * num; } num2 -= Mathf.Max(0f, settings.AwakeDrainPerSecond); if (snapshot.IsSwimming) { num2 -= Mathf.Max(0f, settings.SwimmingDrainPerSecond); } else if (snapshot.IsSprinting) { num2 -= Mathf.Max(0f, settings.SprintDrainPerSecond); } if (snapshot.IsActionActive) { num2 -= Mathf.Max(0f, settings.ActionDrainPerSecond); } if (snapshot.IsCold) { num2 -= Mathf.Max(0f, settings.ColdDrainPerSecond); } if (snapshot.IsFreezing) { num2 -= Mathf.Max(0f, settings.FreezingExtraDrainPerSecond); } if (snapshot.FoodCount < 2) { num2 -= Mathf.Max(0f, settings.LowFoodDrainPerSecond); } num2 -= Mathf.Max(0f, settings.InjuryDrainAtMaximumPerSecond) * Mathf.Clamp01(snapshot.InjurySeverity / 100f); if (snapshot.IsDry && snapshot.IsSheltered && snapshot.IsRestingStill) { num2 += Mathf.Max(0f, settings.ShelterRestRecoveryPerSecond); } if (snapshot.IsDry && snapshot.IsWarm) { num2 += Mathf.Max(0f, settings.WarmRecoveryPerSecond); } if (snapshot.FoodCount >= 2) { num2 += Mathf.Max(0f, settings.FoodRecoveryPerSecond); } return num2 * num; } internal static bool RunSelfTests(out string failure) { failure = null; if (ClampEnergy(-5f) != 0f || ClampEnergy(105f) != 100f) { failure = "ClampEnergy returned an invalid range."; return false; } if (GetStage(100f) != FatigueStage.Fresh || GetStage(50f) != FatigueStage.Worn || GetStage(25f) != FatigueStage.Tired || GetStage(0f) != FatigueStage.Exhausted) { failure = "Fatigue stage thresholds are invalid."; return false; } FatigueSettings settings = CreateBeginnerFriendlyDefaults(); if (CalculateEnergyDelta(new FatigueSnapshot { IsSleeping = true }, 1f, settings) <= 0f) { failure = "Sleeping must restore energy."; return false; } return true; } } internal static class YoureTiredPatches { public static void SEManModifyStaminaRegenPostfix(Character ___m_character, ref float __0) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((___m_character is Player) ? ___m_character : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { __0 *= instance.GetStaminaRegenMultiplier(); } } public static void SEManModifyRunStaminaDrainPostfix(Character ___m_character, ref float __1) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((___m_character is Player) ? ___m_character : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { __1 *= instance.GetRunStaminaMultiplier(); } } public static void SEManModifyAttackStaminaUsagePostfix(Character ___m_character, ref float __1) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((___m_character is Player) ? ___m_character : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { instance.NotifyStaminaUsingAction(player); __1 *= instance.GetAttackStaminaMultiplier(); } } public static void CharacterGetJogSpeedFactorPostfix(Character __instance, ref float __result) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { __result *= instance.GetMovementMultiplier(); } } public static void CharacterGetRunSpeedFactorPostfix(Character __instance, ref float __result) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { __result *= instance.GetMovementMultiplier(); } } public static void CharacterApplyDamagePostfix(Character __instance) { YoureTiredPlugin instance = YoureTiredPlugin.Instance; Player player = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)instance != (Object)null && instance.IsActiveFor(player)) { instance.NotifyDamageTaken(player); } } }