Decompiled source of PeakSoundtrackRemastered v0.1.5

PEAKsoundtrackRemastered.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Peak;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Zorro.Core;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace PeakSoundtrackRemastered;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("local.peaksoundtrackremastered", "PEAKsoundtrack remastered", "0.1.5")]
public sealed class Plugin : BaseUnityPlugin
{
	internal static Plugin Instance;

	private readonly ScoreRules rules = new ScoreRules();

	private readonly FallCue fallCue = new FallCue();

	private AudioSource meme;

	private float lastMeme = -100f;

	private float lastWarning = -100f;

	private readonly Dictionary<Track, float> resumeTimes = new Dictionary<Track, float>();

	private readonly Dictionary<Track, AudioClip> clips = new Dictionary<Track, AudioClip>();

	private readonly Dictionary<AudioSource, bool> muted = new Dictionary<AudioSource, bool>();

	private readonly HashSet<GhostBall> resolvedGhosts = new HashSet<GhostBall>();

	private readonly FieldInfo tornadoCaught = AccessTools.Field(typeof(Tornado), "caughtCharacters");

	private readonly FieldInfo ghostHealth = AccessTools.Field(typeof(GhostBall), "burnHealth");

	private readonly MethodInfo checkReached = AccessTools.Method(typeof(MountainProgressHandler), "CheckReached", (Type[])null, (Type[])null);

	private readonly MethodInfo spawnTornado = AccessTools.Method(typeof(TornadoSpawner), "SpawnTornado", (Type[])null, (Type[])null);

	private float nextTestSpawn;

	private float noticeUntil;

	private string notice = "";

	private ConfigEntry<float> volume;

	private ConfigEntry<float> fadeSeconds;

	private ConfigEntry<bool> enabledMusic;

	private ConfigEntry<bool> duckMusic;

	private AudioSource source;

	private AudioSource tail;

	private float tailStart;

	private float tailVolume;

	private Track tailTrack;

	private PropertyInfo afterglowProperty;

	private bool lastDead;

	private Harmony harmony;

	private Character tracked;

	private MapHandler trackedMap;

	private Track playing;

	private bool loaded;

	private bool fading;

	private bool failed;

	private float fadeStart;

	private float fadeVolume;

	private float nextPoll;

	private float nextDuck;

	private string root;

	private string region = "";

	private int dustCount;

	private int ghostCount;

