using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using TMPro; using UnboundLib; using UnboundLib.Utils.UI; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace DKyzaNoSawSound; [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.dkyza.rounds.nosawsound", "DKyzaNoSawSound", "1.1.4")] [BepInProcess("Rounds.exe")] public sealed class NoSawSoundPlugin : BaseUnityPlugin { private sealed class SawAudioState { public AudioSource Source; public float BaseVolume; public bool BaseMute; public float LastAppliedVolume; public bool LastAppliedMute; public bool HasApplied; } public const string ModId = "com.dkyza.rounds.nosawsound"; public const string ModName = "DKyzaNoSawSound"; public const string ModVersion = "1.1.4"; private static NoSawSoundPlugin instance; private readonly Dictionary sawSources = new Dictionary(); private ConfigEntry sawVolumePercent; private ConfigEntry keywordsConfig; private ConfigEntry logMatchesConfig; private string[] normalizedKeywords; private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown instance = this; sawVolumePercent = ((BaseUnityPlugin)this).Config.Bind("Volume", "SawVolumePercent", 0f, new ConfigDescription("Saw volume. 0 is silent and 100 is the normal game volume.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[0])); keywordsConfig = ((BaseUnityPlugin)this).Config.Bind("Detection", "Keywords", "saw,sawblade,buzzsaw,circularsaw", "Comma-separated words checked in audio clip, object and parent names."); logMatchesConfig = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogDetectedSawSounds", false, "Write every detected saw AudioSource to the BepInEx log."); ReloadKeywords(); keywordsConfig.SettingChanged += OnKeywordsChanged; sawVolumePercent.SettingChanged += OnVolumeConfigChanged; } private void Start() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Unbound.RegisterMenu("DKyzaNoSawSound", new UnityAction(OnMenuOpened), (Action)BuildModMenu, (GameObject)null, false); ((BaseUnityPlugin)this).Logger.LogInfo((object)("DKyzaNoSawSound loaded. Current saw volume: " + sawVolumePercent.Value.ToString("0") + "%")); } private static void OnMenuOpened() { } private static void BuildModMenu(GameObject menu) { if (!((Object)(object)instance == (Object)null)) { TextMeshProUGUI val = default(TextMeshProUGUI); MenuHandler.CreateText("DKyzaNoSawSound", menu, ref val, 60, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null); TextMeshProUGUI val2 = default(TextMeshProUGUI); MenuHandler.CreateText("0% = silent 100% = normal game volume", menu, ref val2, 28, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null); TextMeshProUGUI val3 = default(TextMeshProUGUI); MenuHandler.CreateText(" ", menu, ref val3, 18, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null); Slider val4 = default(Slider); MenuHandler.CreateSlider("Saw volume (%)", menu, 34, 0f, 100f, instance.sawVolumePercent.Value, (UnityAction)instance.OnVolumeSliderChanged, ref val4, true, (Color?)null, (Direction)0, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null); } } private void OnVolumeSliderChanged(float value) { float value2 = Mathf.Clamp(Mathf.Round(value), 0f, 100f); sawVolumePercent.Value = value2; ApplyVolumeToTrackedSources(); } private void OnVolumeConfigChanged(object sender, EventArgs args) { ApplyVolumeToTrackedSources(); } private void OnKeywordsChanged(object sender, EventArgs args) { ReloadKeywords(); } private void ReloadKeywords() { string text = ((keywordsConfig != null) ? keywordsConfig.Value : "saw,sawblade,buzzsaw,circularsaw"); string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); List list = new List(); for (int i = 0; i < array.Length; i++) { string text2 = Normalize(array[i]); if (!string.IsNullOrEmpty(text2) && !list.Contains(text2)) { list.Add(text2); } } normalizedKeywords = list.ToArray(); } private void LateUpdate() { float num = ((sawVolumePercent != null) ? Mathf.Clamp(sawVolumePercent.Value, 0f, 100f) : 0f); if (num >= 99.5f) { RestoreAllSources(); return; } AudioSource[] array = Object.FindObjectsOfType(); HashSet hashSet = new HashSet(); foreach (AudioSource val in array) { if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); if (IsSawSource(val)) { hashSet.Add(instanceID); TrackAndApply(val, instanceID); } else if (sawSources.ContainsKey(instanceID)) { RestoreSource(instanceID); } } } List list = null; foreach (KeyValuePair sawSource in sawSources) { SawAudioState value = sawSource.Value; if ((Object)(object)value.Source == (Object)null) { if (list == null) { list = new List(); } list.Add(sawSource.Key); } else if (hashSet.Contains(sawSource.Key)) { UpdateBaseFromExternalChanges(value); ApplyVolume(value); } } if (list != null) { for (int j = 0; j < list.Count; j++) { sawSources.Remove(list[j]); } } } private void TrackAndApply(AudioSource source, int id) { if (!sawSources.TryGetValue(id, out var value)) { value = new SawAudioState(); value.Source = source; value.BaseVolume = source.volume; value.BaseMute = source.mute; value.LastAppliedVolume = source.volume; value.LastAppliedMute = source.mute; value.HasApplied = false; sawSources.Add(id, value); if (logMatchesConfig != null && logMatchesConfig.Value) { string text = (((Object)(object)source.clip != (Object)null) ? ((Object)source.clip).name : ""); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Detected saw sound: object='" + GetHierarchyName(((Component)source).transform) + "', clip='" + text + "'")); } } else { UpdateBaseFromExternalChanges(value); } ApplyVolume(value); } private void UpdateBaseFromExternalChanges(SawAudioState state) { if (state != null && !((Object)(object)state.Source == (Object)null) && state.HasApplied) { bool flag = Mathf.Abs(state.Source.volume - state.LastAppliedVolume) > 0.001f; bool flag2 = state.Source.mute != state.LastAppliedMute; if (flag) { state.BaseVolume = state.Source.volume; } if (flag2) { state.BaseMute = state.Source.mute; } } } private void ApplyVolumeToTrackedSources() { float num = ((sawVolumePercent != null) ? Mathf.Clamp(sawVolumePercent.Value, 0f, 100f) : 0f); if (num >= 99.5f) { RestoreAllSources(); return; } foreach (KeyValuePair sawSource in sawSources) { if ((Object)(object)sawSource.Value.Source != (Object)null) { UpdateBaseFromExternalChanges(sawSource.Value); ApplyVolume(sawSource.Value); } } } private void ApplyVolume(SawAudioState state) { if (state == null || (Object)(object)state.Source == (Object)null) { return; } float num = ((sawVolumePercent != null) ? Mathf.Clamp(sawVolumePercent.Value, 0f, 100f) : 0f); if (num >= 99.5f) { RestoreState(state); return; } if (num <= 0.01f) { state.Source.volume = 0f; state.Source.mute = true; } else { state.Source.mute = state.BaseMute; state.Source.volume = state.BaseVolume * (num / 100f); } state.LastAppliedVolume = state.Source.volume; state.LastAppliedMute = state.Source.mute; state.HasApplied = true; } private void RestoreState(SawAudioState state) { if (state != null && !((Object)(object)state.Source == (Object)null)) { state.Source.volume = state.BaseVolume; state.Source.mute = state.BaseMute; state.LastAppliedVolume = state.Source.volume; state.LastAppliedMute = state.Source.mute; state.HasApplied = false; } } private void RestoreAllSources() { List list = new List(sawSources.Keys); for (int i = 0; i < list.Count; i++) { RestoreSource(list[i]); } } private void RestoreSource(int id) { if (sawSources.TryGetValue(id, out var value)) { RestoreState(value); sawSources.Remove(id); } } private bool IsSawSource(AudioSource source) { StringBuilder stringBuilder = new StringBuilder(); if ((Object)(object)source.clip != (Object)null) { stringBuilder.Append(((Object)source.clip).name); stringBuilder.Append(' '); } Transform val = ((Component)source).transform; int num = 0; while ((Object)(object)val != (Object)null && num < 12) { stringBuilder.Append(((Object)val).name); stringBuilder.Append(' '); val = val.parent; num++; } string text = Normalize(stringBuilder.ToString()); for (int i = 0; i < normalizedKeywords.Length; i++) { if (text.Contains(normalizedKeywords[i])) { return true; } } return false; } private static string GetHierarchyName(Transform transform) { if ((Object)(object)transform == (Object)null) { return ""; } List list = new List(); Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 12) { list.Add(((Object)val).name); val = val.parent; num++; } list.Reverse(); return string.Join("/", list.ToArray()); } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private void OnDestroy() { if (keywordsConfig != null) { keywordsConfig.SettingChanged -= OnKeywordsChanged; } if (sawVolumePercent != null) { sawVolumePercent.SettingChanged -= OnVolumeConfigChanged; } List list = new List(sawSources.Keys); for (int i = 0; i < list.Count; i++) { RestoreSource(list[i]); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } }