Decompiled source of YoureTired v0.2.6

YoureTired.dll

Decompiled a day ago
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<bool> modEnabled;

	private ConfigEntry<KeyCode> diagnosticKey;

	private ConfigEntry<KeyCode> sessionToggleKey;

	private ConfigEntry<float> tickInterval;

	private ConfigEntry<float> startingEnergy;

	private ConfigEntry<float> saveInterval;

	private ConfigEntry<bool> verboseLogging;

	private ConfigEntry<string> difficultyPreset;

	private ConfigEntry<string> lastAppliedDifficultyPreset;

	private ConfigEntry<float> minimumStaminaRegenMultiplier;

	private ConfigEntry<bool> showHud;

	private ConfigEntry<bool> hideHudWithCtrlF3;

	private ConfigEntry<float> stackRightAtReference;

	private ConfigEntry<float> stackTopAtReference;

	private ConfigEntry<float> stackBottomAtReference;

	private ConfigEntry<float> gapAtReference;

	private ConfigEntry<float> meterWidthAtReference;

	private ConfigEntry<float> frameThicknessAtReference;

	private ConfigEntry<KeyCode> hudLayoutEditorKey;

	private ConfigEntry<bool> enableYawnAudio;

	private ConfigEntry<float> yawnEnergyThreshold;

	private ConfigEntry<float> yawnRearmMargin;

	private ConfigEntry<float> yawnRepeatAtThresholdSeconds;

	private ConfigEntry<float> yawnRepeatAtZeroSeconds;

	private ConfigEntry<float> yawnRepeatRandomnessSeconds;

	private ConfigEntry<float> yawnAudioVolume;

	private ConfigEntry<string> yawnAudioFile;

	private ConfigEntry<float> awakeDrainPerSecond;

	private ConfigEntry<float> sprintDrainPerSecond;

	private ConfigEntry<float> swimmingDrainPerSecond;

	private ConfigEntry<float> actionDrainPerSecond;

	private ConfigEntry<float> coldDrainPerSecond;

	private ConfigEntry<float> freezingExtraDrainPerSecond;

	private ConfigEntry<float> lowFoodDrainPerSecond;

	private ConfigEntry<float> injuryDrainAtMaximumPerSecond;

	private ConfigEntry<float> shelterRestRecoveryPerSecond;

	private ConfigEntry<float> warmRecoveryPerSecond;

	private ConfigEntry<float> foodRecoveryPerSecond;

	private ConfigEntry<float> sleepingRecoveryPerSecond;

	private ConfigEntry<bool> enableTestKeys;

	private ConfigEntry<KeyCode> testDrainKey;

	private ConfigEntry<KeyCode> 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<object> 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<object> 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<ZNetView>();
		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<bool>("General", "Enabled", true, "Set false to disable the entire tiredness system, including Energy changes and fatigue penalties.");
		diagnosticKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "DiagnosticKey", (KeyCode)285, "Writes current Energy, environment and patch information to BepInEx/LogOutput.log.");
		sessionToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("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<float>("General", "TickIntervalSeconds", 0.5f, "How often Energy is recalculated. Lower is smoother but uses slightly more CPU.");
		startingEnergy = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StartingEnergyForNewCharacters", 100f, "Energy used only when this character has no saved You're Tired state yet.");
		saveInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SaveIntervalSeconds", 5f, "How often Energy is saved to the owned player data.");
		verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Writes one fatigue line every tick. Leave off during normal play.");
		difficultyPreset = ((BaseUnityPlugin)this).Config.Bind<string>("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<string>("Difficulty", "LastAppliedPreset", "Normal", "Internal preset tracking. Leave this alone; it records the last difficulty preset applied.");
		minimumStaminaRegenMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("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<bool>("HUD", "ShowTiredMeter", true, "Set false to hide only the Tired HUD while the tiredness system continues running.");
		hideHudWithCtrlF3 = ((BaseUnityPlugin)this).Config.Bind<bool>("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<float>("HUD Layout", "ExistingStackRightAt2048x1152", 470f, "Right edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot.");
		stackTopAtReference = ((BaseUnityPlugin)this).Config.Bind<float>("HUD Layout", "ExistingStackTopAt2048x1152", 982f, "Top edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot.");
		stackBottomAtReference = ((BaseUnityPlugin)this).Config.Bind<float>("HUD Layout", "ExistingStackBottomAt2048x1152", 1111f, "Bottom edge of the existing Cold/Injured survival stack in the supplied 2048x1152 screenshot.");
		gapAtReference = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("HUD Layout", "MeterWidthAt2048x1152", 30f, "Width of the Tired meter including its frame.");
		frameThicknessAtReference = ((BaseUnityPlugin)this).Config.Bind<float>("HUD Layout", "FrameThicknessAt2048x1152", 2f, "Frame thickness of the Tired meter.");
		hudLayoutEditorKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("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<bool>("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<float>("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<float>("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<float>("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<float>("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<float>("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<float>("Yawn Audio", "Volume", 0.62f, "Volume for the externally loaded yawn sound.");
		yawnAudioFile = ((BaseUnityPlugin)this).Config.Bind<string>("Yawn Audio", "YawnFile", "yawn.mp3", "MP3 filename expected in assets/audio beside the You're Tired DLL.");
		awakeDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "AwakeDrainPerSecond", fatigueSettings.AwakeDrainPerSecond, "Gentle Energy drain while awake. It is intentionally low so ordinary exploration is fair.");
		sprintDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "SprintDrainPerSecond", fatigueSettings.SprintDrainPerSecond, "Extra Energy drain while sprinting.");
		swimmingDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "SwimmingDrainPerSecond", fatigueSettings.SwimmingDrainPerSecond, "Extra Energy drain while swimming. Swimming replaces sprint drain rather than stacking with it.");
		actionDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "CombatAndWorkDrainPerSecond", fatigueSettings.ActionDrainPerSecond, "Extra Energy drain during stamina-using attacks, including combat, chopping and mining.");
		coldDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "ColdDrainPerSecond", fatigueSettings.ColdDrainPerSecond, "Extra Energy drain during cold conditions.");
		freezingExtraDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "FreezingExtraDrainPerSecond", fatigueSettings.FreezingExtraDrainPerSecond, "Additional Energy drain during freezing conditions on top of ordinary cold.");
		lowFoodDrainPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "LowFoodDrainPerSecond", fatigueSettings.LowFoodDrainPerSecond, "Extra drain when fewer than two Valheim food slots are active.");
		injuryDrainAtMaximumPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Drain", "InjuryDrainAtMaximumPerSecond", fatigueSettings.InjuryDrainAtMaximumPerSecond, "Maximum extra drain from severe injury/low health. It scales gradually and never deals damage.");
		shelterRestRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Recovery", "DryShelterRestRecoveryPerSecond", fatigueSettings.ShelterRestRecoveryPerSecond, "Energy restored while dry, sheltered and nearly still.");
		warmRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Recovery", "WarmthRecoveryPerSecond", fatigueSettings.WarmRecoveryPerSecond, "Extra Energy restored while dry near a campfire or heat area.");
		foodRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Recovery", "FoodRecoveryPerSecond", fatigueSettings.FoodRecoveryPerSecond, "Extra Energy restored with two or more active Valheim food slots.");
		sleepingRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Recovery", "SleepingRecoveryPerSecond", fatigueSettings.SleepingRecoveryPerSecond, "Strong Energy restoration while in a dry sheltered bed.");
		enableTestKeys = ((BaseUnityPlugin)this).Config.Bind<bool>("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<KeyCode>("Debug", "TestDrainKey", (KeyCode)286, "Debug-only key that removes 10 Energy when EnableTestKeys is true.");
		testRestoreKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("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);
		}
	}
}