	private void Awake()
	{
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Expected O, but got Unknown
		Instance = this;
		root = Path.GetDirectoryName(typeof(Plugin).Assembly.Location);
		enabledMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Music", "Enabled", true, "Enable this local soundtrack.");
		volume = ((BaseUnityPlugin)this).Config.Bind<float>("Music", "Volume", 0.65f, "Local soundtrack volume, 0 to 1.");
		fadeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Music", "FadeSeconds", 2f, "Fade after a flare, defeated ghost, departing biome, or leaving a dust devil.");
		duckMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Music", "QuietVanillaMusic", true, "Temporarily mute known vanilla music sources while a replacement track plays. Voice and game effects are untouched.");
		try
		{
			if (tornadoCaught == null || ghostHealth == null || checkReached == null)
			{
				throw new MissingMemberException("Required game event fields are missing.");
			}
			source = ((Component)this).gameObject.AddComponent<AudioSource>();
			source.playOnAwake = false;
			source.spatialBlend = 0f;
			source.priority = 64;
			tail = ((Component)this).gameObject.AddComponent<AudioSource>();
			tail.playOnAwake = false;
			tail.spatialBlend = 0f;
			tail.priority = 64;
			meme = ((Component)this).gameObject.AddComponent<AudioSource>();
			meme.playOnAwake = false;
			meme.spatialBlend = 0f;
			meme.priority = 60;
			SceneManager.sceneLoaded += SceneLoaded;
			harmony = new Harmony("local.peaksoundtrackremastered");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((MonoBehaviour)this).StartCoroutine(LoadTracks());
			Log("0.1.5 loaded. Local soundtrack; Lost Girl plays for a lone summit arrival.");
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private void Log(string message)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		try
		{
			File.AppendAllText(Path.Combine(root, "soundtrack-diagnostics.log"), DateTime.UtcNow.ToString("o") + " " + message + Environment.NewLine);
		}
		catch
		{
		}
	}

	private IEnumerator LoadTracks()
	{
		try
		{
			Track[] array = new Track[14]
			{
				Track.DustDevils,
				Track.Summit,
				Track.Nadir,
				Track.Ghost,
				Track.Citadel,
				Track.NadirAmbient,
				Track.Finale,
				Track.Credits,
				Track.Spectating,
				Track.Airport,
				Track.LoneSummit,
				Track.Falling,
				Track.Rising,
				Track.ScoutChase
			};
			foreach (Track track in array)
			{
				string path = Path.Combine(root, "assets/" + track.ToString() + ".wav");
				UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)20);
				try
				{
					((DownloadHandlerAudioClip)request.downloadHandler).streamAudio = true;
					yield return request.SendWebRequest();
					if ((int)request.result != 1)
					{
						Log(string.Concat("Cannot load ", track, ": ", request.error));
						continue;
					}
					AudioClip clip = DownloadHandlerAudioClip.GetContent(request);
					if ((Object)(object)clip == (Object)null || clip.length <= ScoreRules.StartSeconds(track) + 0.1f)
					{
						Log("Track missing or too short for offset: " + track);
						if ((Object)(object)clip != (Object)null)
						{
							Object.Destroy((Object)(object)clip);
						}
						continue;
					}
					clips[track] = clip;
					Log(string.Concat("Loaded ", track, ", ", clip.length.ToString("F1"), "s, starts at ", ScoreRules.StartSeconds(track), "s."));
				}
				finally
				{
					((IDisposable)request)?.Dispose();
				}
			}
		}
		finally
		{
		}
		loaded = true;
	}

	private bool LocalAlive(Character c)
	{
		if ((Object)(object)c != (Object)null && (Object)(object)c.data != (Object)null && c.IsLocal && (Object)(object)((MonoBehaviourPun)c).photonView != (Object)null && ((MonoBehaviourPun)c).photonView.IsMine && !c.data.dead)
		{
			return !c.isBot;
		}
		return false;
	}

	private void Update()
	{
		if (failed)
		{
			return;
		}
		try
		{
			CheckTestKey();
			if (!enabledMusic.Value)
			{
				StopAll();
				fallCue.Reset();
				rules.RisingWarning = false;
				return;
			}
			UpdateFallCue();
			if (Time.unscaledTime >= nextPoll)
			{
				nextPoll = Time.unscaledTime + 0.25f;
				Poll();
			}
			UpdateTail();
			if ((Object)(object)source == (Object)null || playing == Track.None)
			{
				if ((Object)(object)tail != (Object)null && tail.isPlaying && duckMusic.Value)
				{
					QuietVanilla();
				}
				return;
			}
			if (fading)
			{
				source.volume = fadeVolume * ScoreRules.FadeGain(Time.unscaledTime - fadeStart, Mathf.Max(0.05f, fadeSeconds.Value));
				if (source.volume <= 0.0001f)
				{
					StopNow();
					return;
				}
			}
			else
			{
				source.volume = Mathf.Clamp01(volume.Value);
			}
			if (!source.isPlaying && !AudioListener.pause)
			{
				rules.Finished(playing);
				resumeTimes.Remove(playing);
				StopNow();
				nextPoll = 0f;
			}
			else if (!duckMusic.Value)
			{
				RestoreVanilla();
			}
			else if (Time.unscaledTime >= nextDuck)
			{
				nextDuck = Time.unscaledTime + 0.5f;
				QuietVanilla();
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private void Notice(string text)
	{
		notice = text;
		noticeUntil = Time.unscaledTime + 4f;
		Log(text);
	}

	private void CheckTestKey()
	{
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		Keyboard current = Keyboard.current;
		if (current == null || !Application.isFocused || (!((ButtonControl)current.digit6Key).wasPressedThisFrame && !((ButtonControl)current.numpad6Key).wasPressedThisFrame) || (Object)(object)GUIManager.instance == (Object)null || GUIManager.instance.windowBlockingInput || GUIManager.instance.wheelActive)
		{
			return;
		}
		Character localCharacter = Character.localCharacter;
		MapHandler instance = Singleton<MapHandler>.Instance;
		if (!PhotonNetwork.InRoom || !LocalAlive(localCharacter) || !localCharacter.data.fullyConscious || (Object)(object)instance == (Object)null)
		{
			Notice("Dust-devil test: enter a round as a conscious scout.");
		}
		else if (!PhotonNetwork.IsMasterClient)
		{
			Notice("Dust-devil test: only the host can spawn one.");
		}
		else if (CurrentRegion(localCharacter, instance) != "MESA")
		{
			Notice("Dust-devil test: you must be in Mesa.");
		}
		else
		{
			if (Time.unscaledTime < nextTestSpawn)
			{
				return;
			}
			TornadoSpawner val = null;
			float num = float.PositiveInfinity;
			TornadoSpawner[] array = Object.FindObjectsByType<TornadoSpawner>((FindObjectsSortMode)0);
			foreach (TornadoSpawner val2 in array)
			{
				if ((Object)(object)val2 == (Object)null || !((Behaviour)val2).isActiveAndEnabled)
				{
					continue;
				}
				PhotonView component = ((Component)val2).GetComponent<PhotonView>();
				Transform val3 = ((Component)val2).transform.Find("TornadoPoints");
				if (!((Object)(object)component == (Object)null) && component.ViewID != 0 && !((Object)(object)val3 == (Object)null) && val3.childCount != 0)
				{
					Vector3 val4 = ((Component)val2).transform.position - localCharacter.Center;
					float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						val = val2;
					}
				}
			}
			if (!((Object)(object)val == (Object)null) && !(spawnTornado == null))
			{
				nextTestSpawn = Time.unscaledTime + 1f;
				try
				{
					spawnTornado.Invoke(val, null);
					nextPoll = 0f;
					Notice("Dust devil spawned at a vanilla Mesa spawn point. Everyone can see it.");
					return;
				}
				catch (Exception ex)
				{
					Notice("Dust-devil test failed: " + ex.GetBaseException().Message);
					return;
				}
			}
			Notice("Dust-devil test: no active Mesa spawner with valid spawn points.");
		}
	}

	private void OnGUI()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		if (!(Time.unscaledTime >= noticeUntil))
		{
			GUI.Box(new Rect((float)Screen.width / 2f - 300f, 35f, 600f, 60f), notice);
		}
	}

	private string CurrentRegion(Character c, MapHandler map)
	{
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Invalid comparison between Unknown and I4
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Invalid comparison between Unknown and I4
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		if (VoidBiome.VoidBiomeActive)
		{
			return "NADIR";
		}
		MountainProgressHandler instance = Singleton<MountainProgressHandler>.Instance;
		if ((Object)(object)instance != (Object)null && instance.progressPoints != null)
		{
			for (int num = instance.progressPoints.Length - 1; num >= 0; num--)
			{
				ProgressPoint val = instance.progressPoints[num];
				if (val != null && (Object)(object)val.transform != (Object)null && (bool)checkReached.Invoke(instance, new object[1] { val }))
				{
					return (val.title ?? "").Trim().ToUpperInvariant();
				}
			}
		}
		if ((Object)(object)map == (Object)null)
		{
			return "";
		}
		BiomeType currentBiome = map.GetCurrentBiome();
		if ((int)currentBiome == 17)
		{
			return "NADIR";
		}
		if ((int)currentBiome == 8)
		{
			return "GLOOM";
		}
		return ((object)currentBiome).ToString().ToUpperInvariant();
	}

	private void Poll()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0314: Unknown result type (might be due to invalid IL or missing references)
		//IL_031a: Unknown result type (might be due to invalid IL or missing references)
		Character localCharacter = Character.localCharacter;
		MapHandler instance = Singleton<MapHandler>.Instance;
		Scene activeScene = SceneManager.GetActiveScene();
		if (((Scene)(ref activeScene)).name == "Airport")
		{
			if (region != "AIRPORT")
			{
				ResetSession();
				region = "AIRPORT";
			}
			if (loaded)
			{
				Apply(rules.Select("AIRPORT", alive: true, CountCaptured(localCharacter), 0));
			}
			return;
		}
		if (region == "AIRPORT")
		{
			ResetSession();
		}
		if (!PhotonNetwork.InRoom)
		{
			ResetSession();
			return;
		}
		if (rules.InCredits || rules.FinalCutscene)
		{
			if (loaded)
			{
				Apply(rules.Select(region, alive: true, 0, 0));
			}
			return;
		}
		if ((Object)(object)localCharacter == (Object)null || (Object)(object)instance == (Object)null)
		{
			ResetSession();
			return;
		}
		if ((Object)(object)tracked != (Object)(object)localCharacter || (Object)(object)trackedMap != (Object)(object)instance)
		{
			ResetSession();
			tracked = localCharacter;
			trackedMap = instance;
		}
		if (!loaded || (Object)(object)localCharacter.data == (Object)null)
		{
			return;
		}
		bool dead = localCharacter.data.dead;
		if (dead != lastDead)
		{
			lastDead = dead;
			resumeTimes.Clear();
			if (dead)
			{
				Log("Waiting for AFTERGLOW and spectating.");
			}
		}
		if (dead)
		{
			meme.Stop();
			Apply(rules.Select(region, alive: false, 0, 0, AfterglowShowing(), localCharacter.data.deathTimer >= 5f));
			return;
		}
		if (!LocalAlive(localCharacter))
		{
			BeginFade();
			return;
		}
		string text = CurrentRegion(localCharacter, instance);
		if (region != text)
		{
			resumeTimes.Clear();
			region = text;
			Log("Local region: " + region);
		}
		dustCount = 0;
		ghostCount = 0;
		resolvedGhosts.RemoveWhere((GhostBall g) => (Object)(object)g == (Object)null);
		dustCount = CountCaptured(localCharacter);
		if (region == "GLOOM")
		{
			GhostBall[] array = Object.FindObjectsByType<GhostBall>((FindObjectsSortMode)0);
			foreach (GhostBall val in array)
			{
				if ((Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled && !resolvedGhosts.Contains(val) && (float)ghostHealth.GetValue(val) > 0f)
				{
					ghostCount++;
				}
			}
		}
		bool scoutPresent = rules.ScoutPresent;
		rules.ScoutPresent = false;
		Scoutmaster[] array2 = Object.FindObjectsByType<Scoutmaster>((FindObjectsSortMode)0);
		foreach (Scoutmaster val2 in array2)
		{
			if ((Object)(object)val2 != (Object)null && ((Behaviour)val2).isActiveAndEnabled && (Object)(object)val2.currentTarget == (Object)(object)localCharacter && Vector3.Distance(((Component)val2).transform.position, localCharacter.Center) < 50f)
			{
				rules.ScoutPresent = true;
				break;
			}
		}
		if (scoutPresent && !rules.ScoutPresent)
		{
			resumeTimes.Remove(Track.ScoutChase);
		}
		Apply(rules.Select(region, alive: true, dustCount, ghostCount, afterglow: false, spectating: false, region == "PEAK" && AloneAmongSurvivors(localCharacter)));
	}

	private void UpdateFallCue()
	{
		Character localCharacter = Character.localCharacter;
		bool flag = PhotonNetwork.InRoom && LocalAlive(localCharacter) && !localCharacter.warping && !localCharacter.data.isClimbingAnything && !localCharacter.data.isInWater && !localCharacter.data.isCarried;
		if (fallCue.Step(flag, !flag || localCharacter.data.isGrounded, flag ? localCharacter.data.avarageVelocity.y : 0f, Time.deltaTime))
		{
			PlayMeme();
		}
		if ((Object)(object)meme != (Object)null)
		{
			meme.volume = Mathf.Clamp01(volume.Value);
			if (!flag && !LocalAlive(localCharacter))
			{
				meme.Stop();
			}
		}
	}

	internal void PlayMeme()
	{
		if (!failed && enabledMusic.Value && loaded && PhotonNetwork.InRoom && LocalAlive(Character.localCharacter) && !rules.FinalCutscene && !rules.InCredits && clips.TryGetValue(Track.Falling, out var value) && !meme.isPlaying && !(Time.unscaledTime - lastMeme < 1f))
		{
			lastMeme = Time.unscaledTime;
			meme.clip = value;
			meme.loop = false;
			meme.volume = Mathf.Clamp01(volume.Value);
			meme.Play();
			Log("Let Me Know triggered.");
		}
	}

	internal void BugleUsed(Action_CallScoutmaster action)
	{
		Item val = (((Object)(object)action == (Object)null) ? null : ((Component)action).GetComponent<Item>());
		if ((Object)(object)val != (Object)null && (Object)(object)val.holderCharacter == (Object)(object)Character.localCharacter)
		{
			PlayMeme();
		}
	}

	internal void HazardWarning()
	{
		if (!failed && enabledMusic.Value && PhotonNetwork.InRoom && LocalAlive(Character.localCharacter) && !rules.FinalCutscene && !rules.InCredits && !(Time.unscaledTime - lastWarning < 5f))
		{
			lastWarning = Time.unscaledTime;
			rules.RisingWarning = true;
			nextPoll = 0f;
			Log("Rising-hazard warning received.");
		}
	}

	private bool AloneAmongSurvivors(Character local)
	{
		if (!LocalAlive(local) || !PhotonNetwork.InRoom)
		{
			return false;
		}
		Player[] playerList = PhotonNetwork.PlayerList;
		foreach (Player val in playerList)
		{
			if (val == null || val.IsLocal || val.IsInactive)
			{
				continue;
			}
			bool flag = false;
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.isBot && !((Object)(object)((MonoBehaviourPun)allCharacter).photonView == (Object)null) && ((MonoBehaviourPun)allCharacter).photonView.OwnerActorNr == val.ActorNumber)
				{
					flag = true;
					if (!allCharacter.IsInitialized || (Object)(object)allCharacter.data == (Object)null || !allCharacter.data.dead)
					{
						return false;
					}
				}
			}
			if (!flag)
			{
				return false;
			}
		}
		return true;
	}

	private int CountCaptured(Character c)
	{
		if (!LocalAlive(c))
		{
			return 0;
		}
		int num = 0;
		Tornado[] array = Object.FindObjectsByType<Tornado>((FindObjectsSortMode)0);
		foreach (Tornado val in array)
		{
			if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled && tornadoCaught.GetValue(val) is List<Character> list && list.Contains(c))
			{
				num++;
			}
		}
		return num;
	}

	private void Apply(Track wanted)
	{
		if (wanted == Track.None)
		{
			BeginFade();
		}
		else if (wanted == playing)
		{
			if (fading)
			{
				fading = false;
				source.volume = Mathf.Clamp01(volume.Value);
			}
		}
		else
		{
			Play(wanted);
		}
	}

	private void Play(Track track)
	{
		if (clips.TryGetValue(track, out var value))
		{
			tail.Stop();
			if (source.isPlaying)
			{
				tail.clip = source.clip;
				tail.loop = source.loop;
				tail.time = source.time;
				tail.volume = source.volume;
				tailVolume = source.volume;
				tailStart = Time.unscaledTime;
				tailTrack = playing;
				tail.Play();
			}
			if ((track == Track.Rising || track == Track.ScoutChase) && playing != Track.None && playing != Track.ScoutChase && source.isPlaying && !fading)
			{
				resumeTimes[playing] = source.time;
			}
			source.Stop();
			fading = false;
			playing = track;
			source.clip = value;
			source.loop = ScoreRules.Loops(track);
			source.volume = Mathf.Clamp01(volume.Value);
			source.time = ((resumeTimes.TryGetValue(track, out var value2) && value2 < value.length - 0.1f) ? value2 : ScoreRules.StartSeconds(track));
			source.Play();
			nextDuck = 0f;
			Log(string.Concat("Playing ", track, " from ", ScoreRules.StartSeconds(track), "s."));
		}
	}

	private void BeginFade()
	{
		if (playing != Track.None && !fading)
		{
			fading = true;
			fadeStart = Time.unscaledTime;
			fadeVolume = source.volume;
			Log(string.Concat("Fading ", playing, "."));
		}
	}

	private void StopNow()
	{
		if ((Object)(object)source != (Object)null)
		{
			source.Stop();
		}
		playing = Track.None;
		fading = false;
		if ((Object)(object)tail == (Object)null || !tail.isPlaying)
		{
			RestoreVanilla();
		}
	}

	private void StopAll()
	{
		if ((Object)(object)meme != (Object)null)
		{
			meme.Stop();
		}
		if ((Object)(object)tail != (Object)null)
		{
			tail.Stop();
		}
		StopNow();
	}

	private void UpdateTail()
	{
		if ((Object)(object)tail == (Object)null || !tail.isPlaying)
		{
			return;
		}
		tail.volume = tailVolume * ScoreRules.FadeGain(Time.unscaledTime - tailStart, Mathf.Max(0.05f, fadeSeconds.Value));
		if (tail.volume <= 0.0001f)
		{
			tail.Stop();
			if (playing == Track.None)
			{
				RestoreVanilla();
			}
		}
	}

	private void SceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		if ((int)mode == 0)
		{
			ResetSession();
		}
	}

	private bool AfterglowShowing()
	{
		if (!Chainloader.PluginInfos.TryGetValue("local.entitiesplus", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			return false;
		}
		if (afterglowProperty == null)
		{
			afterglowProperty = AccessTools.Property(((object)value.Instance).GetType(), "DeathShowing");
		}
		if (afterglowProperty != null)
		{
			return (bool)afterglowProperty.GetValue(null, null);
		}
		return false;
	}

	internal void EndingEvent(int stage)
	{
		if (failed || !PhotonNetwork.InRoom)
		{
			return;
		}
		if (stage == 1)
		{
			rules.NadirWon = true;
			Log("Nadir completed; fading its music.");
			if (playing == Track.Nadir || playing == Track.NadirAmbient)
			{
				BeginFade();
			}
		}
		if (stage == 2)
		{
			rules.NadirWon = true;
			rules.FinalCutscene = true;
			Log("Post-Nadir final cutscene.");
		}
		if (stage == 3)
		{
			rules.InCredits = true;
			Log("Credits started.");
		}
		nextPoll = 0f;
	}

	private void ResetSession()
	{
		StopAll();
		fallCue.Reset();
		resumeTimes.Clear();
		lastMeme = (lastWarning = -100f);
		lastDead = false;
		rules.Reset();
		tracked = null;
		trackedMap = null;
		region = "";
		resolvedGhosts.Clear();
		dustCount = (ghostCount = 0);
	}

	internal void GhostEnded(GhostBall ghost)
	{
		if (!failed && !((Object)(object)ghost == (Object)null))
		{
			resolvedGhosts.Add(ghost);
			nextPoll = 0f;
		}
	}

	internal void FlareLit()
	{
		if (!failed && LocalAlive(Character.localCharacter) && (playing == Track.Summit || playing == Track.LoneSummit || region == "PEAK"))
		{
			rules.FlareLit();
			if (playing == Track.Summit || playing == Track.LoneSummit)
			{
				BeginFade();
			}
		}
	}

	private void Quiet(AudioSource audio)
	{
		if (!((Object)(object)audio == (Object)null) && !((Object)(object)audio == (Object)(object)source) && !((Object)(object)audio == (Object)(object)tail) && !((Object)(object)audio == (Object)(object)meme))
		{
			if (!muted.ContainsKey(audio))
			{
				muted[audio] = audio.mute;
			}
			audio.mute = true;
		}
	}

	private void QuietRoot(GameObject obj)
	{
		if ((Object)(object)obj != (Object)null)
		{
			AudioSource[] componentsInChildren = obj.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource audio in componentsInChildren)
			{
				Quiet(audio);
			}
		}
	}

	private void QuietVanilla()
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		if (region == "AIRPORT")
		{
			AudioSource[] array = Object.FindObjectsByType<AudioSource>((FindObjectsSortMode)0);
			foreach (AudioSource val in array)
			{
				Scene scene = ((Component)val).gameObject.scene;
				if (((Scene)(ref scene)).name == "Airport" && (Object)(object)val.outputAudioMixerGroup != (Object)null && ((Object)val.outputAudioMixerGroup).name.IndexOf("music", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Quiet(val);
				}
			}
		}
		AmbienceAudio[] array2 = Object.FindObjectsByType<AmbienceAudio>((FindObjectsSortMode)0);
		foreach (AmbienceAudio val2 in array2)
		{
			Quiet(val2.mainMusic);
		}
		BiomeMusicCheck[] array3 = Object.FindObjectsByType<BiomeMusicCheck>((FindObjectsSortMode)0);
		foreach (BiomeMusicCheck val3 in array3)
		{
			QuietRoot(val3.regularMusic);
			QuietRoot(val3.mesaMusic);
		}
		MyresAmbience[] array4 = Object.FindObjectsByType<MyresAmbience>((FindObjectsSortMode)0);
		foreach (MyresAmbience val4 in array4)
		{
			Quiet(val4.fearMusic);
		}
		if (region == "NADIR" && !rules.NadirWon)
		{
			ScoutmasterSoulPillar[] array5 = Object.FindObjectsByType<ScoutmasterSoulPillar>((FindObjectsSortMode)0);
			foreach (ScoutmasterSoulPillar val5 in array5)
			{
				Quiet(val5.music);
			}
		}
		PeakHandler instance = Singleton<PeakHandler>.Instance;
		if ((Object)(object)instance != (Object)null && (rules.FinalCutscene || rules.InCredits))
		{
			QuietCutsceneMusic(instance.endCutscene);
			QuietCutsceneMusic(instance.endCutsceneFinal);
			QuietCutsceneMusic(instance.endCutsceneFinalSpecialDay);
		}
		Type type = AccessTools.TypeByName("DeathTunes.DeathTunesPlugin");
		PropertyInfo propertyInfo = ((type == null) ? null : AccessTools.Property(type, "AudioSource"));
		if (propertyInfo != null && propertyInfo.GetGetMethod(nonPublic: true) != null && propertyInfo.GetGetMethod(nonPublic: true).IsStatic)
		{
			object? value = propertyInfo.GetValue(null, null);
			AudioSource val6 = (AudioSource)((value is AudioSource) ? value : null);
			if (playing == Track.Spectating || ((Object)(object)tail != (Object)null && tail.isPlaying && tailTrack == Track.Spectating))
			{
				Quiet(val6);
			}
			else if ((Object)(object)val6 != (Object)null && muted.ContainsKey(val6))
			{
				val6.mute = muted[val6];
				muted.Remove(val6);
			}
		}
	}

	private void QuietCutsceneMusic(GameObject obj)
	{
		if ((Object)(object)obj == (Object)null)
		{
			return;
		}
		AudioSource[] componentsInChildren = obj.GetComponentsInChildren<AudioSource>(true);
		foreach (AudioSource val in componentsInChildren)
		{
			if ((Object)(object)val.outputAudioMixerGroup != (Object)null && ((Object)val.outputAudioMixerGroup).name.IndexOf("music", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				Quiet(val);
			}
		}
	}

	private void RestoreVanilla()
	{
		foreach (KeyValuePair<AudioSource, bool> item in muted)
		{
			if ((Object)(object)item.Key != (Object)null)
			{
				item.Key.mute = item.Value;
			}
		}
		muted.Clear();
	}

	private void Fail(Exception error)
	{
		Log("Soundtrack disabled after error: " + error);
		failed = true;
		StopAll();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	private void OnDisable()
	{
		StopAll();
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= SceneLoaded;
		StopAll();
		((MonoBehaviour)this).StopAllCoroutines();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		foreach (AudioClip value in clips.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		if ((Object)(object)source != (Object)null)
		{
			Object.Destroy((Object)(object)source);
		}
		if ((Object)(object)tail != (Object)null)
		{
			Object.Destroy((Object)(object)tail);
		}
		if ((Object)(object)meme != (Object)null)
		{
			Object.Destroy((Object)(object)meme);
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}
}
[HarmonyPatch(typeof(Action_CallScoutmaster), "RunAction")]
internal static class BugleCue
{
	private static void Postfix(Action_CallScoutmaster __instance)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.BugleUsed(__instance);
		}
	}
}
[HarmonyPatch(typeof(GUIManager), "TheFogRises")]
internal static class FogWarningCue
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.HazardWarning();
		}
	}
}
[HarmonyPatch(typeof(GUIManager), "TheGloomRises")]
internal static class GloomWarningCue
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.HazardWarning();
		}
	}
}
[HarmonyPatch(typeof(GUIManager), "TheLavaRises")]
internal static class LavaWarningCue
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.HazardWarning();
		}
	}
}
[HarmonyPatch(typeof(GhostBall), "Explode")]
internal static class GhostExplosion
{
	private static void Postfix(GhostBall __instance)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.GhostEnded(__instance);
		}
	}
}
[HarmonyPatch(typeof(GhostBall), "OnDestroy")]
internal static class GhostDestroyed
{
	private static void Prefix(GhostBall __instance)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.GhostEnded(__instance);
		}
	}
}
[HarmonyPatch(typeof(Flare), "LightFlare")]
internal static class FlareIgnition
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.FlareLit();
		}
	}
}
[HarmonyPatch(typeof(Flare), "EnableFlareVisuals")]
internal static class SyncedFlareIgnition
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.FlareLit();
		}
	}
}
[HarmonyPatch(typeof(GlobalEvents), "TriggerSoulFreed")]
internal static class NadirVictory
{
	private static void Postfix(int __0)
	{
		if (__0 >= 1 && (Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.EndingEvent(1);
		}
	}
}
[HarmonyPatch(typeof(PeakHandler), "EndCutsceneFinal")]
internal static class NadirFinale
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.EndingEvent(2);
		}
	}
}
[HarmonyPatch(typeof(PeakHandler), "EndScreenComplete")]
internal static class CreditsStart
{
	private static void Postfix()
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.EndingEvent(3);
		}
	}
}
public enum Track
{
	None,
	DustDevils,
	Summit,
	Nadir,
	Ghost,
	Citadel,
	NadirAmbient,
	Finale,
	Credits,
	Spectating,
	Airport,
	LoneSummit,
	Falling,
	Rising,
	ScoutChase
}
public sealed class FallCue
{
	private float descending;

