using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("AmbientMuter")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("AmbientMuter")] [assembly: AssemblyTitle("AmbientMuter")] [assembly: AssemblyVersion("1.0.0.0")] namespace AmbientMuter; [BepInPlugin("h3vr.invent60.ambientmuter", "AmbientMuter", "2.1.0")] public class AmbientMuterPlugin : BaseUnityPlugin { private ConfigEntry _cfgMuteMusic; private ConfigEntry _cfgMuteAmbient; private ConfigEntry _cfgSweepDelay; private ConfigEntry _cfgKeepFootsteps; private ConfigEntry _cfgKeepInteractive; private ConfigEntry _cfgMutePlayerHurt; internal static ManualLogSource Log; private Harmony _harmony; private bool _calibrated; private int _calibrationAttempts; private AmbientMuterRunner _runner; internal static bool IsU120Plus; private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BindConfig(); IsU120Plus = (object)AccessTools.TypeByName("FistVR.VaultFileDisplayMode") == null; ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AmbientMuter] H3VR 1.0/U120+ install detected: " + IsU120Plus)); SceneManager.sceneLoaded += OnSceneLoaded; _harmony = new Harmony("h3vr.invent60.ambientmuter"); _harmony.PatchAll(typeof(CursedLLPitchFix)); try { _harmony.PatchAll(typeof(MagBoopTimerFix)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[AmbientMuter] MagBoop slow-mo timer fix applied."); } catch { } _runner = ((Component)this).gameObject.AddComponent(); _runner.Log = Log; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[AmbientMuter] Loaded."); } private void BindConfig() { _cfgMuteMusic = ((BaseUnityPlugin)this).Config.Bind("Music", "Mute map music", true, "Zeros the FMOD music bus every frame, silencing background music in all maps (TNH, outdoor assault maps, etc.). Overrides any other mod that sets music volume."); _cfgMuteAmbient = ((BaseUnityPlugin)this).Config.Bind("Ambient", "Mute ambient sounds", true, "Mutes map ambient AudioSources (wind loops, propaganda speakers, environment drones). Applied once per scene load after the sweep delay."); _cfgSweepDelay = ((BaseUnityPlugin)this).Config.Bind("Ambient", "Sweep delay seconds", 4f, "Seconds to wait after scene load before sweeping ambient sources. Increase if some ambient sounds are not being caught. (0 to 30)"); _cfgKeepInteractive = ((BaseUnityPlugin)this).Config.Bind("Ambient", "Keep interactive object sounds", true, "Buttons, levers, switches, and other interactive map objects keep their sounds. Disable to also silence these."); _cfgKeepFootsteps = ((BaseUnityPlugin)this).Config.Bind("Player", "Keep footstep mod sounds", true, "Footstep mods (PlayerFootsteps, AlyxFootsteps, EFT Frame, BF4 Footsteps) are not silenced. Disable to mute footstep sounds too."); _cfgMutePlayerHurt = ((BaseUnityPlugin)this).Config.Bind("Player", "Mute player hurt sounds", false, "Mutes pain and impact sounds when the player takes damage."); } private void Update() { float timeScale = Time.timeScale; bool flag = timeScale < 0.9999f && timeScale > 0.001f; bool wasInSlowMo = CursedLLPitchFix.WasInSlowMo; if (flag) { CursedLLPitchFix.LastSlowTimeScale = timeScale; } CursedLLPitchFix.CachedTimeScale = timeScale; CursedLLPitchFix.WasInSlowMo = flag; if (wasInSlowMo && !flag) { CursedLLPitchFix.RestoreScaledSources(); } if (CursedLLPitchFix.PatcherDetected && !_calibrated && _calibrationAttempts < 5 && Time.timeScale < 0.99f && Time.timeScale > 0.001f) { _calibrationAttempts++; MeasurePitchCompound(); _calibrated = CursedLLPitchFix.Initialized; } if (_cfgMuteMusic.Value) { if (IsU120Plus) { MusicBusMuter.TryMute(); } else { LegacyMusicBusMuter.TryMute(); } } } private void MeasurePitchCompound() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown try { float cachedTimeScale = CursedLLPitchFix.CachedTimeScale; if (cachedTimeScale <= 0.001f || cachedTimeScale >= 0.999f) { return; } GameObject val = new GameObject("__AMCalib"); AudioSource val2 = val.AddComponent(); val2.volume = 0f; val2.mute = true; val2.pitch = 1f; float pitch = val2.pitch; Object.Destroy((Object)(object)val); if (pitch <= 0f) { return; } float num = Mathf.Log(cachedTimeScale); float num2 = Mathf.Log(pitch); if (!(Mathf.Abs(num) < 0.0001f)) { int num3 = Mathf.RoundToInt(num2 / num); if (num3 > 0) { CursedLLPitchFix.CompoundLevels = num3; CursedLLPitchFix.ExtraCompounders = Mathf.Max(0, num3 - 1); CursedLLPitchFix.CursedActive = true; CursedLLPitchFix.Initialized = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AmbientMuter] CursedLL calibrated: {num3} compound level(s), {CursedLLPitchFix.ExtraCompounders} extra (ts={cachedTimeScale:F3})"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[AmbientMuter] CursedLL calibration error: " + ex.Message)); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (IsU120Plus) { MusicBusMuter.Reset(); } else { LegacyMusicBusMuter.Reset(); } if (_cfgMuteAmbient.Value) { ((MonoBehaviour)_runner).Invoke("RunMuteAmbient", _cfgSweepDelay.Value); } } private void MuteAmbientSources() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) bool value = _cfgKeepFootsteps.Value; bool value2 = _cfgKeepInteractive.Value; bool value3 = _cfgMutePlayerHurt.Value; int num = 0; int num2 = 0; AudioSource[] array = Object.FindObjectsOfType(); foreach (AudioSource val in array) { if ((Object)(object)((Component)val).GetComponent() != (Object)null) { num2++; continue; } if (value) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).name == "DontDestroyOnLoad") { num2++; continue; } } if (value2 && (Object)(object)((Component)val).GetComponentInParent() != (Object)null) { num2++; continue; } if (!value2 && (Object)(object)((Component)val).GetComponentInParent() != (Object)null) { num2++; continue; } if ((Object)(object)((Component)val).GetComponentInParent() != (Object)null) { num2++; continue; } FVRMovementManager componentInParent = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && !value3) { num2++; continue; } if (value && (Object)(object)((Component)val).GetComponentInParent() != (Object)null) { num2++; continue; } val.mute = true; num++; } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AmbientMuter] Scene sweep done. Muted: {num}, Kept: {num2}"); } internal void RunMuteAmbientSources() { MuteAmbientSources(); } } internal static class MusicBusMuter { private static object _bus; private static MethodInfo _setVolume; private static bool _obtained; private static bool _unavailable; internal static void TryMute() { if (_unavailable || (!_obtained && !TryGetBus())) { return; } try { _setVolume.Invoke(_bus, new object[1] { 0f }); } catch { _obtained = false; } } private static bool TryGetBus() { try { Type type = Type.GetType("FMODUnity.RuntimeManager, Assembly-CSharp-firstpass"); if ((object)type == null) { _unavailable = true; return false; } MethodInfo method = type.GetMethod("GetBus", new Type[1] { typeof(string) }); if ((object)method == null) { _unavailable = true; return false; } _bus = method.Invoke(null, new object[1] { "bus:/Music" }); if (_bus == null) { _unavailable = true; return false; } _setVolume = _bus.GetType().GetMethod("setVolume", new Type[1] { typeof(float) }); if ((object)_setVolume == null) { _unavailable = true; return false; } _obtained = true; return true; } catch { _unavailable = true; return false; } } internal static void Reset() { _obtained = false; } } internal static class LegacyMusicBusMuter { private static object _bus; private static MethodInfo _setVolume; private static bool _obtained; private static bool _unavailable; internal static void TryMute() { if (_unavailable || (!_obtained && !TryGetBus())) { return; } try { _setVolume.Invoke(_bus, new object[1] { 0f }); } catch { _obtained = false; } } private static bool TryGetBus() { try { Type type = Type.GetType("FMODUnity.RuntimeManager, Assembly-CSharp-firstpass"); if ((object)type == null) { _unavailable = true; return false; } MethodInfo method = type.GetMethod("GetBus", new Type[1] { typeof(string) }); if ((object)method == null) { _unavailable = true; return false; } _bus = method.Invoke(null, new object[1] { "bus:/Music" }); if (_bus == null) { _unavailable = true; return false; } _setVolume = _bus.GetType().GetMethod("setVolume", new Type[1] { typeof(float) }); if ((object)_setVolume == null) { _unavailable = true; return false; } _obtained = true; return true; } catch { _unavailable = true; return false; } } internal static void Reset() { _obtained = false; } } internal sealed class AmbientMuterRunner : MonoBehaviour { internal ManualLogSource Log; private AmbientMuterPlugin _plugin; private void Awake() { _plugin = ((Component)this).GetComponent(); } private void Start() { ((MonoBehaviour)this).StartCoroutine(DetectCursedLL()); } internal void RunMuteAmbient() { _plugin?.RunMuteAmbientSources(); } internal IEnumerator DetectCursedLL() { yield return null; try { PropertyInfo property = typeof(AudioSource).GetProperty("pitch"); if ((object)property == null) { yield break; } MethodInfo setMethod = property.GetSetMethod(); if ((object)setMethod == null) { yield break; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)setMethod); if (patchInfo == null) { yield break; } int num = 0; foreach (Patch prefix in patchInfo.Prefixes) { if (!string.Equals(prefix.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase)) { Log.LogInfo((object)("[AmbientMuter] pitch-setter prefix: " + prefix.owner)); num++; } } foreach (Patch postfix in patchInfo.Postfixes) { if (!string.Equals(postfix.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase)) { Log.LogInfo((object)("[AmbientMuter] pitch-setter postfix: " + postfix.owner)); num++; } } foreach (Patch transpiler in patchInfo.Transpilers) { if (!string.Equals(transpiler.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase)) { Log.LogInfo((object)("[AmbientMuter] pitch-setter transpiler: " + transpiler.owner)); num++; } } if (num > 0) { CursedLLPitchFix.PatcherDetected = true; Log.LogInfo((object)("[AmbientMuter] pitch-setter patcher(s) found (" + num + "). Calibration deferred to first slow-mo.")); } else { Log.LogInfo((object)"[AmbientMuter] CursedLL pitch fix: not needed"); } } catch { } } } [HarmonyPatch] public static class MagBoopTimerFix { private static MethodBase TargetMethod() { return AccessTools.Method("MagBoop.MagTriggerScript:Update", (Type[])null, (Type[])null); } [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { MethodInfo getMethod = typeof(Time).GetProperty("fixedUnscaledDeltaTime").GetGetMethod(); List list = instructions.ToList(); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Call && list[i].operand is MethodInfo methodInfo && (object)methodInfo.DeclaringType == typeof(Time) && methodInfo.Name == "get_fixedDeltaTime") { list[i].operand = getMethod; } } return list; } } public static class CursedLLPitchFix { public static bool PatcherDetected = false; public static bool Initialized = false; public static bool CursedActive = false; public static int CompoundLevels = 0; public static int ExtraCompounders = 0; public static float CachedTimeScale = 1f; public static bool WasInSlowMo = false; public static float LastSlowTimeScale = 1f; private static readonly HashSet _scaledSources = new HashSet(); [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void CompensatePitchRead(ref float __result) { if (Initialized && CursedActive && !(CachedTimeScale >= 0.9999f) && !(CachedTimeScale <= 0.001f)) { __result /= CachedTimeScale; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void PreCompensatePitchSet(AudioSource __instance, ref float value) { if (!Initialized || !CursedActive || CachedTimeScale >= 0.9999f || CachedTimeScale <= 0.001f) { return; } if (ExtraCompounders > 0) { for (int i = 0; i < ExtraCompounders; i++) { value /= CachedTimeScale; } } if ((Object)(object)__instance != (Object)null) { _scaledSources.Add(__instance); } } public static void RestoreScaledSources() { float lastSlowTimeScale = LastSlowTimeScale; if (_scaledSources.Count == 0) { return; } if (lastSlowTimeScale <= 0.001f || lastSlowTimeScale >= 0.9999f) { _scaledSources.Clear(); return; } foreach (AudioSource scaledSource in _scaledSources) { if (!((Object)(object)scaledSource == (Object)null)) { scaledSource.pitch /= lastSlowTimeScale; } } _scaledSources.Clear(); } }