Decompiled source of SoundFalloff v1.0.1

Mods/SoundFalloff.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using SoundFalloff;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "SoundFalloff", "1.0.0", "Lunobe", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SoundFalloff")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SoundFalloff")]
[assembly: AssemblyTitle("SoundFalloff")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 SoundFalloff
{
	public class Core : MelonMod
	{
		private struct AudioSourceOriginalSettings
		{
			public float MinDistance;

			public float MaxDistance;

			public AudioRolloffMode RolloffMode;
		}

		public enum SoundCategory
		{
			Quiet,
			Medium,
			Loud
		}

		[HarmonyPatch(typeof(AudioSource))]
		public static class AudioSourcePatches
		{
			[HarmonyPatch("Play", new Type[] { })]
			[HarmonyPrefix]
			public static bool PlayPrefix(AudioSource __instance)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}

			[HarmonyPatch("Play", new Type[] { typeof(double) })]
			[HarmonyPrefix]
			public static bool PlayDelayedPrefix(AudioSource __instance)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}

			[HarmonyPatch("PlayOneShot", new Type[] { typeof(AudioClip) })]
			[HarmonyPrefix]
			public static bool PlayOneShotPrefix(AudioSource __instance, AudioClip clip)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}

			[HarmonyPatch("PlayOneShot", new Type[]
			{
				typeof(AudioClip),
				typeof(float)
			})]
			[HarmonyPrefix]
			public static bool PlayOneShotVolumePrefix(AudioSource __instance, AudioClip clip)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}

			[HarmonyPatch("PlayDelayed")]
			[HarmonyPrefix]
			public static bool PlayDelayedMethodPrefix(AudioSource __instance)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}

			[HarmonyPatch("PlayScheduled")]
			[HarmonyPrefix]
			public static bool PlayScheduledPrefix(AudioSource __instance)
			{
				if (ShouldSkipSound(__instance))
				{
					return false;
				}
				ProcessAudioSource(__instance);
				return true;
			}
		}

		public static Core Instance;

		private static MelonPreferences_Category _prefCategory;

		public static MelonPreferences_Entry<bool> PrefEnabled;

		public static MelonPreferences_Entry<float> PrefLocalRadius;

		public static MelonPreferences_Entry<float> PrefQuietMaxDist;

		public static MelonPreferences_Entry<float> PrefMediumMaxDist;

		public static MelonPreferences_Entry<float> PrefLoudMaxDist;

		public static MelonPreferences_Entry<bool> PrefDebugLogs;

		private static FloatElement _localRadiusElement;

		private static FloatElement _quietMaxDistElement;

		private static FloatElement _mediumMaxDistElement;

		private static FloatElement _loudMaxDistElement;

		private static readonly HashSet<string> QuietKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"reload", "mag", "magazine", "bolt", "slide", "chamber", "cock", "rack", "insert", "eject",
			"click", "latch", "lock", "unlock", "safety", "foot", "step", "walk", "run", "land",
			"jump", "grab", "grip", "hold", "pickup", "drop", "rub", "scrape", "handle", "touch",
			"interact", "cloth", "leather", "fabric", "rustle", "ui", "menu", "button", "beep", "tick",
			"pop", "creak", "squeak", "hinge", "door_open", "door_close", "drawer", "breath", "gulp", "swallow",
			"eat", "drink"
		};

		private static readonly HashSet<string> LoudKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"explo", "boom", "blast", "detonate", "bomb", "grenade", "missile", "rocket", "mortar", "artillery",
			"nuke", "c4", "tnt", "dynamite", "cannon", "tank", "50cal", "50_cal", ".50", "heavy",
			"turret", "minigun", "gatling", "lmg", "hmg", "sniper_fire", "barrett", "crash", "vehicle", "car_",
			"truck", "helicopter", "jet", "engine_loud", "thunder", "collapse", "avalanche", "earthquake", "siren"
		};

		private static readonly Dictionary<int, AudioSourceOriginalSettings> OriginalSettings = new Dictionary<int, AudioSourceOriginalSettings>();

		public static bool IsEnabled => PrefEnabled?.Value ?? false;

		public static bool DebugMode => PrefDebugLogs?.Value ?? false;

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log("=== SoundFalloff Initialization Started ===");
			try
			{
				SetupPreferences();
				Log("Preferences initialized");
			}
			catch (Exception value)
			{
				LogError($"Failed to setup preferences: {value}");
			}
			try
			{
				SetupBoneMenu();
				Log("BoneMenu initialized");
			}
			catch (Exception value2)
			{
				LogError($"Failed to setup BoneMenu: {value2}");
			}
			Log($"=== SoundFalloff Ready | Enabled: {IsEnabled} ===");
		}

		private void SetupPreferences()
		{
			_prefCategory = MelonPreferences.CreateCategory("SoundFalloff");
			PrefEnabled = _prefCategory.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefLocalRadius = _prefCategory.CreateEntry<float>("LocalRadius", 3f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefQuietMaxDist = _prefCategory.CreateEntry<float>("QuietMaxDistance", 40f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefMediumMaxDist = _prefCategory.CreateEntry<float>("MediumMaxDistance", 150f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefLoudMaxDist = _prefCategory.CreateEntry<float>("LoudMaxDistance", 800f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefDebugLogs = _prefCategory.CreateEntry<bool>("DebugLogs", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			LogDebug($"Prefs loaded - Enabled:{PrefEnabled.Value} LocalRadius:{PrefLocalRadius.Value} Quiet:{PrefQuietMaxDist.Value} Medium:{PrefMediumMaxDist.Value} Loud:{PrefLoudMaxDist.Value}");
		}

		private void SetupBoneMenu()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			Page obj = Page.Root.CreatePage("Sound Falloff", Color.cyan, 0, true);
			LogDebug("Created BoneMenu page");
			obj.CreateBool("Enabled", Color.green, PrefEnabled.Value, (Action<bool>)delegate(bool value)
			{
				SafeSetPref("Enabled", delegate
				{
					PrefEnabled.Value = value;
				});
			});
			_localRadiusElement = obj.CreateFloat("Local Radius", Color.white, PrefLocalRadius.Value, 0.5f, 1f, 10f, (Action<float>)delegate(float value)
			{
				SafeSetPref("LocalRadius", delegate
				{
					PrefLocalRadius.Value = Mathf.Max(1f, value);
				});
			});
			_quietMaxDistElement = obj.CreateFloat("Quiet Max Dist", Color.gray, PrefQuietMaxDist.Value, 5f, 10f, 100f, (Action<float>)delegate(float value)
			{
				SafeSetPref("QuietMaxDistance", delegate
				{
					PrefQuietMaxDist.Value = Mathf.Max(5f, value);
				});
			});
			_mediumMaxDistElement = obj.CreateFloat("Medium Max Dist", Color.yellow, PrefMediumMaxDist.Value, 10f, 50f, 400f, (Action<float>)delegate(float value)
			{
				SafeSetPref("MediumMaxDistance", delegate
				{
					PrefMediumMaxDist.Value = Mathf.Max(20f, value);
				});
			});
			_loudMaxDistElement = obj.CreateFloat("Loud Max Dist", Color.red, PrefLoudMaxDist.Value, 50f, 200f, 2000f, (Action<float>)delegate(float value)
			{
				SafeSetPref("LoudMaxDistance", delegate
				{
					PrefLoudMaxDist.Value = Mathf.Max(100f, value);
				});
			});
			obj.CreateFunction("Reset to Defaults", Color.red, (Action)delegate
			{
				ResetToDefaults();
			});
			LogDebug("All BoneMenu elements created");
		}

		private void SafeSetPref(string name, Action action)
		{
			try
			{
				action();
				_prefCategory.SaveToFile(true);
				LogDebug("Pref changed: " + name);
			}
			catch (Exception value)
			{
				LogError($"Failed to save pref '{name}': {value}");
			}
		}

		private void ResetToDefaults()
		{
			try
			{
				PrefLocalRadius.Value = 3f;
				PrefQuietMaxDist.Value = 40f;
				PrefMediumMaxDist.Value = 150f;
				PrefLoudMaxDist.Value = 800f;
				_prefCategory.SaveToFile(true);
				if (_localRadiusElement != null)
				{
					_localRadiusElement.Value = 3f;
				}
				if (_quietMaxDistElement != null)
				{
					_quietMaxDistElement.Value = 40f;
				}
				if (_mediumMaxDistElement != null)
				{
					_mediumMaxDistElement.Value = 150f;
				}
				if (_loudMaxDistElement != null)
				{
					_loudMaxDistElement.Value = 800f;
				}
				Log("Settings reset to defaults");
			}
			catch (Exception value)
			{
				LogError($"Failed to reset settings: {value}");
			}
		}

		public static SoundCategory CategorizeSound(string clipName)
		{
			if (string.IsNullOrEmpty(clipName))
			{
				return SoundCategory.Medium;
			}
			string text = clipName.ToLowerInvariant();
			foreach (string loudKeyword in LoudKeywords)
			{
				if (text.Contains(loudKeyword.ToLowerInvariant()))
				{
					LogDebug($"[CAT] '{clipName}' -> LOUD (keyword: {loudKeyword})");
					return SoundCategory.Loud;
				}
			}
			foreach (string quietKeyword in QuietKeywords)
			{
				if (text.Contains(quietKeyword.ToLowerInvariant()))
				{
					LogDebug($"[CAT] '{clipName}' -> QUIET (keyword: {quietKeyword})");
					return SoundCategory.Quiet;
				}
			}
			LogDebug("[CAT] '" + clipName + "' -> MEDIUM (default)");
			return SoundCategory.Medium;
		}

		public static float GetMaxDistanceForCategory(SoundCategory category)
		{
			return category switch
			{
				SoundCategory.Quiet => PrefQuietMaxDist.Value, 
				SoundCategory.Medium => PrefMediumMaxDist.Value, 
				SoundCategory.Loud => PrefLoudMaxDist.Value, 
				_ => PrefMediumMaxDist.Value, 
			};
		}

		public static float GetMinDistanceForCategory(SoundCategory category)
		{
			return category switch
			{
				SoundCategory.Quiet => 1f, 
				SoundCategory.Medium => 2f, 
				SoundCategory.Loud => 5f, 
				_ => 2f, 
			};
		}

		public static bool IsLocalSound(Vector3 soundPosition)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Transform head = Player.Head;
				if ((Object)(object)head == (Object)null)
				{
					return false;
				}
				return Vector3.Distance(soundPosition, head.position) <= PrefLocalRadius.Value;
			}
			catch
			{
				return false;
			}
		}

		public static void ProcessAudioSource(AudioSource source)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEnabled || (Object)(object)source == (Object)null)
			{
				return;
			}
			try
			{
				if (source.spatialBlend < 0.5f)
				{
					LogDebug($"[AUDIO] Skipping 2D sound: spatialBlend={source.spatialBlend:F2}");
					return;
				}
				Vector3 position = ((Component)source).transform.position;
				if (IsLocalSound(position))
				{
					LogDebug($"[AUDIO] Skipping local sound at {position}");
					return;
				}
				AudioClip clip = source.clip;
				string text = ((clip != null) ? ((Object)clip).name : null) ?? ((Object)((Component)source).gameObject).name;
				SoundCategory soundCategory = CategorizeSound(text);
				float maxDistanceForCategory = GetMaxDistanceForCategory(soundCategory);
				float minDistanceForCategory = GetMinDistanceForCategory(soundCategory);
				int instanceID = ((Object)source).GetInstanceID();
				if (!OriginalSettings.ContainsKey(instanceID))
				{
					OriginalSettings[instanceID] = new AudioSourceOriginalSettings
					{
						MinDistance = source.minDistance,
						MaxDistance = source.maxDistance,
						RolloffMode = source.rolloffMode
					};
				}
				if (source.maxDistance > maxDistanceForCategory)
				{
					LogDebug($"[AUDIO] Modifying '{text}' [{soundCategory}]: maxDist {source.maxDistance:F0} -> {maxDistanceForCategory:F0}");
					source.minDistance = minDistanceForCategory;
					source.maxDistance = maxDistanceForCategory;
					source.rolloffMode = (AudioRolloffMode)0;
				}
				else
				{
					LogDebug($"[AUDIO] '{text}' [{soundCategory}]: keeping original maxDist={source.maxDistance:F0} (< {maxDistanceForCategory:F0})");
				}
			}
			catch (Exception value)
			{
				LogError($"ProcessAudioSource error: {value}");
			}
		}

		public static bool ShouldSkipSound(AudioSource source)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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)
			if (!IsEnabled || (Object)(object)source == (Object)null)
			{
				return false;
			}
			try
			{
				if (source.spatialBlend < 0.5f)
				{
					return false;
				}
				Vector3 position = ((Component)source).transform.position;
				if (IsLocalSound(position))
				{
					return false;
				}
				Transform head = Player.Head;
				if ((Object)(object)head == (Object)null)
				{
					return false;
				}
				float num = Vector3.Distance(position, head.position);
				AudioClip clip = source.clip;
				string text = ((clip != null) ? ((Object)clip).name : null) ?? ((Object)((Component)source).gameObject).name;
				SoundCategory soundCategory = CategorizeSound(text);
				float maxDistanceForCategory = GetMaxDistanceForCategory(soundCategory);
				if (num > maxDistanceForCategory)
				{
					LogDebug($"[SKIP] '{text}' at {num:F0}m > {maxDistanceForCategory:F0}m [{soundCategory}]");
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		public static void Log(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Msg(msg);
			}
		}

		public static void LogWarning(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Warning(msg);
			}
		}

		public static void LogError(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Error(msg);
			}
		}

		public static void LogDebug(string msg)
		{
			if (DebugMode)
			{
				Core instance = Instance;
				if (instance != null)
				{
					((MelonBase)instance).LoggerInstance.Msg("[DEBUG] " + msg);
				}
			}
		}
	}
}