Decompiled source of Stamina Panic v2.0.0

tony4twentys-Stamina Panic.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Settings;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyTitle("Stamina Panic")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Stamina Panic")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("f3d2d2cf-e3bd-4b0f-a0ba-2082998aceb8")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.0")]
[module: UnverifiableCode]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace StaminaPanic
{
	[BepInPlugin("tony4twentys.Stamina_Panic", "Stamina Panic", "2.0.0")]
	public class StaminaPanic : BaseUnityPlugin
	{
		private ConfigEntry<bool> enabledConfig;

		private ConfigEntry<bool> enableLoggingConfig;

		private ConfigEntry<float> thresholdConfig;

		private ConfigEntry<bool> afflictionOnlyConfig;

		private ConfigEntry<bool> playAudioConfig;

		private ConfigEntry<string> audioFileConfig;

		private ConfigEntry<float> volumeConfig;

		private ConfigEntry<float> fadeOutTimeConfig;

		private ConfigEntry<bool> loopConfig;

		private ConfigEntry<bool> limitEnabledConfig;

		private ConfigEntry<float> limitSecondsConfig;

		private ConfigEntry<float> extraThresholdConfig;

		private ConfigEntry<bool> extraPlayAudioConfig;

		private ConfigEntry<string> extraAudioFileConfig;

		private ConfigEntry<float> extraVolumeConfig;

		private ConfigEntry<float> extraFadeOutTimeConfig;

		private ConfigEntry<bool> extraLoopConfig;

		private ConfigEntry<bool> extraLimitEnabledConfig;

		private ConfigEntry<float> extraLimitSecondsConfig;

		private ConfigEntry<string> extraFlashColorConfig;

		private ConfigEntry<bool> flashEnabledConfig;

		private ConfigEntry<string> flashColorConfig;

		private ConfigEntry<float> flashSpeedConfig;

		private ConfigEntry<bool> respectPhotosensitivityConfig;

		private GameObject audioHost;

		private CuePlayer mainCue;

		private CuePlayer extraCue;

		private Coroutine mainLoadRoutine;

		private Coroutine extraLoadRoutine;

		private bool inGameScene;

		private bool inRedZone;

		private bool extraWarning;

		private bool loggedTickError;

		private bool mainWantedAudio;

		private bool mainAudioCapped;

		private float mainAudioStartedAt;

		private bool extraWantedAudio;

		private bool extraAudioCapped;

		private float extraAudioStartedAt;

		private float lastExtraStamina = -1f;

		private float extraDrainUntil;

		private float quietUntil;

		private Character hookedCharacter;

		private bool quiet;

		private bool loggedMainRed;

		private bool loggedExtraLow;

		private void Awake()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Expected O, but got Unknown
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Expected O, but got Unknown
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Expected O, but got Unknown
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Expected O, but got Unknown
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Expected O, but got Unknown
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_043c: Expected O, but got Unknown
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Expected O, but got Unknown
			//IL_04e5: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary.Initialize(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
			enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for Stamina Panic.");
			enableLoggingConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableLogging", false, "If true, write stamina / extra-stamina debug lines to the BepInEx log. Off by default.");
			thresholdConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "Threshold", 0.2f, new ConfigDescription("Red-zone threshold for the main stamina bar (0.0 to 1.0). In affliction-only mode this is remaining max stamina after statuses occupy the bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			afflictionOnlyConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina", "AfflictionOnly", false, "If true, the main cue and bar flash only care about afflictions eating the bar (injury, hunger, poison, weight, cold, etc.). Sprinting, climbing, and jumping will not trigger them. If false, remaining usable stamina (including movement drain) is used.");
			playAudioConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina", "PlayAudio", true, "Play the main stamina warning sound. Turn this off to keep the outline flash without audio.");
			audioFileConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Stamina", "AudioFile", AudioLibrary.DefaultCueFile(), new ConfigDescription("Audio file for the main stamina cue. Drop .wav or .ogg files next to the plugin (or in subfolders), then pick one here. Reopen ModConfig after adding files if the list looks stale.", (AcceptableValueBase)(object)AudioLibrary.FileList, Array.Empty<object>()));
			volumeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "CueVolume", 100f, new ConfigDescription("Volume for the main warning cue (0 to 100). Also scaled by the game Master Volume slider.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			fadeOutTimeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "FadeOutTime", 1.5f, new ConfigDescription("Seconds to fade out the main cue when it should stop.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			loopConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina", "CueLoop", true, "If true, the main cue loops while you stay in the red zone. If false, it plays once and will not play again until stamina goes back above the threshold.");
			limitEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina", "LimitCueDuration", false, "If true, the main warning sound stops after CueDurationLimitSeconds even if you stay in the red zone. It can play again after you leave the red zone and re-enter. The bar flash is not capped.");
			limitSecondsConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "CueDurationLimitSeconds", 180f, new ConfigDescription("How long the main warning sound may play per red-zone stretch when LimitCueDuration is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1800f), Array.Empty<object>()));
			extraThresholdConfig = ((BaseUnityPlugin)this).Config.Bind<float>("ExtraStamina", "Threshold", 0.2f, new ConfigDescription("Extra-stamina bar is considered low at or below this value (0.0 to 1.0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			extraPlayAudioConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("ExtraStamina", "PlayAudio", false, "Play the extra-stamina warning sound. Off by default so extra stamina only flashes the outline.");
			extraAudioFileConfig = ((BaseUnityPlugin)this).Config.Bind<string>("ExtraStamina", "AudioFile", AudioLibrary.DefaultCueFile(), new ConfigDescription("Audio file for the extra-stamina cue. Drop .wav or .ogg files next to the plugin (or in subfolders), then pick one here. Reopen ModConfig after adding files if the list looks stale.", (AcceptableValueBase)(object)AudioLibrary.FileList, Array.Empty<object>()));
			extraVolumeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("ExtraStamina", "CueVolume", 100f, new ConfigDescription("Volume for the extra-stamina warning cue (0 to 100). Also scaled by Master Volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			extraFadeOutTimeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("ExtraStamina", "FadeOutTime", 1.5f, new ConfigDescription("Seconds to fade out the extra-stamina cue when it should stop.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			extraLoopConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("ExtraStamina", "CueLoop", false, "If true, the extra-stamina cue loops while extra stamina stays below the threshold. If false, it plays once and will not play again until extra stamina goes back above the threshold.");
			extraLimitEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("ExtraStamina", "LimitCueDuration", false, "If true, the extra-stamina warning sound stops after CueDurationLimitSeconds even if extra stamina stays low and draining. It can play again after that drain stretch ends. The extra outline flash is not capped.");
			extraLimitSecondsConfig = ((BaseUnityPlugin)this).Config.Bind<float>("ExtraStamina", "CueDurationLimitSeconds", 180f, new ConfigDescription("How long the extra-stamina warning sound may play per low stretch when LimitCueDuration is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1800f), Array.Empty<object>()));
			extraFlashColorConfig = ((BaseUnityPlugin)this).Config.Bind<string>("ExtraStamina", "FlashColor", "#FFB02A", "Extra-stamina outline flash color as #RRGGBB or #RRGGBBAA.");
			flashEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "FlashBarOutline", true, "Flash stamina-bar outlines (not affliction fills). The main outline flashes while in the red zone. The extra-stamina outline flashes only while extra stamina is low and being drained.");
			flashColorConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Visual", "FlashColor", "#FF2A2A", "Main stamina outline flash color as #RRGGBB or #RRGGBBAA.");
			flashSpeedConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "FlashSpeed", 2.5f, new ConfigDescription("Outline flash pulses per second.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>()));
			respectPhotosensitivityConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "RespectPhotosensitivity", true, "If the game photosensitivity option is on, use a static outline tint instead of pulsing.");
			audioHost = new GameObject("StaminaPanicAudioHost");
			Object.DontDestroyOnLoad((Object)(object)audioHost);
			mainCue = new CuePlayer(GameAudio.CreateSource(audioHost, "MainCue"));
			extraCue = new CuePlayer(GameAudio.CreateSource(audioHost, "ExtraCue"));
			audioFileConfig.SettingChanged += OnMainAudioFileChanged;
			extraAudioFileConfig.SettingChanged += OnExtraAudioFileChanged;
			inGameScene = IsLevelScene(SceneManager.GetActiveScene());
			SceneManager.activeSceneChanged += OnSceneChanged;
			ReloadMainClip();
			ReloadExtraClip();
		}

		private void OnDestroy()
		{
			if (audioFileConfig != null)
			{
				audioFileConfig.SettingChanged -= OnMainAudioFileChanged;
			}
			if (extraAudioFileConfig != null)
			{
				extraAudioFileConfig.SettingChanged -= OnExtraAudioFileChanged;
			}
			HookRevive(null);
			SceneManager.activeSceneChanged -= OnSceneChanged;
			Silence();
			BarFlash.RestoreAll();
			GameAudio.Reset();
			AudioLibrary.Shutdown();
			if ((Object)(object)audioHost != (Object)null)
			{
				Object.Destroy((Object)(object)audioHost);
			}
		}

		private void OnSceneChanged(Scene oldScene, Scene newScene)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			inGameScene = IsLevelScene(newScene);
			quietUntil = Time.time + 3f;
			Silence();
			BarFlash.RestoreAll();
			GameAudio.Reset();
		}

		private void OnMainAudioFileChanged(object sender, EventArgs e)
		{
			ReloadMainClip();
		}

		private void OnExtraAudioFileChanged(object sender, EventArgs e)
		{
			ReloadExtraClip();
		}

		private void Update()
		{
			if (AudioLibrary.ConsumeFolderChange())
			{
				ReloadMainClip();
				ReloadExtraClip();
			}
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				if (!loggedTickError)
				{
					loggedTickError = true;
					((BaseUnityPlugin)this).Logger.LogError((object)("Stamina Panic tick failed once: " + ex));
					Silence();
					BarFlash.RestoreAll();
				}
			}
		}

		private void LateUpdate()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			bool flag = flashEnabledConfig.Value && enabledConfig.Value && inGameScene && !quiet && (Object)(object)Character.localCharacter != (Object)null && (Object)(object)Character.observedCharacter == (Object)(object)Character.localCharacter;
			bool photosensitivity = respectPhotosensitivityConfig.Value && (Object)(object)GUIManager.instance != (Object)null && GUIManager.instance.photosensitivity;
			BarFlash.Tick(flag && inRedZone, flag && extraWarning, ParseColor(flashColorConfig.Value, new Color(1f, 0.16f, 0.16f, 1f)), ParseColor(extraFlashColorConfig.Value, new Color(1f, 0.69f, 0.16f, 1f)), flashSpeedConfig.Value, photosensitivity);
		}

		private void Tick()
		{
			float localVolume = Mathf.Clamp(volumeConfig.Value, 0f, 100f) / 100f;
			float localVolume2 = Mathf.Clamp(extraVolumeConfig.Value, 0f, 100f) / 100f;
			float value = fadeOutTimeConfig.Value;
			float value2 = extraFadeOutTimeConfig.Value;
			if (!enabledConfig.Value || !inGameScene)
			{
				Silence();
				return;
			}
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null)
			{
				HookRevive(null);
				Silence();
				return;
			}
			bool flag = (Object)(object)hookedCharacter != (Object)(object)localCharacter;
			HookRevive(localCharacter);
			if (flag)
			{
				quietUntil = Time.time + 3f;
			}
			CharacterData data = localCharacter.data;
			bool flag2 = data.dead || data.passedOut || data.fullyPassedOut || !data.fullyConscious;
			bool flag3 = localCharacter.warping || data.passedOutOnTheBeach > 0f;
			if (flag2 || flag3)
			{
				quietUntil = Time.time + 3f;
				BeginQuiet(resetStretch: true);
				FadeOutCues(resetOneShot: true);
				return;
			}
			quiet = Time.time < quietUntil;
			if (quiet)
			{
				BeginQuiet(resetStretch: false);
				FadeOutCues(resetOneShot: false);
				return;
			}
			float num = Mathf.Clamp01(thresholdConfig.Value);
			float extraStamina = data.extraStamina;
			float ceiling = Mathf.Max(0f, 1f - (float)data.petrifyAmount * 0.01f);
			float ceiling2 = (afflictionOnlyConfig.Value ? 1f : (1f + extraStamina));
			float num2 = (afflictionOnlyConfig.Value ? localCharacter.GetMaxStamina() : localCharacter.GetTotalStamina());
			float recoverAt = RecoverPoint(num, ceiling2);
			bool flag4 = IsRecovered(num2, recoverAt);
			inRedZone = !flag4 && (inRedZone || num2 <= num);
			float num3 = Mathf.Clamp01(extraThresholdConfig.Value);
			float recoverAt2 = RecoverPoint(num3, ceiling);
			bool flag5 = IsRecovered(extraStamina, recoverAt2);
			bool flag6 = extraStamina > 0.001f && extraStamina <= num3;
			extraWarning = flag6 && IsExtraDraining(localCharacter, extraStamina);
			LogZone("main", inRedZone, ref loggedMainRed, "tracked=" + num2.ToString("0.000") + " max=" + localCharacter.GetMaxStamina().ToString("0.000") + " ceiling=" + ceiling2.ToString("0.000") + " thresh=" + num.ToString("0.000") + " recoverAt=" + recoverAt.ToString("0.000") + " afflictionOnly=" + afflictionOnlyConfig.Value);
			LogZone("extra", flag6, ref loggedExtraLow, "extra=" + extraStamina.ToString("0.000") + " ceiling=" + ceiling.ToString("0.000") + " thresh=" + num3.ToString("0.000") + " recoverAt=" + recoverAt2.ToString("0.000") + " draining=" + extraWarning);
			bool wantPlay = WantAudio(inRedZone, flag4, playAudioConfig.Value, limitEnabledConfig.Value, limitSecondsConfig.Value, ref mainWantedAudio, ref mainAudioStartedAt, ref mainAudioCapped);
			bool wantPlay2 = WantAudio(flag6, flag5, extraPlayAudioConfig.Value, extraLimitEnabledConfig.Value, extraLimitSecondsConfig.Value, ref extraWantedAudio, ref extraAudioStartedAt, ref extraAudioCapped);
			mainCue.Tick(wantPlay, localVolume, value, loopConfig.Value, flag4);
			extraCue.Tick(wantPlay2, localVolume2, value2, extraLoopConfig.Value, flag5);
		}

		private static bool WantAudio(bool inZone, bool recovered, bool playAudio, bool limitEnabled, float limitSeconds, ref bool wasWanted, ref float startedAt, ref bool capped)
		{
			if (recovered)
			{
				wasWanted = false;
				capped = false;
			}
			if (!inZone || !playAudio)
			{
				return false;
			}
			if (!wasWanted)
			{
				startedAt = Time.time;
			}
			wasWanted = true;
			if (limitEnabled && Time.time - startedAt >= Mathf.Max(0.1f, limitSeconds))
			{
				capped = true;
			}
			return !capped;
		}

		private static float RecoverPoint(float threshold, float ceiling)
		{
			threshold = Mathf.Clamp01(threshold);
			if (ceiling < 0.0005f)
			{
				return threshold;
			}
			float num = Mathf.Max(0f, ceiling - threshold);
			if (num <= 0.0005f)
			{
				return ceiling;
			}
			float num2 = Mathf.Min(num, Mathf.Max(0.02f, Mathf.Min(0.05f, num * 0.25f)));
			return threshold + num2;
		}

		private static bool IsRecovered(float value, float recoverAt)
		{
			return value + 0.0005f >= recoverAt;
		}

		private void Log(string message)
		{
			if (enableLoggingConfig != null && enableLoggingConfig.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}
		}

		private void LogZone(string name, bool active, ref bool logged, string detail)
		{
			if (active != logged)
			{
				logged = active;
				Log(name + (active ? " ENTER " : " EXIT ") + detail);
			}
		}

		private bool IsExtraDraining(Character character, float extra)
		{
			if (lastExtraStamina >= 0f && extra + 0.0005f < lastExtraStamina)
			{
				extraDrainUntil = Time.time + 0.4f;
			}
			lastExtraStamina = extra;
			return Time.time < extraDrainUntil || (character.data.currentStamina <= 0.005f && character.data.sinceUseStamina < 0.4f);
		}

		private void FadeOutCues(bool resetOneShot)
		{
			float localVolume = Mathf.Clamp(volumeConfig.Value, 0f, 100f) / 100f;
			float localVolume2 = Mathf.Clamp(extraVolumeConfig.Value, 0f, 100f) / 100f;
			if (mainCue != null)
			{
				mainCue.Tick(wantPlay: false, localVolume, fadeOutTimeConfig.Value, loop: false, resetOneShot);
			}
			if (extraCue != null)
			{
				extraCue.Tick(wantPlay: false, localVolume2, extraFadeOutTimeConfig.Value, loop: false, resetOneShot);
			}
		}

		private void BeginQuiet(bool resetStretch)
		{
			quiet = true;
			inRedZone = false;
			extraWarning = false;
			if (resetStretch)
			{
				mainWantedAudio = false;
				extraWantedAudio = false;
				mainAudioCapped = false;
				extraAudioCapped = false;
				lastExtraStamina = -1f;
				extraDrainUntil = 0f;
			}
		}

		private void Silence()
		{
			BeginQuiet(resetStretch: true);
			if (mainCue != null)
			{
				mainCue.StopImmediate();
			}
			if (extraCue != null)
			{
				extraCue.StopImmediate();
			}
		}

		private void HookRevive(Character character)
		{
			if (!((Object)(object)hookedCharacter == (Object)(object)character))
			{
				if ((Object)(object)hookedCharacter != (Object)null)
				{
					Character obj = hookedCharacter;
					obj.reviveAction = (Action)Delegate.Remove(obj.reviveAction, new Action(OnRevived));
				}
				hookedCharacter = character;
				if ((Object)(object)hookedCharacter != (Object)null)
				{
					Character obj2 = hookedCharacter;
					obj2.reviveAction = (Action)Delegate.Combine(obj2.reviveAction, new Action(OnRevived));
				}
			}
		}

		private void OnRevived()
		{
			quietUntil = Time.time + 3f;
			BeginQuiet(resetStretch: true);
		}

		private void ReloadMainClip()
		{
			if (mainLoadRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(mainLoadRoutine);
			}
			mainLoadRoutine = ((MonoBehaviour)this).StartCoroutine(LoadSelectedClip(audioFileConfig.Value, mainCue, "main"));
		}

		private void ReloadExtraClip()
		{
			if (extraLoadRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(extraLoadRoutine);
			}
			extraLoadRoutine = ((MonoBehaviour)this).StartCoroutine(LoadSelectedClip(extraAudioFileConfig.Value, extraCue, "extra"));
		}

		private IEnumerator LoadSelectedClip(string displayName, CuePlayer player, string label)
		{
			if (player == null)
			{
				yield break;
			}
			if (AudioLibrary.IsNone(displayName))
			{
				player.SetClip(null);
				yield break;
			}
			string path = AudioLibrary.Resolve(displayName);
			if (string.IsNullOrEmpty(path))
			{
				Log("audio file not found (" + label + "): " + displayName);
				player.SetClip(null);
				yield break;
			}
			AudioClip clip = null;
			yield return LoadClip(path, delegate(AudioClip loaded)
			{
				clip = loaded;
			});
			if (player != null)
			{
				player.SetClip(clip);
				if ((Object)(object)clip != (Object)null)
				{
					Log(label + " cue: " + path);
				}
			}
		}

		private IEnumerator LoadClip(string path, Action<AudioClip> onLoaded)
		{
			string url = new Uri(Path.GetFullPath(path)).AbsoluteUri;
			AudioType audioType = (AudioType)(path.EndsWith(".ogg", StringComparison.OrdinalIgnoreCase) ? 14 : 20);
			UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType);
			try
			{
				yield return www.SendWebRequest();
				if ((int)www.result != 1)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Stamina Panic failed to load " + path + ": " + www.error));
					onLoaded(null);
					yield break;
				}
				AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
				if ((Object)(object)clip == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Stamina Panic failed to create clip from " + path));
				}
				onLoaded(clip);
			}
			finally
			{
				((IDisposable)www)?.Dispose();
			}
		}

		private static bool IsLevelScene(Scene scene)
		{
			return !string.IsNullOrEmpty(((Scene)(ref scene)).name) && ((Scene)(ref scene)).name.Contains("Level_");
		}

		private static Color ParseColor(string hex, Color fallback)
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(hex))
			{
				return fallback;
			}
			string text = hex.Trim();
			if (text.StartsWith("#", StringComparison.Ordinal))
			{
				text = text.Substring(1);
			}
			if (text.Length != 6 && text.Length != 8)
			{
				return fallback;
			}
			try
			{
				byte b = Convert.ToByte(text.Substring(0, 2), 16);
				byte b2 = Convert.ToByte(text.Substring(2, 2), 16);
				byte b3 = Convert.ToByte(text.Substring(4, 2), 16);
				byte b4 = ((text.Length == 8) ? Convert.ToByte(text.Substring(6, 2), 16) : byte.MaxValue);
				return new Color((float)(int)b / 255f, (float)(int)b2 / 255f, (float)(int)b3 / 255f, (float)(int)b4 / 255f);
			}
			catch
			{
				return fallback;
			}
		}
	}
	internal sealed class MutableValueList : AcceptableValueList<string>
	{
		private string[] _values;

		public override string[] AcceptableValues => _values;

		internal MutableValueList(params string[] values)
			: base(Sanitize(values))
		{
			_values = Sanitize(values);
		}

		internal void SetValues(string[] values)
		{
			_values = Sanitize(values);
		}

		private static string[] Sanitize(string[] values)
		{
			if (values != null && values.Length != 0)
			{
				return values;
			}
			return new string[1] { "(none)" };
		}
	}
	internal static class AudioLibrary
	{
		internal const string None = "(none)";

		internal static string Directory;

		internal static MutableValueList FileList;

		private static string _signature = "";

		private static FileSystemWatcher _watcher;

		private static volatile bool _dirty;

		internal static void Initialize(string directory)
		{
			Directory = directory;
			FileList = new MutableValueList(ScanNames());
			_signature = BuildSignature();
			StartWatcher();
		}

		internal static void Shutdown()
		{
			FileSystemWatcher watcher = _watcher;
			_watcher = null;
			if (watcher == null)
			{
				return;
			}
			try
			{
				watcher.EnableRaisingEvents = false;
				watcher.Created -= OnFileSystemEvent;
				watcher.Changed -= OnFileSystemEvent;
				watcher.Deleted -= OnFileSystemEvent;
				watcher.Renamed -= OnFileSystemEvent;
				watcher.Dispose();
			}
			catch
			{
			}
		}

		internal static bool ConsumeFolderChange()
		{
			if (!_dirty)
			{
				return false;
			}
			_dirty = false;
			return RefreshIfChanged();
		}

		internal static bool RefreshIfChanged()
		{
			string text = BuildSignature();
			if (text == _signature)
			{
				return false;
			}
			_signature = text;
			if (FileList != null)
			{
				FileList.SetValues(ScanNames());
			}
			return true;
		}

		internal static string DefaultCueFile()
		{
			string[] array = CurrentNames();
			string text = "(none)";
			for (int i = 0; i < array.Length; i++)
			{
				if (!IsNone(array[i]))
				{
					if (text == "(none)")
					{
						text = array[i];
					}
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(array[i]);
					if (string.Equals(fileNameWithoutExtension, "Panic", StringComparison.OrdinalIgnoreCase))
					{
						return array[i];
					}
				}
			}
			return text;
		}

		internal static bool IsNone(string name)
		{
			return string.IsNullOrEmpty(name) || string.Equals(name, "(none)", StringComparison.OrdinalIgnoreCase);
		}

		internal static string Resolve(string displayName)
		{
			if (IsNone(displayName) || string.IsNullOrEmpty(Directory))
			{
				return null;
			}
			string path = Path.Combine(Directory, displayName.Replace('/', Path.DirectorySeparatorChar));
			if (File.Exists(path))
			{
				return Path.GetFullPath(path);
			}
			try
			{
				string[] files = System.IO.Directory.GetFiles(Directory, "*.*", SearchOption.AllDirectories);
				string fileName = Path.GetFileName(displayName);
				for (int i = 0; i < files.Length; i++)
				{
					if (IsAudioFile(files[i]) && string.Equals(Path.GetFileName(files[i]), fileName, StringComparison.OrdinalIgnoreCase))
					{
						return Path.GetFullPath(files[i]);
					}
				}
			}
			catch
			{
			}
			return null;
		}

		internal static bool IsAudioFile(string path)
		{
			return path.EndsWith(".ogg", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".wav", StringComparison.OrdinalIgnoreCase);
		}

		private static string[] CurrentNames()
		{
			if (FileList != null && ((AcceptableValueList<string>)FileList).AcceptableValues != null && ((AcceptableValueList<string>)FileList).AcceptableValues.Length != 0)
			{
				return ((AcceptableValueList<string>)FileList).AcceptableValues;
			}
			return ScanNames();
		}

		private static string[] ScanNames()
		{
			List<string> list = new List<string> { "(none)" };
			if (string.IsNullOrEmpty(Directory) || !System.IO.Directory.Exists(Directory))
			{
				return list.ToArray();
			}
			try
			{
				string text = Path.GetFullPath(Directory);
				string text2 = text;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				if (!text2.EndsWith(directorySeparatorChar.ToString()))
				{
					string text3 = text;
					directorySeparatorChar = Path.DirectorySeparatorChar;
					text = text3 + directorySeparatorChar;
				}
				string[] files = System.IO.Directory.GetFiles(Directory, "*.*", SearchOption.AllDirectories);
				Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				for (int i = 0; i < files.Length; i++)
				{
					if (IsAudioFile(files[i]))
					{
						string fullPath = Path.GetFullPath(files[i]);
						string text4 = fullPath;
						if (fullPath.StartsWith(text, StringComparison.OrdinalIgnoreCase))
						{
							text4 = fullPath.Substring(text.Length);
						}
						list.Add(text4.Replace('\\', '/'));
					}
				}
			}
			catch
			{
			}
			return list.ToArray();
		}

		private static string BuildSignature()
		{
			if (string.IsNullOrEmpty(Directory) || !System.IO.Directory.Exists(Directory))
			{
				return "";
			}
			try
			{
				List<string> list = new List<string>();
				string[] files = System.IO.Directory.GetFiles(Directory, "*.*", SearchOption.AllDirectories);
				Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				for (int i = 0; i < files.Length; i++)
				{
					if (IsAudioFile(files[i]))
					{
						list.Add(files[i] + "|" + File.GetLastWriteTimeUtc(files[i]).Ticks);
					}
				}
				return string.Join("\n", list.ToArray());
			}
			catch
			{
				return "";
			}
		}

		private static void StartWatcher()
		{
			Shutdown();
			if (string.IsNullOrEmpty(Directory) || !System.IO.Directory.Exists(Directory))
			{
				return;
			}
			try
			{
				FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Directory);
				fileSystemWatcher.IncludeSubdirectories = true;
				fileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size | NotifyFilters.LastWrite;
				fileSystemWatcher.Created += OnFileSystemEvent;
				fileSystemWatcher.Changed += OnFileSystemEvent;
				fileSystemWatcher.Deleted += OnFileSystemEvent;
				fileSystemWatcher.Renamed += OnFileSystemEvent;
				fileSystemWatcher.EnableRaisingEvents = true;
				_watcher = fileSystemWatcher;
			}
			catch
			{
				_watcher = null;
			}
		}

		private static void OnFileSystemEvent(object sender, FileSystemEventArgs e)
		{
			_dirty = true;
		}
	}
	internal static class GameAudio
	{
		private static MasterVolumeSetting _master;

		private static AudioMixerGroup _mixerGroup;

		private static bool _triedSettings;

		internal static void Reset()
		{
			_master = null;
			_mixerGroup = null;
			_triedSettings = false;
		}

		internal static void Apply(AudioSource source, float localVolume)
		{
			if (!((Object)(object)source == (Object)null))
			{
				EnsureSettings();
				if ((Object)(object)_mixerGroup != (Object)null)
				{
					source.outputAudioMixerGroup = _mixerGroup;
				}
				source.volume = ScaledVolume(localVolume);
			}
		}

		internal static float ScaledVolume(float localVolume)
		{
			EnsureSettings();
			float num = Mathf.Clamp01(localVolume);
			if (_master != null)
			{
				num *= Mathf.Clamp01(((FloatSetting)_master).Value);
			}
			return num;
		}

		internal static AudioSource CreateSource(GameObject host, string name)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(host.transform, false);
			AudioSource val2 = val.AddComponent<AudioSource>();
			val2.playOnAwake = false;
			val2.spatialBlend = 0f;
			val2.dopplerLevel = 0f;
			val2.rolloffMode = (AudioRolloffMode)1;
			val2.minDistance = 1f;
			val2.maxDistance = 500f;
			val2.ignoreListenerPause = true;
			val2.bypassEffects = false;
			val2.bypassListenerEffects = false;
			val2.bypassReverbZones = true;
			val2.priority = 64;
			EnsureSettings();
			if ((Object)(object)_mixerGroup != (Object)null)
			{
				val2.outputAudioMixerGroup = _mixerGroup;
			}
			return val2;
		}

		private static void EnsureSettings()
		{
			if (_master == null && (!_triedSettings || !((Object)(object)GameHandler.Instance == (Object)null)))
			{
				_triedSettings = true;
				if (!((Object)(object)GameHandler.Instance == (Object)null) && GameHandler.Instance.SettingsHandler != null)
				{
					_master = GameHandler.Instance.SettingsHandler.GetSetting<MasterVolumeSetting>();
					ResolveMixerGroup();
				}
			}
		}

		private static void ResolveMixerGroup()
		{
			_mixerGroup = null;
			if (_master == null || (Object)(object)((VolumeSetting)_master).mixerGroup == (Object)null)
			{
				return;
			}
			AudioMixer audioMixer = ((VolumeSetting)_master).mixerGroup.audioMixer;
			if ((Object)(object)audioMixer != (Object)null)
			{
				AudioMixerGroup[] array = audioMixer.FindMatchingGroups("Master");
				if (array != null && array.Length != 0 && (Object)(object)array[0] != (Object)null)
				{
					_mixerGroup = array[0];
					return;
				}
			}
			_mixerGroup = ((VolumeSetting)_master).mixerGroup;
		}
	}
	internal sealed class CuePlayer
	{
		private readonly AudioSource _source;

		private AudioClip _clip;

		private bool _fading;

		private float _fadeElapsed;

		private float _fadeDuration;

		private bool _oneShotPlayed;

		internal bool HasClip => (Object)(object)_clip != (Object)null && (Object)(object)_source != (Object)null;

		internal bool IsAudible => (Object)(object)_source != (Object)null && (_source.isPlaying || _fading);

		internal CuePlayer(AudioSource source)
		{
			_source = source;
		}

		internal void SetClip(AudioClip clip)
		{
			StopImmediate(resetOneShot: false);
			if ((Object)(object)_clip != (Object)null && (Object)(object)_clip != (Object)(object)clip)
			{
				Object.Destroy((Object)(object)_clip);
			}
			_clip = clip;
			if (!((Object)(object)_source == (Object)null))
			{
				_source.clip = clip;
			}
		}

		internal void Tick(bool wantPlay, float localVolume, float fadeOutSeconds, bool loop, bool resetOneShot)
		{
			if ((Object)(object)_source == (Object)null || (Object)(object)_clip == (Object)null)
			{
				return;
			}
			if (resetOneShot)
			{
				_oneShotPlayed = false;
			}
			float num = GameAudio.ScaledVolume(localVolume);
			if (wantPlay)
			{
				_source.loop = loop;
				_fading = false;
				_fadeElapsed = 0f;
				GameAudio.Apply(_source, localVolume);
				if (!_source.isPlaying && (loop || !_oneShotPlayed))
				{
					_source.Play();
					if (!loop)
					{
						_oneShotPlayed = true;
					}
				}
				return;
			}
			_source.loop = false;
			if (!_source.isPlaying && !_fading)
			{
				GameAudio.Apply(_source, localVolume);
				return;
			}
			if (!_fading)
			{
				_fading = true;
				_fadeElapsed = 0f;
				_fadeDuration = Mathf.Max(0f, fadeOutSeconds);
			}
			if (_fadeDuration <= 0f)
			{
				StopImmediate(resetOneShot: false);
				GameAudio.Apply(_source, localVolume);
				return;
			}
			_fadeElapsed += Time.deltaTime;
			float num2 = Mathf.Clamp01(_fadeElapsed / _fadeDuration);
			_source.volume = Mathf.Lerp(num, 0f, num2);
			if (num2 >= 1f)
			{
				StopImmediate(resetOneShot: false);
				GameAudio.Apply(_source, localVolume);
			}
		}

		internal void StopImmediate(bool resetOneShot = true)
		{
			_fading = false;
			_fadeElapsed = 0f;
			if (resetOneShot)
			{
				_oneShotPlayed = false;
			}
			if ((Object)(object)_source != (Object)null)
			{
				_source.loop = false;
				if (_source.isPlaying)
				{
					_source.Stop();
				}
			}
		}
	}
	internal static class BarFlash
	{
		private struct FlashedImage
		{
			public Image Image;

			public Color Original;
		}

		private static StaminaBar _bar;

		private static readonly List<FlashedImage> _main = new List<FlashedImage>(8);

		private static readonly List<FlashedImage> _extra = new List<FlashedImage>(8);

		internal static void Tick(bool flashMain, bool flashExtra, Color mainColor, Color extraColor, float speedHz, bool photosensitivity)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			StaminaBar val = (((Object)(object)GUIManager.instance != (Object)null) ? GUIManager.instance.bar : null);
			if ((Object)(object)val == (Object)null)
			{
				RestoreAll();
				return;
			}
			if ((Object)(object)val != (Object)(object)_bar)
			{
				Recache(val);
			}
			Apply(_main, flashMain, mainColor, speedHz, photosensitivity);
			Apply(_extra, flashExtra, extraColor, speedHz, photosensitivity);
		}

		internal static void RestoreAll()
		{
			Restore(_main);
			Restore(_extra);
			_main.Clear();
			_extra.Clear();
			_bar = null;
		}

		private static void Recache(StaminaBar bar)
		{
			RestoreAll();
			_bar = bar;
			if (!((Object)(object)bar == (Object)null))
			{
				Cache(bar.staminaBarOutline, _main);
				Cache(bar.staminaBarOutlineOverflowBar, _main);
				Cache(bar.extraBarOutline, _extra);
			}
		}

		private static void Cache(RectTransform root, List<FlashedImage> list)
		{
			//IL_004d: 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)
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true);
			FlashedImage item = default(FlashedImage);
			foreach (Image val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<BarAffliction>() != (Object)null))
				{
					item.Image = val;
					item.Original = ((Graphic)val).color;
					list.Add(item);
				}
			}
		}

		private static void Apply(List<FlashedImage> list, bool flash, Color flashColor, float speedHz, bool photosensitivity)
		{
			//IL_007b: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			float num = ((!flash) ? 0f : ((!photosensitivity) ? (0.5f + 0.5f * Mathf.Sin(Time.time * Mathf.Max(0.1f, speedHz) * (float)Math.PI * 2f)) : 0.55f));
			for (int i = 0; i < list.Count; i++)
			{
				Image image = list[i].Image;
				if (!((Object)(object)image == (Object)null))
				{
					((Graphic)image).color = Color.Lerp(list[i].Original, flashColor, num);
				}
			}
		}

		private static void Restore(List<FlashedImage> list)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < list.Count; i++)
			{
				Image image = list[i].Image;
				if ((Object)(object)image != (Object)null)
				{
					((Graphic)image).color = list[i].Original;
				}
			}
		}
	}
}