Decompiled source of TouhouMeltdownBGM v1.0.7

plugins/TouhouMeltdownBGM/TouhouMeltdownBGM.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace TouhouMeltdownBGM;

[BepInPlugin("com.cutyimodo.touhounuclearmeltdownbgm", "Touhou Meltdown BGM", "1.0.7")]
public class Plugin : BaseUnityPlugin
{
	[CompilerGenerated]
	private sealed class <FadeOutAndStop>d__19 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public float duration;

		public Plugin <>4__this;

		private float <startVol>5__1;

		private float <elapsed>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <FadeOutAndStop>d__19(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (!isPlaying || audioSource == null)
				{
					return false;
				}
				isPlaying = false;
				<startVol>5__1 = audioSource.volume;
				<elapsed>5__2 = 0f;
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			if (<elapsed>5__2 < duration)
			{
				<elapsed>5__2 += Time.deltaTime;
				if (audioSource != null)
				{
					audioSource.volume = Mathf.Lerp(<startVol>5__1, 0f, <elapsed>5__2 / duration);
				}
				<>2__current = null;
				<>1__state = 1;
				return true;
			}
			if (audioSource != null)
			{
				audioSource.Stop();
				audioSource.volume = <startVol>5__1;
			}
			((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Meltdown music stopped");
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <PlayMusic>d__18 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Plugin <>4__this;

		private float <timeout>5__1;

		private float <waited>5__2;

		private float <elapsed>5__3;

		private float <targetVol>5__4;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <PlayMusic>d__18(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Invalid comparison between Unknown and I4
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (isPlaying)
				{
					return false;
				}
				<timeout>5__1 = 10f;
				<waited>5__2 = 0f;
				goto IL_007c;
			case 1:
				<>1__state = -1;
				goto IL_007c;
			case 2:
				{
					<>1__state = -1;
					break;
				}
				IL_007c:
				if ((meltdownClip == null || (int)meltdownClip.loadState == 1) && <waited>5__2 < <timeout>5__1)
				{
					<waited>5__2 += Time.deltaTime;
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if (meltdownClip == null || (int)meltdownClip.loadState != 2)
				{
					((BaseUnityPlugin)<>4__this).Logger.LogError((object)$"Cannot play: audio clip not loaded (clip null: {meltdownClip == null})");
					return false;
				}
				if (audioSource == null)
				{
					audioSource = ((Component)<>4__this).gameObject.AddComponent<AudioSource>();
					audioSource.spatialBlend = 0f;
					audioSource.loop = true;
					audioSource.playOnAwake = false;
					audioSource.priority = 0;
				}
				audioSource.clip = meltdownClip;
				audioSource.volume = 0f;
				audioSource.Play();
				isPlaying = true;
				<>4__this.SuppressOtherAudio();
				((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"Playing meltdown music! (clip: {meltdownClip.length}s, {meltdownClip.channels}ch)");
				<elapsed>5__3 = 0f;
				<targetVol>5__4 = volumeConfig.Value;
				break;
			}
			if (<elapsed>5__3 < 1f)
			{
				<elapsed>5__3 += Time.deltaTime;
				if (audioSource != null)
				{
					audioSource.volume = Mathf.Lerp(0f, <targetVol>5__4, <elapsed>5__3 / 1f);
				}
				<>2__current = null;
				<>1__state = 2;
				return true;
			}
			if (audioSource != null)
			{
				audioSource.volume = <targetVol>5__4;
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <PreloadAudio>d__13 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Plugin <>4__this;

		private string <fileUri>5__1;

		private float <timeout>5__2;

		private float <waited>5__3;

		private UnityWebRequest <request>5__4;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <PreloadAudio>d__13(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			int num = <>1__state;
			if (num == -3 || num == 1)
			{
				try
				{
				}
				finally
				{
					<>m__Finally1();
				}
			}
			<fileUri>5__1 = null;
			<request>5__4 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Invalid comparison between Unknown and I4
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Invalid comparison between Unknown and I4
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			bool result;
			try
			{
				switch (<>1__state)
				{
				default:
					result = false;
					goto end_IL_0000;
				case 0:
					<>1__state = -1;
					<fileUri>5__1 = "file:///" + <>4__this.musicPath.Replace("\\", "/");
					<request>5__4 = UnityWebRequest.Get(<fileUri>5__1);
					<>1__state = -3;
					<request>5__4.downloadHandler = (DownloadHandler)new DownloadHandlerAudioClip(<fileUri>5__1, (AudioType)13);
					<>2__current = <request>5__4.SendWebRequest();
					<>1__state = 1;
					result = true;
					goto end_IL_0000;
				case 1:
					<>1__state = -3;
					if (<request>5__4.isNetworkError || <request>5__4.isHttpError)
					{
						((BaseUnityPlugin)<>4__this).Logger.LogError((object)("Failed to preload audio: " + <request>5__4.error));
						result = false;
						<>m__Finally1();
					}
					else
					{
						meltdownClip = DownloadHandlerAudioClip.GetContent(<request>5__4);
						<>m__Finally1();
						<request>5__4 = null;
						if (meltdownClip != null)
						{
							<timeout>5__2 = 5f;
							<waited>5__3 = 0f;
							break;
						}
						((BaseUnityPlugin)<>4__this).Logger.LogError((object)"Audio clip is null after preload");
						result = false;
					}
					goto end_IL_0000;
				case 2:
					<>1__state = -1;
					break;
				}
				if ((int)meltdownClip.loadState == 1 && <waited>5__3 < <timeout>5__2)
				{
					<waited>5__3 += Time.deltaTime;
					<>2__current = null;
					<>1__state = 2;
					result = true;
				}
				else
				{
					if ((int)meltdownClip.loadState == 2)
					{
						((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"Preloaded NuclearFusion.mp3 ({meltdownClip.length}s, {meltdownClip.channels}ch)");
					}
					else
					{
						((BaseUnityPlugin)<>4__this).Logger.LogError((object)$"Audio preload failed, state: {meltdownClip.loadState}");
					}
					result = false;
				}
				end_IL_0000:;
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
			return result;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		private void <>m__Finally1()
		{
			<>1__state = -1;
			if (<request>5__4 != null)
			{
				((IDisposable)<request>5__4).Dispose();
			}
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	internal static Plugin instance;

	internal static AudioClip meltdownClip;

	internal static AudioSource audioSource;

	internal static bool isPlaying;

	internal static bool wasTriggered;

	private bool wasInOrbit = true;

	internal static ConfigEntry<float> volumeConfig;

	private string musicPath;

	private Harmony harmony;

	private float suppressCheckTimer;

	private const float SUPPRESS_INTERVAL = 0.5f;

	private void Awake()
	{
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Expected O, but got Unknown
		instance = this;
		volumeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.8f, "Music volume (0.0 - 1.0)");
		string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		musicPath = Path.Combine(directoryName, "NuclearFusion.mp3");
		if (File.Exists(musicPath))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Found music file at " + musicPath));
			((MonoBehaviour)this).StartCoroutine(PreloadAudio());
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Music file not found at " + musicPath));
		}
		harmony = new Harmony("com.cutyimodo.touhounuclearmeltdownbgm");
		PatchLungProp();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Touhou Meltdown BGM v1.0.7 loaded! (Harmony patch mode)");
	}

	private void PatchLungProp()
	{
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		Type type = null;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			type = assembly.GetType("LungProp");
			if (type != null)
			{
				break;
			}
		}
		if (type == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"LungProp type not found! Cannot patch.");
			return;
		}
		MethodInfo method = type.GetMethod("EquipItem", BindingFlags.Instance | BindingFlags.Public);
		if (method == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"LungProp.EquipItem method not found!");
			return;
		}
		MethodInfo method2 = typeof(LungPropPatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public);
		harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched LungProp.EquipItem successfully!");
	}

	[IteratorStateMachine(typeof(<PreloadAudio>d__13))]
	private IEnumerator PreloadAudio()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <PreloadAudio>d__13(0)
		{
			<>4__this = this
		};
	}

	internal void TriggerMeltdownMusic()
	{
		if (!wasTriggered)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Apparatus pulled! Starting meltdown music...");
			wasTriggered = true;
			((MonoBehaviour)this).StartCoroutine(PlayMusic());
		}
	}

	private void Update()
	{
		if (GetIsDisconnecting() && (isPlaying || wasTriggered))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Disconnecting detected, stopping music.");
			if (audioSource != null)
			{
				audioSource.Stop();
			}
			isPlaying = false;
			wasTriggered = false;
			wasInOrbit = true;
			return;
		}
		if (isPlaying && audioSource != null)
		{
			object startOfRound = GetStartOfRound();
			if (startOfRound == null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"StartOfRound gone, stopping music.");
				audioSource.Stop();
				isPlaying = false;
				wasTriggered = false;
				return;
			}
		}
		object startOfRound2 = GetStartOfRound();
		if (startOfRound2 != null)
		{
			if (!GetBool(startOfRound2, "shipHasLanded"))
			{
				if (!wasInOrbit && wasTriggered)
				{
					wasTriggered = false;
					if (audioSource != null && isPlaying)
					{
						audioSource.Stop();
						isPlaying = false;
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Left moon, reset meltdown trigger for next round.");
				}
				wasInOrbit = true;
			}
			else
			{
				wasInOrbit = false;
			}
		}
		if (isPlaying && audioSource != null)
		{
			suppressCheckTimer += Time.deltaTime;
			if (suppressCheckTimer >= 0.5f)
			{
				suppressCheckTimer = 0f;
				SuppressOtherAudio();
			}
			object startOfRound3 = GetStartOfRound();
			if (startOfRound3 != null && GetBool(startOfRound3, "shipIsLeaving"))
			{
				((MonoBehaviour)this).StartCoroutine(FadeOutAndStop(2f));
			}
		}
	}

	private void SuppressOtherAudio()
	{
		AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
		if (array == null)
		{
			return;
		}
		AudioSource[] array2 = array;
		foreach (AudioSource val in array2)
		{
			if (val != null && val != audioSource && val.isPlaying)
			{
				string text = ((((Component)val).gameObject != null) ? ((Object)((Component)val).gameObject).name.ToLower() : "");
				string text2 = ((val.clip != null && ((Object)val.clip).name != null) ? ((Object)val.clip).name.ToLower() : "");
				if (text.Contains("meltdown") || text.Contains("facilitymeltdown") || text2.Contains("meltdown"))
				{
					val.volume = 0f;
					val.mute = true;
				}
			}
		}
	}

	private void OnDestroy()
	{
		wasTriggered = false;
		isPlaying = false;
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}

	[IteratorStateMachine(typeof(<PlayMusic>d__18))]
	private IEnumerator PlayMusic()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <PlayMusic>d__18(0)
		{
			<>4__this = this
		};
	}

	[IteratorStateMachine(typeof(<FadeOutAndStop>d__19))]
	private IEnumerator FadeOutAndStop(float duration)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <FadeOutAndStop>d__19(0)
		{
			<>4__this = this,
			duration = duration
		};
	}

