Decompiled source of DKyzaNoSawSound v1.1.4

BepInEx/plugins/DKyzaNoSawSound/DKyzaNoSawSound.dll

Decompiled 2 days ago
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<int, SawAudioState> sawSources = new Dictionary<int, SawAudioState>();

	private ConfigEntry<float> sawVolumePercent;

	private ConfigEntry<string> keywordsConfig;

	private ConfigEntry<bool> 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<float>("Volume", "SawVolumePercent", 0f, new ConfigDescription("Saw volume. 0 is silent and 100 is the normal game volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[0]));
		keywordsConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Detection", "Keywords", "saw,sawblade,buzzsaw,circularsaw", "Comma-separated words checked in audio clip, object and parent names.");
		logMatchesConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("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<GameObject>)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<float>)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<string> list = new List<string>();
		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<AudioSource>();
		HashSet<int> hashSet = new HashSet<int>();
		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<int> list = null;
		foreach (KeyValuePair<int, SawAudioState> sawSource in sawSources)
		{
			SawAudioState value = sawSource.Value;
			if ((Object)(object)value.Source == (Object)null)
			{
				if (list == null)
				{
					list = new List<int>();
				}
				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 : "<no clip>");
				((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<int, SawAudioState> 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<int> list = new List<int>(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 "<null>";
		}
		List<string> list = new List<string>();
		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<int> list = new List<int>(sawSources.Keys);
		for (int i = 0; i < list.Count; i++)
		{
			RestoreSource(list[i]);
		}
		if ((Object)(object)instance == (Object)(object)this)
		{
			instance = null;
		}
	}
}