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);
}
}
}
}
}