	private static bool GetIsDisconnecting()
	{
		try
		{
			Type type = null;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				type = assembly.GetType("GameNetworkManager");
				if (type != null)
				{
					break;
				}
			}
			if (type == null)
			{
				return false;
			}
			PropertyInfo property = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null) : null);
			if (obj == null)
			{
				FieldInfo field = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public);
				obj = ((field != null) ? field.GetValue(null) : null);
			}
			if (obj == null)
			{
				return false;
			}
			PropertyInfo property2 = type.GetProperty("isDisconnecting", BindingFlags.Instance | BindingFlags.Public);
			if (property2 != null)
			{
				return (bool)property2.GetValue(obj);
			}
			FieldInfo field2 = type.GetField("isDisconnecting", BindingFlags.Instance | BindingFlags.Public);
			if (field2 != null)
			{
				return (bool)field2.GetValue(obj);
			}
			return false;
		}
		catch
		{
			return false;
		}
	}

	private static object GetStartOfRound()
	{
		Type type = null;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			type = assembly.GetType("StartOfRound");
			if (type != null)
			{
				break;
			}
		}
		if (type == null)
		{
			return null;
		}
		PropertyInfo property = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
		if (property != null)
		{
			return property.GetValue(null);
		}
		FieldInfo field = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public);
		if (field != null)
		{
			return field.GetValue(null);
		}
		return null;
	}

	private static bool GetBool(object obj, string name)
	{
		if (obj == null)
		{
			return false;
		}
		Type type = obj.GetType();
		PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
		if (property != null)
		{
			return (bool)property.GetValue(obj);
		}
		FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public);
		if (field != null)
		{
			return (bool)field.GetValue(obj);
		}
		return false;
	}
}
public static class LungPropPatch
{
	public static void Prefix(object __instance)
	{
		if (Plugin.instance != null && !Plugin.wasTriggered)
		{
			Type type = __instance.GetType();
			FieldInfo field = type.GetField("isLungDocked", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(field == null) && (bool)field.GetValue(__instance))
			{
				Plugin.instance.TriggerMeltdownMusic();
			}
		}
	}
}