Decompiled source of MithrixChimeraMusic v1.0.2

MithrixChimeraMusic.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates.Missions.BrotherEncounter;
using Microsoft.CodeAnalysis;
using On.EntityStates.Missions.BrotherEncounter;
using On.RoR2;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MithrixChimeraMusic")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MithrixChimeraMusic")]
[assembly: AssemblyTitle("MithrixChimeraMusic")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MithrixChimeraMusic
{
	[BepInPlugin("com.emerson.mithrixchimeramusic", "Mithrix Chimera Music", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_OnEnter <>9__16_0;

			public static hook_OnEnter <>9__16_1;

			public static hook_OnEnter <>9__16_2;

			public static hook_OnEnter <>9__16_3;

			public static hook_BeginGameOver <>9__16_4;

			public static hook_Start <>9__16_5;

			public static Func<Type, bool> <>9__21_0;

			internal void <Awake>b__16_0(orig_OnEnter orig, Phase1 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase1");
			}

			internal void <Awake>b__16_1(orig_OnEnter orig, Phase2 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase2");
			}

			internal void <Awake>b__16_2(orig_OnEnter orig, Phase3 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase3");
			}

			internal void <Awake>b__16_3(orig_OnEnter orig, Phase4 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase4");
			}

			internal void <Awake>b__16_4(orig_BeginGameOver orig, Run self, GameEndingDef ending)
			{
				StopSong();
				currentPhase = null;
				orig.Invoke(self, ending);
			}

			internal void <Awake>b__16_5(orig_Start orig, Stage self)
			{
				orig.Invoke(self);
				if (debugPlayOnStageStart.Value)
				{
					Log.LogInfo((object)"[Debug] Stage started — triggering song");
					StartSong();
				}
			}

			internal bool <PostWwiseEvent>b__21_0(Type t)
			{
				return t.Name == "AkSoundEngine";
			}
		}

		public const string GUID = "com.emerson.mithrixchimeramusic";

		public const string NAME = "Mithrix Chimera Music";

		public const string VERSION = "1.0.2";

		private const string ResourceName = "MithrixChimeraMusic.intermission.ogg";

		internal static ManualLogSource Log;

		private static AudioClip clip;

		private static AudioSource source;

		private static ConfigEntry<string> phaseChoice;

		private static ConfigEntry<float> volume;

		private static ConfigEntry<bool> muteGameMusic;

		private static ConfigEntry<string> pauseEventName;

		private static ConfigEntry<string> resumeEventName;

		private static ConfigEntry<bool> debugPlayOnStageStart;

		private static MethodInfo akPostEvent;

		private static bool akLookupAttempted;

		private static string currentPhase;

		private void Awake()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			phaseChoice = ((BaseUnityPlugin)this).Config.Bind<string>("General", "PhaseToPlayDuring", "Phase2", "Which Mithrix phase to play during. Valid: Phase1, Phase2, Phase3, Phase4. Watch the BepInEx console — every phase entry is logged so you can verify.");
			volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.7f, "0.0 to 1.0");
			muteGameMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "MuteGameMusic", true, "Try to pause Wwise music while the song plays. Best-effort — falls back to overlap if the Wwise call fails.");
			pauseEventName = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "PauseEventName", "Pause_Music_System", "Wwise event posted on phase enter.");
			resumeEventName = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "ResumeEventName", "Resume_Music_System", "Wwise event posted on phase exit.");
			debugPlayOnStageStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "PlayOnStageStart", false, "DEBUG: play the song at the start of every stage instead of waiting for the Mithrix fight. Useful for verifying audio works without grinding to phase 2.");
			((MonoBehaviour)this).StartCoroutine(LoadClipCoroutine());
			object obj = <>c.<>9__16_0;
			if (obj == null)
			{
				hook_OnEnter val = delegate(orig_OnEnter orig, Phase1 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase1");
				};
				<>c.<>9__16_0 = val;
				obj = (object)val;
			}
			Phase1.OnEnter += (hook_OnEnter)obj;
			object obj2 = <>c.<>9__16_1;
			if (obj2 == null)
			{
				hook_OnEnter val2 = delegate(orig_OnEnter orig, Phase2 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase2");
				};
				<>c.<>9__16_1 = val2;
				obj2 = (object)val2;
			}
			Phase2.OnEnter += (hook_OnEnter)obj2;
			object obj3 = <>c.<>9__16_2;
			if (obj3 == null)
			{
				hook_OnEnter val3 = delegate(orig_OnEnter orig, Phase3 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase3");
				};
				<>c.<>9__16_2 = val3;
				obj3 = (object)val3;
			}
			Phase3.OnEnter += (hook_OnEnter)obj3;
			object obj4 = <>c.<>9__16_3;
			if (obj4 == null)
			{
				hook_OnEnter val4 = delegate(orig_OnEnter orig, Phase4 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase4");
				};
				<>c.<>9__16_3 = val4;
				obj4 = (object)val4;
			}
			Phase4.OnEnter += (hook_OnEnter)obj4;
			object obj5 = <>c.<>9__16_4;
			if (obj5 == null)
			{
				hook_BeginGameOver val5 = delegate(orig_BeginGameOver orig, Run self, GameEndingDef ending)
				{
					StopSong();
					currentPhase = null;
					orig.Invoke(self, ending);
				};
				<>c.<>9__16_4 = val5;
				obj5 = (object)val5;
			}
			Run.BeginGameOver += (hook_BeginGameOver)obj5;
			object obj6 = <>c.<>9__16_5;
			if (obj6 == null)
			{
				hook_Start val6 = delegate(orig_Start orig, Stage self)
				{
					orig.Invoke(self);
					if (debugPlayOnStageStart.Value)
					{
						Log.LogInfo((object)"[Debug] Stage started — triggering song");
						StartSong();
					}
				};
				<>c.<>9__16_5 = val6;
				obj6 = (object)val6;
			}
			Stage.Start += (hook_Start)obj6;
		}

		private static void OnPhaseEnter(string name)
		{
			Log.LogInfo((object)("[Mithrix] Entered phase: " + name));
			if (currentPhase == phaseChoice.Value && name != phaseChoice.Value)
			{
				StopSong();
			}
			currentPhase = name;
			if (name == phaseChoice.Value)
			{
				StartSong();
			}
		}

		private IEnumerator LoadClipCoroutine()
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			Stream manifestResourceStream = executingAssembly.GetManifestResourceStream("MithrixChimeraMusic.intermission.ogg");
			if (manifestResourceStream == null)
			{
				Log.LogError((object)("Embedded resource 'MithrixChimeraMusic.intermission.ogg' not found. Available: " + string.Join(", ", executingAssembly.GetManifestResourceNames())));
				yield break;
			}
			string text = Path.Combine(Application.temporaryCachePath, "mithrix_chimera_song.ogg");
			using (FileStream destination = File.Create(text))
			{
				manifestResourceStream.CopyTo(destination);
			}
			manifestResourceStream.Dispose();
			Log.LogInfo((object)$"Wrote OGG to {text} ({new FileInfo(text).Length} bytes)");
			string absoluteUri = new Uri(text).AbsoluteUri;
			UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)14);
			try
			{
				((DownloadHandlerAudioClip)req.downloadHandler).streamAudio = false;
				yield return req.SendWebRequest();
				if ((int)req.result != 1)
				{
					Log.LogError((object)("Audio load failed: " + req.error));
					yield break;
				}
				clip = DownloadHandlerAudioClip.GetContent(req);
				if ((Object)(object)clip == (Object)null || clip.length <= 0f)
				{
					ManualLogSource log = Log;
					object arg = (Object)(object)clip == (Object)null;
					AudioClip obj = clip;
					log.LogError((object)$"AudioClip invalid (null={arg}, length={((obj != null) ? new float?(obj.length) : null)})");
					yield break;
				}
				((Object)clip).name = "MithrixChimeraSong";
				Log.LogInfo((object)$"Clip loaded: {clip.length:F1}s, {clip.channels}ch, {clip.frequency}Hz, samples={clip.samples}");
				GameObject val = new GameObject("MithrixChimeraMusicSource");
				Object.DontDestroyOnLoad((Object)val);
				source = val.AddComponent<AudioSource>();
				source.clip = clip;
				source.loop = true;
				source.playOnAwake = false;
				source.spatialBlend = 0f;
				source.bypassEffects = true;
				source.bypassListenerEffects = true;
				source.bypassReverbZones = true;
				source.ignoreListenerPause = true;
				source.ignoreListenerVolume = false;
				source.volume = volume.Value;
				Log.LogInfo((object)"Mithrix Chimera Music loaded");
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}

		private static void StartSong()
		{
			if ((Object)(object)source == (Object)null)
			{
				Log.LogWarning((object)"Phase entered before AudioSource created");
			}
			else if ((Object)(object)source.clip == (Object)null)
			{
				Log.LogError((object)"StartSong called but clip is null — load failed silently");
			}
			else if (!source.isPlaying)
			{
				if (muteGameMusic.Value)
				{
					PostWwiseEvent(pauseEventName.Value);
				}
				source.volume = volume.Value;
				AudioListener.pause = false;
				source.Play();
				Log.LogInfo((object)$"Custom song started (volume={source.volume}, listenerVol={AudioListener.volume}, listenerPaused={AudioListener.pause}, isPlaying={source.isPlaying})");
			}
		}

		private static void StopSong()
		{
			if (!((Object)(object)source == (Object)null))
			{
				source.Stop();
				if (muteGameMusic.Value)
				{
					PostWwiseEvent(resumeEventName.Value);
				}
				Log.LogInfo((object)"Custom song stopped");
			}
		}

		private static void PostWwiseEvent(string eventName)
		{
			try
			{
				if (!akLookupAttempted)
				{
					akLookupAttempted = true;
					Type type = null;
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly in assemblies)
					{
						try
						{
							type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "AkSoundEngine");
						}
						catch
						{
							continue;
						}
						if (type != null)
						{
							break;
						}
					}
					if (type != null)
					{
						akPostEvent = type.GetMethod("PostEvent", new Type[2]
						{
							typeof(string),
							typeof(GameObject)
						});
					}
					else
					{
						Log.LogWarning((object)"AkSoundEngine type not found at runtime — game music won't be muted");
					}
				}
				if (akPostEvent != null && (Object)(object)source != (Object)null)
				{
					akPostEvent.Invoke(null, new object[2]
					{
						eventName,
						((Component)source).gameObject
					});
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Wwise event post failed: " + ex.Message));
			}
		}
	}
}