Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PeashooterSoundboard v2.0.0
PeashooterSoundboardremake.dll
Decompiled 2 weeks agousing 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 } } }; } }