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