Decompiled source of PeashooterSoundboard v2.0.0

PeashooterSoundboardremake.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using LethalNetworkAPI;
using PeashooterSoundboardremake.Audio;
using PeashooterSoundboardremake.Runtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PeashooterSoundboardremake")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PeashooterSoundboardremake")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("9d878c88-8e69-45ad-80f3-897a0d8733f4")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace PeashooterSoundboardremake
{
	[BepInPlugin("Che3Bee-PeashooterSoundboard", "PeashooterSoundboard", "2.0.0")]
	public class SoundboardMod : BaseUnityPlugin
	{
		public struct SoundEventData
		{
			public string group;

			public int index;

			public Vector3 position;

			public bool deadAudio;
		}

		private Dictionary<string, List<AudioClip>> groups;

		private CooldownController cooldown;

		private SoundPlayback playback;

		private static LNetworkMessage<SoundEventData> soundMessage;

		private void Awake()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing Peashooter Soundboard API");
			cooldown = new CooldownController();
			playback = new SoundPlayback(((Component)this).gameObject);
			groups = SoundGroupBuilder.BuildAll(((BaseUnityPlugin)this).Logger);
			RegisterNetworkMessage();
		}

		private void RegisterNetworkMessage()
		{
			soundMessage = LNetworkMessage<SoundEventData>.Connect("CheePeashooterSoundboard", (Action<SoundEventData, ulong>)null, (Action<SoundEventData>)null, (Action<SoundEventData, ulong>)OnClientReceivedFromClient);
		}

		private void OnClientReceivedFromClient(SoundEventData data, ulong senderId)
		{
			playback.PlayReceived(groups, data);
		}

		private void Update()
		{
			cooldown.Tick();
			Keyboard current = Keyboard.current;
			if (current != null)
			{
				if (((ButtonControl)current.numpad7Key).wasPressedThisFrame)
				{
					TryPlay("ps_idle");
				}
				if (((ButtonControl)current.numpad8Key).wasPressedThisFrame)
				{
					TryPlay("ps_excited");
				}
				if (((ButtonControl)current.numpad9Key).wasPressedThisFrame)
				{
					TryPlay("ps_taunt");
				}
				if (((ButtonControl)current.numpad4Key).wasPressedThisFrame)
				{
					TryPlay("pc_excited");
				}
				if (((ButtonControl)current.numpad5Key).wasPressedThisFrame)
				{
					TryPlay("pc_death");
				}
				if (((ButtonControl)current.numpad6Key).wasPressedThisFrame)
				{
					TryPlay("ps_yes");
				}
				if (((ButtonControl)current.numpad3Key).wasPressedThisFrame)
				{
					TryPlay("ps_no");
				}
				if (((ButtonControl)current.enterKey).wasPressedThisFrame)
				{
					TryPlay("ps_silly");
				}
				if (((ButtonControl)current.rightCtrlKey).isPressed && ((ButtonControl)current.numpad0Key).wasPressedThisFrame)
				{
					TryPlay("scream");
				}
			}
		}

		private void TryPlay(string group)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (cooldown.CanPlay(group))
			{
				PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController;
				if (!((Object)(object)localPlayerController == (Object)null) && groups.ContainsKey(group) && groups[group].Count != 0)
				{
					int index = Random.Range(0, groups[group].Count);
					AudioClip clip = groups[group][index];
					bool isPlayerDead = localPlayerController.isPlayerDead;
					playback.PlayLocal(clip, group, ((Component)localPlayerController).transform.position, isPlayerDead);
					soundMessage.SendOtherClients(new SoundEventData
					{
						group = group,
						index = index,
						position = ((Component)localPlayerController).transform.position,
						deadAudio = isPlayerDead
					});
					cooldown.OnPlay(group);
				}
			}
		}
	}
}
namespace PeashooterSoundboardremake.Runtime
{
	public class CooldownController
	{
		private float lastPlay;

		private float dynamicCooldown = 1.6f;

		private const float baseCooldown = 1.6f;

		private const float maxCooldown = 4f;

		private const float recoveryRate = 0.5f;

		public void Tick()
		{
			if (dynamicCooldown > 1.6f)
			{
				dynamicCooldown -= Time.deltaTime * 0.5f;
				if (dynamicCooldown < 1.6f)
				{
					dynamicCooldown = 1.6f;
				}
			}
		}

