Decompiled source of SteadyMusic v1.0.2

plugins\SteadyMusic.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Allifreyr")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("SteadyMusic")]
[assembly: AssemblyTitle("SteadyMusic")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 SteadyMusic
{
	[HarmonyPatch(typeof(MusicMan), "GetEnvironmentMusic")]
	internal static class MusicManGetEnvironmentMusicPatch
	{
		private static bool Prefix(MusicMan __instance, ref NamedMusic __result)
		{
			if (!PluginConfig.ModEnabled.Value)
			{
				return true;
			}
			__result = MusicStabilizer.GetStabilizedEnvironmentMusic(__instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(MusicMan), "StartMusic", new Type[] { typeof(NamedMusic) })]
	internal static class MusicManStartMusicPatch
	{
		private static void Postfix(NamedMusic music)
		{
			if (PluginConfig.ModEnabled.Value && music != null && (music.m_ambientMusic || music.m_name == "home"))
			{
				MusicStabilizer.OnTrackStarted(music.m_name);
			}
		}
	}
	[HarmonyPatch(typeof(MusicMan), "Reset")]
	internal static class MusicManResetPatch
	{
		private static void Postfix()
		{
			MusicStabilizer.Reset();
		}
	}
	internal static class MusicStabilizer
	{
		private static string s_currentStableMusicName;

		private static float s_trackStartTime;

		private static string s_pendingMusicName;

		private static float s_pendingStartTime;

		public static NamedMusic GetStabilizedEnvironmentMusic(MusicMan musicMan)
		{
			if ((Object)(object)musicMan == (Object)null)
			{
				return null;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && (((Character)Player.m_localPlayer).IsTeleporting() || ((Character)Player.m_localPlayer).IsDead()))
			{
				Reset();
			}
			string rawDesiredMusicName = GetRawDesiredMusicName();
			if (string.IsNullOrEmpty(rawDesiredMusicName))
			{
				return null;
			}
			string text = musicMan.m_currentMusic?.m_name;
			if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(s_currentStableMusicName))
			{
				s_currentStableMusicName = rawDesiredMusicName;
				s_pendingMusicName = null;
				s_trackStartTime = Time.time;
				return FindMusic(musicMan, rawDesiredMusicName);
			}
			if (!string.IsNullOrEmpty(text) && text != s_currentStableMusicName)
			{
				s_currentStableMusicName = text;
				s_pendingMusicName = null;
			}
			if (rawDesiredMusicName == s_currentStableMusicName)
			{
				if (s_pendingMusicName != null)
				{
					if (PluginConfig.EnableDebugLogging.Value)
					{
						Plugin.Log.LogInfo((object)("[SteadyMusic] Reverted to current stable music '" + s_currentStableMusicName + "' before delay elapsed. Transition cancelled."));
					}
					s_pendingMusicName = null;
				}
				return FindMusic(musicMan, s_currentStableMusicName);
			}
			if (s_pendingMusicName != rawDesiredMusicName)
			{
				s_pendingMusicName = rawDesiredMusicName;
				s_pendingStartTime = Time.time;
				if (PluginConfig.EnableDebugLogging.Value)
				{
					Plugin.Log.LogInfo((object)("[SteadyMusic] Pending change: '" + s_currentStableMusicName + "' -> '" + rawDesiredMusicName + "'. Waiting for transition delay..."));
				}
			}
			float requiredDelay = GetRequiredDelay(s_currentStableMusicName, rawDesiredMusicName);
			float num = Time.time - s_pendingStartTime;
			float num2 = Time.time - s_trackStartTime;
			bool num3 = num >= requiredDelay;
			bool flag = num2 >= PluginConfig.MinTrackDuration.Value;
			bool flag2 = true;
			if (PluginConfig.FinishTrackBeforeSwitch.Value && (Object)(object)musicMan.m_musicSource != (Object)null && (Object)(object)musicMan.m_musicSource.clip != (Object)null)
			{
				flag2 = musicMan.m_musicSource.clip.length - musicMan.m_musicSource.time <= 4f;
			}
			if (num3 && flag && flag2)
			{
				if (PluginConfig.EnableDebugLogging.Value)
				{
					Plugin.Log.LogInfo((object)$"[SteadyMusic] Switching ambient music: '{s_currentStableMusicName}' -> '{rawDesiredMusicName}' (waited {num:F1}s, track played {num2:F1}s).");
				}
				s_currentStableMusicName = rawDesiredMusicName;
				s_pendingMusicName = null;
				s_trackStartTime = Time.time;
				return FindMusic(musicMan, s_currentStableMusicName);
			}
			return FindMusic(musicMan, s_currentStableMusicName);
		}

		public static void OnTrackStarted(string musicName)
		{
			s_currentStableMusicName = musicName;
			s_trackStartTime = Time.time;
			s_pendingMusicName = null;
		}

		public static void Reset()
		{
			s_currentStableMusicName = null;
			s_pendingMusicName = null;
			s_trackStartTime = 0f;
			s_pendingStartTime = 0f;
		}

		private static float GetRequiredDelay(string fromMusic, string toMusic)
		{
			if (toMusic == "home")
			{
				return PluginConfig.EnterHomeDelay.Value;
			}
			if (fromMusic == "home")
			{
				return PluginConfig.ExitHomeDelay.Value;
			}
			return PluginConfig.BiomeTransitionDelay.Value;
		}

		private static string GetRawDesiredMusicName()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				if (!((Object)(object)EnvMan.instance != (Object)null))
				{
					return null;
				}
				return EnvMan.instance.GetAmbientMusic();
			}
			if (!PluginConfig.DisableHomeMusic.Value)
			{
				bool flag = false;
				if (PluginConfig.ExtendHomeMusicToBase.Value && Player.m_localPlayer.GetBaseValue() >= 1)
				{
					flag = true;
				}
				else if (Player.m_localPlayer.IsSafeInHome())
				{
					flag = true;
				}
				if (flag)
				{
					return "home";
				}
			}
			BiomeSector currentBiomeData = Player.m_localPlayer.GetCurrentBiomeData();
			if (currentBiomeData != null && currentBiomeData.AltBiomes != null)
			{
				foreach (AltBiome altBiome in currentBiomeData.AltBiomes)
				{
					if (!string.IsNullOrEmpty(altBiome.m_forceMusic))
					{
						return altBiome.m_forceMusic;
					}
				}
			}
			if ((Object)(object)EnvMan.instance != (Object)null)
			{
				return EnvMan.instance.GetAmbientMusic();
			}
			return null;
		}

		public static NamedMusic FindMusic(MusicMan musicMan, string musicName)
		{
			if ((Object)(object)musicMan == (Object)null || string.IsNullOrEmpty(musicName))
			{
				return null;
			}
			List<NamedMusic> music = musicMan.m_music;
			if (music == null)
			{
				return null;
			}
			for (int i = 0; i < music.Count; i++)
			{
				NamedMusic val = music[i];
				if (val != null && val.m_name == musicName)
				{
					return val;
				}
			}
			return null;
		}
	}
	[BepInPlugin("Allifreyr.SteadyMusic", "SteadyMusic", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Allifreyr.SteadyMusic";

		public const string PluginName = "SteadyMusic";

		public const string PluginVersion = "1.0.2";

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static Harmony Harmony { get; private set; }

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Game.isModded = true;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			Harmony = new Harmony("Allifreyr.SteadyMusic");
			Harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)string.Format("{0} v{1} loaded successfully. Hotkey: {2}", "SteadyMusic", "1.0.2", PluginConfig.ToggleHotkey.Value));
		}

		private void Update()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (Console.IsVisible() || TextInput.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()))
			{
				return;
			}
			KeyboardShortcut value = PluginConfig.ToggleHotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				bool flag = !PluginConfig.ModEnabled.Value;
				PluginConfig.ModEnabled.Value = flag;
				string text = (flag ? "SteadyMusic: <color=green>ENABLED</color>" : "SteadyMusic: <color=red>DISABLED</color>");
				if (PluginConfig.ShowToggleMessage.Value && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
				Log.LogInfo((object)$"SteadyMusic toggled: {flag}");
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}
	}
	internal static class PluginConfig
	{
		internal static ConfigEntry<bool> ModEnabled { get; private set; }

		internal static ConfigEntry<float> EnterHomeDelay { get; private set; }

		internal static ConfigEntry<float> ExitHomeDelay { get; private set; }

		internal static ConfigEntry<float> MinTrackDuration { get; private set; }

		internal static ConfigEntry<bool> ExtendHomeMusicToBase { get; private set; }

		internal static ConfigEntry<float> BiomeTransitionDelay { get; private set; }

		internal static ConfigEntry<bool> FinishTrackBeforeSwitch { get; private set; }

		internal static ConfigEntry<bool> DisableHomeMusic { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> ToggleHotkey { get; private set; }

		internal static ConfigEntry<bool> ShowToggleMessage { get; private set; }

		internal static ConfigEntry<bool> EnableDebugLogging { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			ModEnabled = config.Bind<bool>("General", "Enable", true, "Master switch for SteadyMusic. Set to false to disable all stabilization features.");
			EnterHomeDelay = config.Bind<float>("Delays", "Enter Home Delay", 10f, new ConfigDescription("Seconds you must remain continuously inside a home before music transitions from biome to home music. Prevents music changes when briefly ducking inside.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
			ExitHomeDelay = config.Bind<float>("Delays", "Exit Home Delay", 20f, new ConfigDescription("Seconds you must remain continuously outside before music transitions from home music back to biome music. Eliminates doorway cover flicker and brief yard trips.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 180f), Array.Empty<object>()));
			MinTrackDuration = config.Bind<float>("Delays", "Min Track Duration", 45f, new ConfigDescription("Minimum seconds an ambient track (home or biome) must play before another ambient track can replace it. Urgent tracks (combat, raids) still interrupt immediately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			BiomeTransitionDelay = config.Bind<float>("Delays", "Biome Transition Delay", 15f, new ConfigDescription("Seconds you must remain in a new biome before ambient music transitions to the new biome's theme. Prevents back-and-forth music switching on biome borders.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
			ExtendHomeMusicToBase = config.Bind<bool>("Features", "Extend Home Music To Base", false, "If enabled, home music continues playing anywhere within your base/workbench perimeter (where player structures exist), not just under covered roofs.");
			FinishTrackBeforeSwitch = config.Bind<bool>("Features", "Finish Track Before Switch", false, "If enabled, ambient tracks will play until near the end of the song before transitioning to a new ambient theme.");
			DisableHomeMusic = config.Bind<bool>("Features", "Disable Home Music", false, "If enabled, the 'home' music theme is disabled entirely and the current biome's music plays continuously even inside houses.");
			ToggleHotkey = config.Bind<KeyboardShortcut>("Controls", "Toggle Hotkey", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Keyboard shortcut to toggle SteadyMusic on or off while playing.");
			ShowToggleMessage = config.Bind<bool>("Controls", "Show Toggle Message", true, "Show on-screen notification when toggling the mod on or off.");
			EnableDebugLogging = config.Bind<bool>("Diagnostics", "Enable Debug Logging", false, "Print music transition evaluations and delay timers to BepInEx console.");
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}