	private bool fired;

	public void Reset()
	{
		descending = 0f;
		fired = false;
	}

	public bool Step(bool eligible, bool grounded, float velocityY, float delta)
	{
		if (!eligible || grounded)
		{
			Reset();
			return false;
		}
		if (velocityY > -5f)
		{
			descending = 0f;
			return false;
		}
		descending += Math.Max(0f, Math.Min(delta, 0.1f));
		if (fired || descending < 1f)
		{
			return false;
		}
		fired = true;
		return true;
	}
}
public sealed class ScoreRules
{
	private string previous = "";

	private bool summitFinished;

	private bool nadirFinished;

	private bool summitChosen;

	private bool loneSummit;

	public bool NadirWon;

	public bool FinalCutscene;

	public bool InCredits;

	public bool RisingWarning;

	public bool ScoutPresent;

	public void Reset()
	{
		previous = "";
		RisingWarning = (ScoutPresent = false);
		summitChosen = (loneSummit = (summitFinished = (nadirFinished = (NadirWon = (FinalCutscene = (InCredits = false))))));
	}

	public Track Select(string region, bool alive, int dustDevils, int ghosts)
	{
		return Select(region, alive, dustDevils, ghosts, afterglow: false, spectating: false);
	}

	public Track Select(string region, bool alive, int dustDevils, int ghosts, bool afterglow, bool spectating)
	{
		return Select(region, alive, dustDevils, ghosts, afterglow, spectating, alone: false);
	}