		private float GetCooldownFor(string group)
		{
			if (SoundGroupRules.Rules.TryGetValue(group, out var value) && value.CooldownOverride > 0f)
			{
				return value.CooldownOverride;
			}
			return dynamicCooldown;
		}

		public bool CanPlay(string group)
		{
			float cooldownFor = GetCooldownFor(group);
			return Time.time - lastPlay >= cooldownFor;
		}

		public void OnPlay(string group)
		{
			lastPlay = Time.time;
			if (!SoundGroupRules.Rules.TryGetValue(group, out var value) || !value.IgnoreSpamPenalty)
			{
				dynamicCooldown = Mathf.Min(dynamicCooldown + 0.25f, 4f);
			}
		}
	}
	public class SoundPlayback
	{
		private readonly AudioSource deadSource;

		public SoundPlayback(GameObject host)
		{
			deadSource = host.AddComponent<AudioSource>();
			deadSource.spatialBlend = 0f;
		}

		private float ApplyVolume(string group, float baseVolume)
		{
			if (SoundGroupRules.Rules.TryGetValue(group, out var value))
			{
				return baseVolume * value.VolumeMultiplier;
			}
			return baseVolume;
		}

		private float ApplyRadius(string group, float baseRadius)
		{
			if (SoundGroupRules.Rules.TryGetValue(group, out var value))
			{
				return baseRadius * value.RadiusMultiplier;
			}
			return baseRadius;
		}

		public void PlayLocal(AudioClip clip, string group, Vector3 pos, bool dead)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			float num = ApplyVolume(group, 1f);
			if (dead)
			{
				deadSource.PlayOneShot(clip, num);
			}
			else
			{
				PlayWorld(clip, pos, group, num);
			}
		}

		public void PlayReceived(Dictionary<string, List<AudioClip>> groups, SoundboardMod.SoundEventData data)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (groups.ContainsKey(data.group))
			{
				AudioClip val = groups[data.group][data.index];
				float num = ApplyVolume(data.group, 1f);
				if (data.deadAudio)
				{
					deadSource.PlayOneShot(val, num);
				}
				else
				{
					PlayWorld(val, data.position, data.group, num);
				}
			}
		}

		private void PlayWorld(AudioClip clip, Vector3 pos, string group, float volume)
		{
			//IL_0010: 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)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			GameObject val = new GameObject("Sound_" + ((Object)clip).name);
			val.transform.position = pos;
			AudioSource obj = val.AddComponent<AudioSource>();
			obj.spatialBlend = 1f;
			obj.minDistance = 2f;
			obj.maxDistance = 25f * ApplyRadius(group, 1f);
			obj.PlayOneShot(clip, volume);
			Object.Destroy((Object)val, clip.length + 0.1f);
		}
	}
}
namespace PeashooterSoundboardremake.Audio
{
	public class EmbeddedAudioLoader
	{
		private readonly ManualLogSource _logger;

		private readonly Assembly _assembly;

		private readonly string[] _resources;

		public EmbeddedAudioLoader(ManualLogSource logger)
		{
			_logger = logger;
			_assembly = Assembly.GetExecutingAssembly();
			_resources = _assembly.GetManifestResourceNames();
		}

		public void LoadGroup(Dictionary<string, List<AudioClip>> groups, string groupName, string prefix)
		{
			List<AudioClip> list = new List<AudioClip>();
			string[] resources = _resources;
			foreach (string text in resources)
			{
				if (text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && text.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
				{
					AudioClip val = LoadWav(text);
					if ((Object)(object)val != (Object)null)
					{
						list.Add(val);
					}
				}
			}
			groups[groupName] = list;
			_logger.LogInfo((object)$"Loaded {list.Count} embedded clips for '{groupName}'");
		}

		public void AddSpecific(List<AudioClip> list, string resourceName)
		{
			if (!_resources.Contains(resourceName))
			{
				_logger.LogWarning((object)("Missing embedded clip: " + resourceName));
				return;
			}
			AudioClip val = LoadWav(resourceName);
			if ((Object)(object)val != (Object)null)
			{
				list.Add(val);
			}
		}

		private AudioClip LoadWav(string resourceName)
		{
			try
			{
				using Stream stream = _assembly.GetManifestResourceStream(resourceName);
				if (stream == null)
				{
					_logger.LogError((object)("Null stream for: " + resourceName));
					return null;
				}
				byte[] array = new byte[stream.Length];
				stream.Read(array, 0, array.Length);
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(resourceName);
				return WavUtility.ToAudioClip(array, fileNameWithoutExtension);
			}
			catch (Exception arg)
			{
				_logger.LogError((object)$"Failed to load WAV '{resourceName}': {arg}");
				return null;
			}
		}
	}
	public static class SoundGroupBuilder
	{
		public static Dictionary<string, List<AudioClip>> BuildAll(ManualLogSource logger)
		{
			EmbeddedAudioLoader embeddedAudioLoader = new EmbeddedAudioLoader(logger);
			Dictionary<string, List<AudioClip>> dictionary = new Dictionary<string, List<AudioClip>>();
			string text = "PeashooterSoundboardremake.EmbeddedAudio";
			embeddedAudioLoader.LoadGroup(dictionary, "ps_idle", text + ".ps_idle");
			embeddedAudioLoader.LoadGroup(dictionary, "ps_excited", text + ".ps_excited");
			embeddedAudioLoader.LoadGroup(dictionary, "ps_taunt", text + ".ps_taunt");
			embeddedAudioLoader.LoadGroup(dictionary, "pc_excited", text + ".pc_excited");
			embeddedAudioLoader.LoadGroup(dictionary, "pc_death", text + ".pc_death");
			embeddedAudioLoader.LoadGroup(dictionary, "scream", text + ".scream");
			BuildCustomGroups(dictionary, embeddedAudioLoader, text);
			return dictionary;
		}

		private static void BuildCustomGroups(Dictionary<string, List<AudioClip>> groups, EmbeddedAudioLoader loader, string root)
		{
			groups["ps_yes"] = new List<AudioClip>();
			loader.AddSpecific(groups["ps_yes"], root + ".ps_taunt.ps_taunt_25.wav");
			loader.AddSpecific(groups["ps_yes"], root + ".ps_taunt.ps_taunt_42.wav");
			loader.AddSpecific(groups["ps_yes"], root + ".ps_taunt.ps_taunt_37.wav");
			groups["ps_no"] = new List<AudioClip>();
			loader.AddSpecific(groups["ps_no"], root + ".ps_taunt.ps_taunt_23.wav");
			loader.AddSpecific(groups["ps_no"], root + ".ps_taunt.ps_taunt_24.wav");
			groups["ps_silly"] = new List<AudioClip>();
			loader.AddSpecific(groups["ps_silly"], root + ".ps_taunt.ps_taunt_11.wav");
			loader.AddSpecific(groups["ps_silly"], root + ".ps_taunt.ps_taunt_15.wav");
			loader.AddSpecific(groups["ps_silly"], root + ".ps_taunt.ps_taunt_16.wav");
		}
	}
	public static class WavUtility
	{
		public static AudioClip ToAudioClip(byte[] wav, string name)
		{
			int num = BitConverter.ToInt16(wav, 22);
			int num2 = BitConverter.ToInt32(wav, 24);
			int num3 = FindDataChunk(wav);
			int num4 = BitConverter.ToInt32(wav, num3 + 4) / 2;
			float[] array = new float[num4];
			int num5 = num3 + 8;
			for (int i = 0; i < num4; i++)
			{
				short num6 = BitConverter.ToInt16(wav, num5 + i * 2);
				array[i] = (float)num6 / 32768f;
			}
			AudioClip obj = AudioClip.Create(name, num4 / num, num, num2, false);
			obj.SetData(array, 0);
			return obj;
		}

		private static int FindDataChunk(byte[] wav)
		{
			for (int i = 12; i < wav.Length - 8; i++)
			{
				if (wav[i] == 100 && wav[i + 1] == 97 && wav[i + 2] == 116 && wav[i + 3] == 97)
				{
					return i;
				}
			}
			throw new Exception("WAV missing data chunk");
		}
	}
	public static class SoundGroupRules
	{
		public class Rule
		{
			public float VolumeMultiplier = 1f;

			public float RadiusMultiplier = 1f;

			public float CooldownOverride = -1f;

			public bool IgnoreSpamPenalty;
		}

		public static readonly Dictionary<string, Rule> Rules = new Dictionary<string, Rule>
		{
			{
				"scream",
				new Rule
				{
					VolumeMultiplier = 1.5f,
					RadiusMultiplier = 2.5f,
					CooldownOverride = 20f,
					IgnoreSpamPenalty = true
				}
			},
			{
				"ps_excited",
				new Rule
				{
					VolumeMultiplier = 0.85f,
					RadiusMultiplier = 1.35f
				}
			}
		};
	}
}