	public Track Select(string region, bool alive, int dustDevils, int ghosts, bool afterglow, bool spectating, bool alone)
	{
		region = (region ?? "").Trim().ToUpperInvariant();
		if (region != previous)
		{
			if (region == "PEAK")
			{
				summitFinished = false;
				summitChosen = false;
			}
			if (region == "NADIR")
			{
				nadirFinished = false;
			}
			previous = region;
		}
		if (InCredits)
		{
			return Track.Credits;
		}
		if (FinalCutscene)
		{
			return Track.Finale;
		}
		if (!alive)
		{
			RisingWarning = (ScoutPresent = false);
			if (afterglow || !spectating)
			{
				return Track.None;
			}
			return Track.Spectating;
		}
		if (region == "PEAK" && !summitChosen)
		{
			summitChosen = true;
			loneSummit = alone;
		}
		if (ScoutPresent)
		{
			return Track.ScoutChase;
		}
		if (RisingWarning)
		{
			return Track.Rising;
		}
		if (dustDevils > 0)
		{
			return Track.DustDevils;
		}
		if (region == "AIRPORT")
		{
			return Track.Airport;
		}
		if (region == "GLOOM" && ghosts > 0)
		{
			return Track.Ghost;
		}
		if (region == "NADIR" && !NadirWon)
		{
			if (!nadirFinished)
			{
				return Track.Nadir;
			}
			return Track.NadirAmbient;
		}
		switch (region)
		{
		case "THE CITADEL":
		case "CITADEL":
			return Track.Citadel;
		case "PEAK":
			if (!summitFinished)
			{
				if (!loneSummit)
				{
					return Track.Summit;
				}
				return Track.LoneSummit;
			}
			break;
		}
		return Track.None;
	}

	public void Finished(Track track)
	{
		if (track == Track.Rising)
		{
			RisingWarning = false;
		}
		if (track == Track.Summit || track == Track.LoneSummit)
		{
			summitFinished = true;
		}
		if (track == Track.Nadir)
		{
			nadirFinished = true;
		}
	}

	public void FlareLit()
	{
		summitFinished = true;
	}

	public static float StartSeconds(Track track)
	{
		return track switch
		{
			Track.Nadir => 26f, 
			Track.Summit => 181f, 
			_ => 0f, 
		};
	}

	public static bool Loops(Track track)
	{
		if (track != Track.ScoutChase && track != Track.Airport && track != Track.DustDevils && track != Track.Ghost && track != Track.Citadel && track != Track.NadirAmbient && track != Track.Finale && track != Track.Credits)
		{
			return track == Track.Spectating;
		}
		return true;
	}

	public static float FadeGain(float age, float duration)
	{
		if (!(duration <= 0f))
		{
			return Math.Max(0f, Math.Min(1f, 1f - age / duration));
		}
		return 0f;
	}
}