using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FMOD; using FMODUnity; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("VoiceGrunts")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("VoiceGrunts")] [assembly: AssemblyTitle("VoiceGrunts")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace VoiceGrunts { internal static class AnnouncerAudio { private static PropertyInfo _announcerVolumeProperty; private static bool _bindSucceeded; internal static void Initialize() { if (_announcerVolumeProperty != null) { return; } Type nestedType = typeof(GameSettings).GetNestedType("AudioSettings", BindingFlags.Public | BindingFlags.NonPublic); if (nestedType == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Could not find GameSettings.AudioSettings for announcer volume routing."); } return; } _announcerVolumeProperty = AccessTools.Property(nestedType, "AnnouncerVolume"); if (_announcerVolumeProperty == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Could not find AnnouncerVolume property for announcer volume routing."); } return; } _bindSucceeded = true; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"Announcer volume routing bound to GameSettings.All.Audio.AnnouncerVolume."); } } internal static bool TryGetAnnouncerVolume(out float volume) { volume = 1f; if (!_bindSucceeded || _announcerVolumeProperty == null) { return false; } try { AllSettings all = GameSettings.All; object obj = ((all != null) ? all.Audio : null); if (obj == null) { return false; } volume = (float)_announcerVolumeProperty.GetValue(obj); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Announcer volume unavailable: " + ex.Message)); } return false; } } internal static void ApplyToChannel(Channel channel) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (((Channel)(ref channel)).hasHandle() && TryGetAnnouncerVolume(out var volume)) { ((Channel)(ref channel)).setVolume(volume); } } } internal static class GameCursor { private static readonly MethodInfo SetCursorForceUnlockedMethod = AccessTools.Method(typeof(CursorManager), "SetCursorForceUnlocked", new Type[1] { typeof(bool) }, (Type[])null); private static readonly MethodInfo UpdateCursorLockMethod = AccessTools.Method(typeof(InputManager), "UpdateCursorLock", (Type[])null, (Type[])null); private static readonly MethodInfo ApplyCursorLockMethod = AccessTools.Method(typeof(CursorManager), "ApplyCursorLock", (Type[])null, (Type[])null); internal static void SetOverlayOpen(bool open) { try { SetCursorForceUnlockedMethod?.Invoke(null, new object[1] { open }); if (!open) { UpdateCursorLockMethod?.Invoke(null, null); if (SingletonBehaviour.HasInstance) { ApplyCursorLockMethod?.Invoke(SingletonBehaviour.Instance, null); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"GameCursor.SetOverlayOpen({open}) failed: {ex.Message}"); } } if (open) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } internal static void RestoreAfterOverlay(bool wasVisible, CursorLockMode previousLockState) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) SetOverlayOpen(open: false); if (wasVisible) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } else { Cursor.lockState = (CursorLockMode)(((int)previousLockState == 0) ? 1 : ((int)previousLockState)); Cursor.visible = false; } } } internal enum GruntEventKind { Jump = 1, Knocked } internal static class GruntClipNetworkSync { private static PlayerInfo _knockRpcPlayerInfo; private static readonly MethodInfo KnockUserCodeRpcMethod = AccessTools.Method(typeof(PlayerMovement), "UserCode_RpcInformKnockedOutOtherPlayer__PlayerInfo__Boolean", (Type[])null, (Type[])null); private static readonly MethodInfo RememberKnockRpcPlayerInfoMethod = AccessTools.Method(typeof(GruntClipNetworkSync), "RememberKnockRpcPlayerInfo", (Type[])null, (Type[])null); private static readonly MethodInfo ConsumeKnockClipTrailingByteMethod = AccessTools.Method(typeof(GruntClipNetworkSync), "ConsumeKnockClipTrailingByte", (Type[])null, (Type[])null); internal static void WriteClipByte(NetworkWriter writer, byte clipIndex) { if (writer != null) { writer.WriteByte(clipIndex); } } internal static void ReadTrailingClipByte(NetworkReader reader, ulong playerGuid, GruntEventKind kind) { if (reader != null && playerGuid != 0L && reader.Remaining >= 1) { VoicePackRegistry.SetPendingClipIndex(kind, playerGuid, reader.ReadByte()); } } internal static void RememberKnockRpcPlayerInfo(PlayerInfo knockedOutPlayer) { _knockRpcPlayerInfo = knockedOutPlayer; } internal static void ConsumeKnockClipTrailingByte(NetworkReader reader) { if ((Object)(object)_knockRpcPlayerInfo == (Object)null || (Object)(object)_knockRpcPlayerInfo.PlayerId == (Object)null || reader == null || reader.Remaining < 1) { _knockRpcPlayerInfo = null; return; } ReadTrailingClipByte(reader, _knockRpcPlayerInfo.PlayerId.Guid, GruntEventKind.Knocked); _knockRpcPlayerInfo = null; } internal static void WriteJumpClipForSend(NetworkWriter writer, PlayerAudio playerAudio, GruntEventKind kind) { if (writer != null && !((Object)(object)playerAudio == (Object)null) && kind == GruntEventKind.Jump) { int num = 0; int clipIndex2; if (VoicePackRegistry.TryTakeRpcJumpClipIndex(((NetworkBehaviour)playerAudio).netId, out var clipIndex)) { num = clipIndex; } else if (VoicePackRegistry.TryPeekPendingClipIndex(GruntEventKind.Jump, Plugin.GetPlayerGuid(playerAudio), out clipIndex2)) { num = clipIndex2; } WriteClipByte(writer, (byte)Mathf.Clamp(num, 0, 255)); } } internal static void WriteKnockedClipForSend(NetworkWriter writer, PlayerInfo knockedOutPlayer, GruntEventKind kind) { if (writer != null && !((Object)(object)knockedOutPlayer == (Object)null) && kind == GruntEventKind.Knocked) { ulong num = (((Object)(object)knockedOutPlayer.PlayerId != (Object)null) ? knockedOutPlayer.PlayerId.Guid : 0); int num2 = 0; if (num != 0L && VoicePackRegistry.TryPeekPendingClipIndex(GruntEventKind.Knocked, num, out var clipIndex)) { num2 = clipIndex; } WriteClipByte(writer, (byte)Mathf.Clamp(num2, 0, 255)); } } internal static void PrepareJumpRpcClip(PlayerAudio playerAudio) { if (NetworkServer.active && !((Object)(object)playerAudio == (Object)null) && VoicePackRegistry.TryPeekPendingClipIndex(GruntEventKind.Jump, Plugin.GetPlayerGuid(playerAudio), out var clipIndex)) { VoicePackRegistry.SetRpcJumpClipIndex(((NetworkBehaviour)playerAudio).netId, clipIndex); } } internal static IEnumerable TranspileAppendClipByteAfterWriterGet(IEnumerable instructions, MethodInfo writeMethod, GruntEventKind kind, bool passPlayerInfoArg = false) { List codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { yield return codes[i]; if (i > 0 && !(codes[i].opcode != OpCodes.Stloc_0) && CodeInstructionExtensions.Calls(codes[i - 1], typeof(NetworkWriterPool).GetMethod("Get", BindingFlags.Static | BindingFlags.Public))) { yield return new CodeInstruction(OpCodes.Ldloc_0, (object)null); if (passPlayerInfoArg) { yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null); } else { yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); } yield return new CodeInstruction(OpCodes.Ldc_I4, (object)(int)kind); yield return new CodeInstruction(OpCodes.Call, (object)writeMethod); } } } private static bool IsReadPlayerInfoFromReader(MethodBase method) { if (method == null || method.Name != "ReadNetworkBehaviour" || !method.IsGenericMethod) { return false; } Type[] genericArguments = method.GetGenericArguments(); if (genericArguments.Length == 1) { return genericArguments[0] == typeof(PlayerInfo); } return false; } internal static IEnumerable TranspileReadKnockedClipBeforeUserCode(IEnumerable instructions) { List codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { if (CodeInstructionExtensions.Calls(codes[i], KnockUserCodeRpcMethod)) { yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)ConsumeKnockClipTrailingByteMethod); } yield return codes[i]; if (codes[i].operand is MethodBase method && IsReadPlayerInfoFromReader(method)) { yield return new CodeInstruction(OpCodes.Dup, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)RememberKnockRpcPlayerInfoMethod); } } } } [HarmonyPatch(typeof(PlayerAudio), "CmdPlayJumpForAllClients")] internal static class JumpCmdTranspilerPatch { private static readonly MethodInfo WriteMethod = AccessTools.Method(typeof(GruntClipNetworkSync), "WriteJumpClipForSend", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { return GruntClipNetworkSync.TranspileAppendClipByteAfterWriterGet(instructions, WriteMethod, GruntEventKind.Jump); } } [HarmonyPatch(typeof(PlayerAudio), "RpcPlayJump")] internal static class JumpRpcTranspilerPatch { private static readonly MethodInfo WriteMethod = AccessTools.Method(typeof(GruntClipNetworkSync), "WriteJumpClipForSend", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { return GruntClipNetworkSync.TranspileAppendClipByteAfterWriterGet(instructions, WriteMethod, GruntEventKind.Jump); } } [HarmonyPatch(typeof(PlayerAudio), "InvokeUserCode_CmdPlayJumpForAllClients__NetworkConnectionToClient")] internal static class JumpCmdInvokePatch { [HarmonyPrefix] internal static void Prefix(NetworkBehaviour obj, NetworkReader reader) { GruntClipNetworkSync.ReadTrailingClipByte(reader, Plugin.GetPlayerGuid((PlayerAudio)(object)((obj is PlayerAudio) ? obj : null)), GruntEventKind.Jump); } } [HarmonyPatch(typeof(PlayerAudio), "InvokeUserCode_RpcPlayJump__NetworkConnectionToClient")] internal static class JumpRpcInvokePatch { [HarmonyPrefix] internal static void Prefix(NetworkBehaviour obj, NetworkReader reader) { GruntClipNetworkSync.ReadTrailingClipByte(reader, Plugin.GetPlayerGuid((PlayerAudio)(object)((obj is PlayerAudio) ? obj : null)), GruntEventKind.Jump); } } [HarmonyPatch(typeof(PlayerAudio), "UserCode_CmdPlayJumpForAllClients__NetworkConnectionToClient")] internal static class JumpCmdUserCodePatch { [HarmonyPrefix] internal static void Prefix(PlayerAudio __instance) { GruntClipNetworkSync.PrepareJumpRpcClip(__instance); } } [HarmonyPatch(typeof(PlayerAudio), "PlayJumpForAllClients")] internal static class JumpForAllClientsPatch { [HarmonyPrefix] internal static void Prefix(PlayerAudio __instance) { if (!((Object)(object)__instance == (Object)null) && Plugin.IsLocalPlayer(__instance)) { VoicePackRegistry.SetPendingClipIndex(GruntEventKind.Jump, Plugin.GetPlayerGuid(__instance), Plugin.PickJumpClipIndex(Plugin.GetVoicePackId(__instance))); } } [HarmonyPostfix] internal static void Postfix(PlayerAudio __instance) { if (!((Object)(object)__instance == (Object)null) && Plugin.IsLocalPlayer(__instance)) { VoicePackRegistry.TryConsumePendingClipIndex(GruntEventKind.Jump, Plugin.GetPlayerGuid(__instance), out var _); } } } [HarmonyPatch(typeof(PlayerMovement), "CmdInformKnockedOut")] internal static class KnockCmdTranspilerPatch { private static readonly MethodInfo WriteMethod = AccessTools.Method(typeof(KnockCmdClipWritePatch), "WriteFromLocalMovement", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { return GruntClipNetworkSync.TranspileAppendClipByteAfterWriterGet(instructions, WriteMethod, GruntEventKind.Knocked); } } internal static class KnockCmdClipWritePatch { internal static void WriteFromLocalMovement(NetworkWriter writer, PlayerMovement movement, GruntEventKind kind) { if (writer != null && !((Object)(object)movement == (Object)null) && kind == GruntEventKind.Knocked) { PlayerInfo playerInfo = movement.PlayerInfo; ulong num = (((Object)(object)playerInfo != (Object)null && (Object)(object)playerInfo.PlayerId != (Object)null) ? playerInfo.PlayerId.Guid : 0); int num2 = 0; if (num != 0L && VoicePackRegistry.TryPeekPendingClipIndex(GruntEventKind.Knocked, num, out var clipIndex)) { num2 = clipIndex; } GruntClipNetworkSync.WriteClipByte(writer, (byte)Mathf.Clamp(num2, 0, 255)); } } } [HarmonyPatch(typeof(PlayerMovement), "RpcInformKnockedOutOtherPlayer")] internal static class KnockRpcTranspilerPatch { private static readonly MethodInfo WriteMethod = AccessTools.Method(typeof(GruntClipNetworkSync), "WriteKnockedClipForSend", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { return GruntClipNetworkSync.TranspileAppendClipByteAfterWriterGet(instructions, WriteMethod, GruntEventKind.Knocked, passPlayerInfoArg: true); } } [HarmonyPatch(typeof(PlayerMovement), "InvokeUserCode_CmdInformKnockedOut__PlayerInfo__KnockoutType__Vector3__Single__ItemType__ItemUseId__Boolean")] internal static class KnockCmdInvokePatch { [HarmonyPostfix] internal static void Postfix(NetworkBehaviour obj, NetworkReader reader) { PlayerMovement val = (PlayerMovement)(object)((obj is PlayerMovement) ? obj : null); PlayerInfo val2 = (((Object)(object)val != (Object)null) ? val.PlayerInfo : null); ulong playerGuid = (((Object)(object)val2 != (Object)null && (Object)(object)val2.PlayerId != (Object)null) ? val2.PlayerId.Guid : 0); GruntClipNetworkSync.ReadTrailingClipByte(reader, playerGuid, GruntEventKind.Knocked); } } [HarmonyPatch(typeof(PlayerMovement), "InvokeUserCode_RpcInformKnockedOutOtherPlayer__PlayerInfo__Boolean")] internal static class KnockRpcInvokeTranspilerPatch { [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { return GruntClipNetworkSync.TranspileReadKnockedClipBeforeUserCode(instructions); } } [BepInPlugin("sbg.voicegrunts", "VoiceGrunts", "1.4.11")] public class Plugin : BaseUnityPlugin { private sealed class SoundVariant { public string RelativePath; public Sound Sound2D; public Sound Sound3D; } internal static class SwingNetworkPatch { internal static void EncodeLocalPackPrefix(PlayerAudio __instance, ref float normalizedPower) { if (!((Object)(object)__instance == (Object)null) && IsLocalPlayer(__instance) && (((NetworkBehaviour)__instance).isClient || ((NetworkBehaviour)__instance).isServer)) { int clipIndex = PickSwingClipIndex(SelectedVoicePack, normalizedPower); VoicePackRegistry.SetPendingSwingClipIndex(GetPlayerGuid(__instance), clipIndex); normalizedPower = VoicePackRegistry.EncodeSwing(normalizedPower, SelectedVoicePack, clipIndex); } } internal static void CmdPlaySwingPrefix(PlayerAudio __instance, ref float normalizedPower, bool isUsingRocketDriver, NetworkConnectionToClient sender) { if (NetworkServer.active && !((Object)(object)__instance == (Object)null) && VoicePackRegistry.LooksEncoded(normalizedPower)) { VoicePackRegistry.CaptureFromEncodedPower(GetPlayerGuid(__instance), normalizedPower); normalizedPower = VoicePackRegistry.StripPackFromPower(normalizedPower); } } internal static void DecodeRemotePackPrefix(PlayerAudio __instance, NetworkConnectionToClient connection, ref float normalizedPower, bool isUsingRocketDriver) { if ((Object)(object)__instance == (Object)null) { return; } if (VoicePackRegistry.LooksEncoded(normalizedPower)) { float encodedPower = normalizedPower; VoicePackRegistry.CaptureFromEncodedPower(GetPlayerGuid(__instance), normalizedPower); normalizedPower = VoicePackRegistry.StripPackFromPower(normalizedPower); if (NetworkServer.active) { int pack = VoicePackRegistry.GetPack(GetPlayerGuid(__instance)); int clipIndex = VoicePackRegistry.DecodeClipIndexFromPower(encodedPower); normalizedPower = VoicePackRegistry.EncodeSwing(normalizedPower, pack, clipIndex); } } else if (NetworkServer.active) { ulong playerGuid = GetPlayerGuid(__instance); int pack2 = VoicePackRegistry.GetPack(playerGuid); int clipIndex2 = 0; VoicePackRegistry.TryGetPendingSwingClipIndex(playerGuid, out clipIndex2); normalizedPower = VoicePackRegistry.EncodeSwing(normalizedPower, pack2, clipIndex2); } } } internal static class NetworkLifecyclePatch { internal static void OnNetworkStoppedPostfix() { VoicePackRegistry.Clear(); } } internal static class SwingGruntPatch { internal static void StripEncodedPackPrefix(PlayerAudio __instance, ref float normalizedPower) { if (!((Object)(object)__instance == (Object)null) && VoicePackRegistry.LooksEncoded(normalizedPower)) { VoicePackRegistry.CaptureFromEncodedPower(GetPlayerGuid(__instance), normalizedPower); normalizedPower = VoicePackRegistry.StripPackFromPower(normalizedPower); } } internal static void Postfix(PlayerAudio __instance, float normalizedPower) { if (_poolsReady && !((Object)(object)__instance == (Object)null)) { ulong playerGuid = GetPlayerGuid(__instance); int clipIndex = -1; if (VoicePackRegistry.TryConsumePendingSwingClipIndex(playerGuid, out var clipIndex2)) { clipIndex = clipIndex2; } PlayGruntForPlayer(__instance, PickSwingGrunt(GetVoicePackId(__instance), normalizedPower, clipIndex)); } } } internal static class JumpGruntPatch { internal static void Postfix(PlayerAudio __instance) { if (_poolsReady && !((Object)(object)__instance == (Object)null)) { ulong playerGuid = GetPlayerGuid(__instance); int clipIndex = -1; int clipIndex3; if (!IsLocalPlayer(__instance) && VoicePackRegistry.TryConsumePendingClipIndex(GruntEventKind.Jump, playerGuid, out var clipIndex2)) { clipIndex = clipIndex2; } else if (VoicePackRegistry.TryPeekPendingClipIndex(GruntEventKind.Jump, playerGuid, out clipIndex3)) { clipIndex = clipIndex3; } PlayGruntForPlayer(__instance, PickJumpGrunt(GetVoicePackId(__instance), clipIndex)); } } } internal static class KnockedGruntPatch { internal static void Postfix(PlayerAudio __instance) { if (_poolsReady && !((Object)(object)__instance == (Object)null)) { int clipIndex = PickKnockedClipIndexFromMovement(((Component)__instance).GetComponentInParent(), GetVoicePackId(__instance)); PlayGruntForPlayer(__instance, PickKnockedGrunt(GetVoicePackId(__instance), clipIndex)); } } } public const string ModGuid = "sbg.voicegrunts"; public const string ModName = "VoiceGrunts"; public const string ModVersion = "1.4.11"; public const int MaxPackCount = 9; private const string SoundsFolderName = "Sounds"; private const string VoicePackPrefix = "VoicePack"; private const string KnockedFolderName = "Knocked"; private static readonly string[] SupportedExtensions = new string[3] { ".wav", ".ogg", ".mp3" }; private const float Audio3DMinDistance = 8f; private const float Audio3DMaxDistance = 60f; private const float AudioHeightOffset = 0.9f; internal static ManualLogSource Log; private static readonly Dictionary> _swingPools = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> _jumpPools = new Dictionary>(); private static readonly Dictionary> _knockedPools = new Dictionary>(); private static readonly Random _rng = new Random(); private static bool _poolsReady; private static int _installedPackCount; private static ConfigEntry _selectedVoicePack; private static FieldRef _playerInfoField; private static readonly (string TierFolder, float MaxPower)[] SwingPowerTiers = new(string, float)[4] { ("SwingGrunt/LightTap", 0.25f), ("SwingGrunt/MediumGrunt", 0.5f), ("SwingGrunt/StrongGrunt", 0.75f), ("SwingGrunt/FullPower", 1f) }; private static readonly PropertyInfo KnockedOutVfxDataProperty = AccessTools.Property(typeof(PlayerMovement), "NetworkknockedOutVfxData"); private static readonly FieldInfo KnockedOutTotalStarCountField = ((KnockedOutVfxDataProperty != null) ? AccessTools.Field(KnockedOutVfxDataProperty.PropertyType, "totalStarCount") : null); private static readonly FieldInfo KnockedOutColoredStarCountField = ((KnockedOutVfxDataProperty != null) ? AccessTools.Field(KnockedOutVfxDataProperty.PropertyType, "coloredStarCount") : null); public static int SelectedVoicePack => Mathf.Clamp(_selectedVoicePack.Value, 1, 9); internal static int InstalledPackCount => _installedPackCount; internal static void SetSelectedVoicePack(int packId) { _selectedVoicePack.Value = Mathf.Clamp(packId, 1, 9); } internal static string GetPackDisplayName(int packId) { return VoicePackNames.GetDisplayName(packId); } private void Awake() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _selectedVoicePack = ((BaseUnityPlugin)this).Config.Bind("Voice", "SelectedPack", 1, new ConfigDescription("Your voice pack (1–9) for your own grunts.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 9), Array.Empty())); _selectedVoicePack.SettingChanged += delegate { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Local voice pack set to {SelectedVoicePack} (takes effect immediately for your swings)."); } }; _playerInfoField = AccessTools.FieldRefAccess(AccessTools.Field(typeof(PlayerAudio), "playerInfo")); AnnouncerAudio.Initialize(); VoicePackNames.Initialize(((BaseUnityPlugin)this).Config); VoicePackOverlay.Initialize(((BaseUnityPlugin)this).Config); DiscoverInstalledVoicePacks(); ApplyHarmonyPatches(); Log.LogInfo((object)"VoiceGrunts v1.4.11 loaded (voice pools load in Start)."); } private void DiscoverInstalledVoicePacks() { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Sounds"); if (!Directory.Exists(text)) { Log.LogWarning((object)("Sounds folder missing: " + text)); return; } _installedPackCount = 0; foreach (int item in DiscoverPackIds(text)) { _installedPackCount = Mathf.Max(_installedPackCount, item); string packRootPath = Path.Combine(text, "VoicePack" + item); VoicePackNames.LoadFromPackFolder(item, packRootPath); } if (_installedPackCount > 0) { Log.LogInfo((object)$"Voice packs discovered: 1–{_installedPackCount}"); } } catch (Exception arg) { Log.LogError((object)$"Voice pack discovery failed: {arg}"); } } internal static int GetVoicePackId(PlayerAudio playerAudio) { if (IsLocalPlayer(playerAudio)) { return SelectedVoicePack; } return VoicePackRegistry.GetPack(GetPlayerGuid(playerAudio)); } internal static ulong GetPlayerGuid(PlayerAudio playerAudio) { if ((Object)(object)playerAudio == (Object)null) { return 0uL; } PlayerInfo val = ((_playerInfoField != null) ? _playerInfoField.Invoke(playerAudio) : null); if ((Object)(object)val == (Object)null) { val = ((Component)playerAudio).GetComponent(); } if ((Object)(object)val == (Object)null) { return 0uL; } PlayerId playerId = val.PlayerId; if (!((Object)(object)playerId != (Object)null)) { return 0uL; } return playerId.Guid; } private static void ApplyHarmonyPatches() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0091: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Expected O, but got Unknown //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Expected O, but got Unknown Harmony val = new Harmony("sbg.voicegrunts"); int num = 0; try { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerAudio), "PlaySwingInternal", new Type[2] { typeof(float), typeof(bool) }, (Type[])null); if (methodInfo == null) { Log.LogError((object)"Could not find PlayerAudio.PlaySwingInternal(float, bool)"); } else { val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SwingGruntPatch), "StripEncodedPackPrefix", (Type[])null), new HarmonyMethod(typeof(SwingGruntPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.PlaySwingInternal (swing grunt layer)"); num++; } } catch (Exception arg) { Log.LogError((object)$"Failed to patch PlayerAudio swing hook: {arg}"); } try { MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerAudio), "PlayJumpInternal", (Type[])null, (Type[])null); if (methodInfo2 == null) { Log.LogError((object)"Could not find PlayerAudio.PlayJumpInternal()"); } else { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(JumpGruntPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.PlayJumpInternal (jump grunt layer)"); num++; } } catch (Exception arg2) { Log.LogError((object)$"Failed to patch PlayerAudio jump hook: {arg2}"); } try { MethodInfo methodInfo3 = AccessTools.Method(typeof(PlayerAudio), "CmdPlaySwingForAllClients", new Type[3] { typeof(float), typeof(bool), typeof(NetworkConnectionToClient) }, (Type[])null); if (methodInfo3 == null) { Log.LogWarning((object)"Could not find PlayerAudio.CmdPlaySwingForAllClients for voice pack sync"); } else { val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(SwingNetworkPatch), "CmdPlaySwingPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.CmdPlaySwingForAllClients (voice pack capture on server)"); num++; } } catch (Exception arg3) { Log.LogError((object)$"Failed to patch PlayerAudio CmdPlaySwing hook: {arg3}"); } try { MethodInfo methodInfo4 = AccessTools.Method(typeof(PlayerAudio), "PlaySwingForAllClients", new Type[2] { typeof(float), typeof(bool) }, (Type[])null); if (methodInfo4 == null) { Log.LogWarning((object)"Could not find PlayerAudio.PlaySwingForAllClients(float, bool) for voice pack sync"); } else { val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(SwingNetworkPatch), "EncodeLocalPackPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.PlaySwingForAllClients (voice pack piggyback)"); num++; } } catch (Exception arg4) { Log.LogError((object)$"Failed to patch PlayerAudio swing network hook: {arg4}"); } try { MethodInfo methodInfo5 = typeof(PlayerAudio).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "RpcPlaySwing" && m.GetParameters().Length == 3 && m.GetParameters()[1].ParameterType == typeof(float)); if (methodInfo5 == null) { Log.LogWarning((object)"Could not find PlayerAudio.RpcPlaySwing for voice pack sync"); } else { val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(SwingNetworkPatch), "DecodeRemotePackPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.RpcPlaySwing (voice pack piggyback)"); num++; } } catch (Exception arg5) { Log.LogError((object)$"Failed to patch PlayerAudio RpcPlaySwing hook: {arg5}"); } try { MethodInfo methodInfo6 = AccessTools.Method(typeof(PlayerAudio), "StartKnockoutLoopLocalOnly", (Type[])null, (Type[])null); if (methodInfo6 == null) { Log.LogWarning((object)"Could not find PlayerAudio.StartKnockoutLoopLocalOnly for knocked grunt layer"); } else { val.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(typeof(KnockedGruntPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched PlayerAudio.StartKnockoutLoopLocalOnly (knocked grunt sync for all clients)"); num++; } } catch (Exception arg6) { Log.LogError((object)$"Failed to patch PlayerAudio StartKnockoutLoopLocalOnly hook: {arg6}"); } try { MethodInfo methodInfo7 = AccessTools.Method(typeof(BNetworkManager), "OnStopClient", (Type[])null, (Type[])null); if (methodInfo7 != null) { val.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(typeof(NetworkLifecyclePatch), "OnNetworkStoppedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; Log.LogInfo((object)"Patched BNetworkManager.OnStopClient (voice pack registry reset)"); } } catch (Exception arg7) { Log.LogError((object)$"Failed to patch BNetworkManager.OnStopClient: {arg7}"); } Log.LogInfo((object)((num == 0) ? "No Harmony patches were applied." : $"Applied {num} Harmony patch(es).")); try { val.PatchAll(typeof(GruntClipNetworkSync).Assembly); Log.LogInfo((object)"Applied jump/knock clip sync Harmony patches."); } catch (Exception arg8) { Log.LogError((object)$"PatchAll failed (swing/jump/knock grunts may be partially broken): {arg8}"); } } private void Start() { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Sounds"); if (!Directory.Exists(text)) { Log.LogError((object)("Sounds folder missing: " + text)); } else { LoadAllVoicePools(text); } } catch (Exception arg) { Log.LogError((object)$"Start failed: {arg}"); } } private static void LoadAllVoicePools(string soundsDir) { _swingPools.Clear(); _jumpPools.Clear(); _knockedPools.Clear(); _installedPackCount = 0; foreach (int item2 in DiscoverPackIds(soundsDir)) { _installedPackCount = Mathf.Max(_installedPackCount, item2); string text = "VoicePack" + item2; string text2 = Path.Combine(soundsDir, text); if (!Directory.Exists(text2)) { continue; } VoicePackNames.LoadFromPackFolder(item2, text2); (string, float)[] swingPowerTiers = SwingPowerTiers; for (int i = 0; i < swingPowerTiers.Length; i++) { string item = swingPowerTiers[i].Item1; string path = Path.Combine(text2, item.Replace('/', Path.DirectorySeparatorChar)); if (!Directory.Exists(path)) { continue; } List list = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).Where(IsSupportedAudioFile).OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase) .ToList(); if (list.Count == 0) { continue; } string text3 = text + "/" + item; List list2 = new List(); foreach (string item3 in list) { if (TryLoadVariant(soundsDir, item3, out var variant)) { list2.Add(variant); } } if (list2.Count <= 0) { continue; } _swingPools[text3] = list2; Log.LogInfo((object)$"Loaded {text3} ({list2.Count} clip(s))"); foreach (SoundVariant item4 in list2) { Log.LogInfo((object)(" - " + item4.RelativePath)); } } LoadSimplePool(soundsDir, text2, text, item2, "JumpGrunt", _jumpPools); LoadSimplePool(soundsDir, text2, text, item2, "Knocked", _knockedPools); } _poolsReady = _swingPools.Count > 0 || _jumpPools.Count > 0 || _knockedPools.Count > 0; if (!_poolsReady) { Log.LogError((object)"No voice pack audio could be loaded."); } else { Log.LogInfo((object)$"Voice packs installed: 1–{_installedPackCount} (max selectable: {9})"); } } private static IEnumerable DiscoverPackIds(string soundsDir) { if (!Directory.Exists(soundsDir)) { yield break; } List list = new List(); string[] directories = Directory.GetDirectories(soundsDir); for (int i = 0; i < directories.Length; i++) { string fileName = Path.GetFileName(directories[i]); if (!fileName.StartsWith("VoicePack", StringComparison.OrdinalIgnoreCase) || !int.TryParse(fileName.Substring("VoicePack".Length), out var result)) { continue; } if (result < 1 || result > 9) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"Ignoring {fileName}: pack id must be 1–{9}."); } } else { list.Add(result); } } list.Sort(); foreach (int item in list) { yield return item; } } private static void LoadSimplePool(string soundsDir, string packRootPath, string packRootName, int packId, string poolFolderName, Dictionary> targetPools) { string path = Path.Combine(packRootPath, poolFolderName); if (!Directory.Exists(path)) { return; } List list = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).Where(IsSupportedAudioFile).OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase) .ToList(); List list2 = new List(); foreach (string item in list) { if (TryLoadVariant(soundsDir, item, out var variant)) { list2.Add(variant); } } if (list2.Count <= 0) { return; } targetPools[packId] = list2; Log.LogInfo((object)$"Loaded {packRootName}/{poolFolderName} ({list2.Count} clip(s))"); foreach (SoundVariant item2 in list2) { Log.LogInfo((object)(" - " + item2.RelativePath)); } } private static bool TryLoadVariant(string soundsDir, string audioPath, out SoundVariant variant) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0020: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) variant = null; string relativePath = GetRelativePath(soundsDir, audioPath); System coreSystem = RuntimeManager.CoreSystem; Sound sound2D = default(Sound); RESULT val = ((System)(ref coreSystem)).createSound(audioPath, (MODE)256, ref sound2D); if ((int)val != 0) { Log.LogError((object)$"FMOD createSound (2D) failed for {relativePath}: {val}"); return false; } Sound sound3D = default(Sound); RESULT val2 = ((System)(ref coreSystem)).createSound(audioPath, (MODE)2097424, ref sound3D); if ((int)val2 != 0) { Log.LogError((object)$"FMOD createSound (3D) failed for {relativePath}: {val2}"); ((Sound)(ref sound2D)).release(); return false; } ((Sound)(ref sound3D)).set3DMinMaxDistance(8f, 60f); variant = new SoundVariant { RelativePath = relativePath, Sound2D = sound2D, Sound3D = sound3D }; return true; } private static bool IsSupportedAudioFile(string path) { string ext = Path.GetExtension(path); return SupportedExtensions.Any((string supported) => string.Equals(ext, supported, StringComparison.OrdinalIgnoreCase)); } private static string GetRelativePath(string rootDir, string fullPath) { return fullPath.Substring(rootDir.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Replace('\\', '/'); } private static string GetSwingTierFolder(float normalizedPower) { float num = Mathf.Clamp01(normalizedPower); (string, float)[] swingPowerTiers = SwingPowerTiers; for (int i = 0; i < swingPowerTiers.Length; i++) { var (result, num2) = swingPowerTiers[i]; if (num <= num2) { return result; } } return SwingPowerTiers[SwingPowerTiers.Length - 1].TierFolder; } private static int ResolveVoicePackId(int requestedPackId) { int num = Mathf.Clamp(requestedPackId, 1, 9); if (PackHasAnyClips(num)) { return num; } if (num != 1 && PackHasAnyClips(1)) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"Voice pack {num} is not installed locally; falling back to VoicePack1."); } return 1; } return num; } private static bool PackHasAnyClips(int packId) { if (!_swingPools.Keys.Any((string k) => k.StartsWith("VoicePack" + packId + "/", StringComparison.OrdinalIgnoreCase)) && !_jumpPools.ContainsKey(packId)) { return _knockedPools.ContainsKey(packId); } return true; } private static SoundVariant PickSwingGrunt(int voicePackId, float normalizedPower, int clipIndex = -1) { string text = "VoicePack" + ResolveVoicePackId(voicePackId) + "/" + GetSwingTierFolder(normalizedPower); if (_swingPools.TryGetValue(text, out var value) && value.Count > 0) { int index = ((clipIndex >= 0) ? (clipIndex % value.Count) : _rng.Next(value.Count)); return value[index]; } ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("No swing clips for " + text)); } return null; } private static int PickSwingClipIndex(int voicePackId, float normalizedPower) { string key = "VoicePack" + ResolveVoicePackId(voicePackId) + "/" + GetSwingTierFolder(normalizedPower); if (_swingPools.TryGetValue(key, out var value) && value.Count > 0) { return _rng.Next(value.Count); } return 0; } internal static int PickJumpClipIndex(int voicePackId) { int key = ResolveVoicePackId(voicePackId); if (_jumpPools.TryGetValue(key, out var value) && value.Count > 0) { return _rng.Next(value.Count); } return 0; } private static SoundVariant PickJumpGrunt(int voicePackId, int clipIndex = -1) { int num = ResolveVoicePackId(voicePackId); if (_jumpPools.TryGetValue(num, out var value) && value.Count > 0) { int index = ((clipIndex >= 0) ? (clipIndex % value.Count) : _rng.Next(value.Count)); return value[index]; } ManualLogSource log = Log; if (log != null) { log.LogDebug((object)$"No jump clips for VoicePack{num}"); } return null; } internal static int PickKnockedClipIndex(int voicePackId) { int key = ResolveVoicePackId(voicePackId); if (_knockedPools.TryGetValue(key, out var value) && value.Count > 0) { return _rng.Next(value.Count); } return 0; } private static int PickKnockedClipIndexFromMovement(PlayerMovement movement, int voicePackId) { if ((Object)(object)movement == (Object)null || KnockedOutVfxDataProperty == null || KnockedOutTotalStarCountField == null || KnockedOutColoredStarCountField == null) { return -1; } object value = KnockedOutVfxDataProperty.GetValue(movement, null); if (value == null) { return -1; } int num = (int)KnockedOutTotalStarCountField.GetValue(value); int num2 = (int)KnockedOutColoredStarCountField.GetValue(value); int key = ResolveVoicePackId(voicePackId); if (!_knockedPools.TryGetValue(key, out var value2) || value2.Count == 0) { return 0; } return Math.Abs(num * 31 + num2) % value2.Count; } private static SoundVariant PickKnockedGrunt(int voicePackId, int clipIndex = -1) { int num = ResolveVoicePackId(voicePackId); if (_knockedPools.TryGetValue(num, out var value) && value.Count > 0) { int index = ((clipIndex >= 0) ? (clipIndex % value.Count) : _rng.Next(value.Count)); return value[index]; } ManualLogSource log = Log; if (log != null) { log.LogDebug((object)$"No knocked clips for VoicePack{num}"); } return null; } private static void PlayGruntForPlayer(PlayerAudio playerAudio, SoundVariant variant) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (variant != null && !((Object)(object)playerAudio == (Object)null)) { Vector3 worldPosition = ((Component)playerAudio).transform.position + Vector3.up * 0.9f; if (IsLocalPlayer(playerAudio)) { PlayLocal2D(variant); } else { PlayLayered3D(variant, worldPosition); } } } private static void PlayLocal2D(SoundVariant variant) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (variant == null) { return; } try { System coreSystem = RuntimeManager.CoreSystem; Channel channel = default(Channel); RESULT val = ((System)(ref coreSystem)).playSound(variant.Sound2D, default(ChannelGroup), false, ref channel); if ((int)val != 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"playSound 2D ({variant.RelativePath}): {val}"); } } else { AnnouncerAudio.ApplyToChannel(channel); } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"PlayLocal2D failed: {arg}"); } } } private static void PlayLayered3D(SoundVariant variant, Vector3 worldPosition) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (variant == null) { return; } try { System coreSystem = RuntimeManager.CoreSystem; Channel channel = default(Channel); RESULT val = ((System)(ref coreSystem)).playSound(variant.Sound3D, default(ChannelGroup), true, ref channel); if ((int)val != 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"playSound 3D ({variant.RelativePath}): {val}"); } } else { ((Channel)(ref channel)).setMode((MODE)2097168); ((Channel)(ref channel)).set3DMinMaxDistance(8f, 60f); VECTOR val2 = RuntimeUtils.ToFMODVector(worldPosition); VECTOR val3 = RuntimeUtils.ToFMODVector(Vector3.zero); ((Channel)(ref channel)).set3DAttributes(ref val2, ref val3); AnnouncerAudio.ApplyToChannel(channel); ((Channel)(ref channel)).setPaused(false); } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"PlayLayered3D failed: {arg}"); } } } internal static bool IsLocalPlayer(PlayerAudio playerAudio) { if ((Object)(object)playerAudio != (Object)null && ((NetworkBehaviour)playerAudio).isLocalPlayer) { return true; } PlayerGolfer localPlayerAsGolfer = GameManager.LocalPlayerAsGolfer; if ((Object)(object)localPlayerAsGolfer != (Object)null && (Object)(object)playerAudio != (Object)null) { return (Object)(object)((Component)playerAudio).gameObject == (Object)(object)((Component)localPlayerAsGolfer).gameObject; } return false; } } internal static class VoicePackNames { private const string DisplayNameFileName = "displayname.txt"; private static readonly Dictionary FileNamesByPackId = new Dictionary(); private static ConfigEntry[] _configOverrides; internal static void Initialize(ConfigFile config) { _configOverrides = new ConfigEntry[10]; for (int i = 1; i <= 9; i++) { int num = i; _configOverrides[num] = config.Bind("DisplayNames", $"Pack{num}", string.Empty, string.Format("Overlay label for VoicePack{0}. Leave empty to use Sounds/VoicePack{1}/{2} or the default folder name.", num, num, "displayname.txt")); } } internal static void LoadFromPackFolder(int packId, string packRootPath) { if (packId < 1 || packId > 9) { return; } string path = Path.Combine(packRootPath, "displayname.txt"); if (!File.Exists(path)) { FileNamesByPackId.Remove(packId); return; } string text = File.ReadAllText(path).Trim(); if (string.IsNullOrWhiteSpace(text)) { FileNamesByPackId.Remove(packId); return; } FileNamesByPackId[packId] = text; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"VoicePack{packId} display name: {text}"); } } internal static string GetDisplayName(int packId) { packId = Math.Max(1, Math.Min(packId, 9)); ConfigEntry[] configOverrides = _configOverrides; string text = ((configOverrides == null) ? null : configOverrides[packId]?.Value?.Trim()); if (!string.IsNullOrWhiteSpace(text)) { return text; } if (FileNamesByPackId.TryGetValue(packId, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } return "VoicePack" + packId; } } internal sealed class VoicePackOverlay : MonoBehaviour { [CompilerGenerated] private static class <>O { public static WindowFunction <0>__DrawWindow; } private static VoicePackOverlay _instance; private static ConfigEntry _overlayHotkey; private static readonly KeyboardShortcut CloseOverlayKey = new KeyboardShortcut((KeyCode)27, Array.Empty()); private const int CursorRestoreFrames = 8; private bool _visible; private Rect _windowRect = new Rect(20f, 80f, 260f, 0f); private bool _cursorWasVisible; private CursorLockMode _previousLockState; private int _cursorRestoreFramesRemaining; internal static void Initialize(ConfigFile config) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown _overlayHotkey = config.Bind("UI", "OverlayHotkey", new KeyboardShortcut((KeyCode)283, Array.Empty()), "Toggle the in-game voice pack picker overlay."); if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("VoiceGruntsOverlay"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); } } private void Update() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut val; if (_overlayHotkey != null) { val = _overlayHotkey.Value; if (((KeyboardShortcut)(ref val)).IsDown()) { SetVisible(!_visible); } } if (_visible) { val = CloseOverlayKey; if (((KeyboardShortcut)(ref val)).IsDown()) { SetVisible(visible: false); } } } private void LateUpdate() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!_visible && _cursorRestoreFramesRemaining > 0) { _cursorRestoreFramesRemaining--; GameCursor.RestoreAfterOverlay(_cursorWasVisible, _previousLockState); } } private void OnDestroy() { if (_visible) { SetVisible(visible: false); } } private void SetVisible(bool visible) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (_visible != visible) { _visible = visible; if (_visible) { _cursorRestoreFramesRemaining = 0; _cursorWasVisible = Cursor.visible; _previousLockState = Cursor.lockState; GameCursor.SetOverlayOpen(open: true); } else { GameCursor.RestoreAfterOverlay(_cursorWasVisible, _previousLockState); _cursorRestoreFramesRemaining = 8; } } } private void OnGUI() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (_visible) { Rect windowRect = _windowRect; object obj = <>O.<0>__DrawWindow; if (obj == null) { WindowFunction val = DrawWindow; <>O.<0>__DrawWindow = val; obj = (object)val; } _windowRect = GUILayout.Window(22087, windowRect, (WindowFunction)obj, "uhm — VoiceGrunts", Array.Empty()); } } private static void DrawWindow(int windowId) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) string packDisplayName = Plugin.GetPackDisplayName(Plugin.SelectedVoicePack); GUILayout.Label("Current pack: " + packDisplayName, Array.Empty()); GUILayout.Label($"Installed: 1–{Mathf.Max(1, Plugin.InstalledPackCount)}", Array.Empty()); GUILayout.Space(6f); int num = Mathf.Max(1, Plugin.InstalledPackCount); for (int i = 1; i <= num; i++) { bool num2 = i == Plugin.SelectedVoicePack; string packDisplayName2 = Plugin.GetPackDisplayName(i); GUI.backgroundColor = (Color)(num2 ? new Color(0.55f, 0.85f, 0.55f) : Color.white); if (GUILayout.Button(num2 ? (packDisplayName2 + " (active)") : packDisplayName2, Array.Empty())) { Plugin.SetSelectedVoicePack(i); } } GUI.backgroundColor = Color.white; GUILayout.Space(6f); GUILayout.Label("Press F2 or Esc to close", Array.Empty()); GUI.DragWindow(); } } internal static class VoicePackRegistry { private const int PowerScale = 1000; private const int PackScale = 10; private const int VariantScale = 100; private const float EncodeDivisor = 100000f; private static readonly Dictionary PackByGuid = new Dictionary(); private static readonly Dictionary PendingSwingClipIndexByGuid = new Dictionary(); private static readonly Dictionary PendingJumpClipIndexByGuid = new Dictionary(); private static readonly Dictionary PendingKnockedClipIndexByGuid = new Dictionary(); private static readonly Dictionary RpcJumpClipIndexByNetId = new Dictionary(); internal static void Clear() { PackByGuid.Clear(); PendingSwingClipIndexByGuid.Clear(); PendingJumpClipIndexByGuid.Clear(); PendingKnockedClipIndexByGuid.Clear(); RpcJumpClipIndexByNetId.Clear(); } internal static int GetPack(ulong playerGuid) { if (playerGuid == 0L) { return 1; } if (!PackByGuid.TryGetValue(playerGuid, out var value)) { return 1; } return value; } internal static void SetPack(ulong playerGuid, int packId) { if (playerGuid != 0L) { PackByGuid[playerGuid] = Mathf.Clamp(packId, 1, 9); } } internal static void SetPendingClipIndex(GruntEventKind kind, ulong playerGuid, int clipIndex) { if (playerGuid != 0L) { clipIndex = Mathf.Max(0, clipIndex); switch (kind) { case GruntEventKind.Jump: PendingJumpClipIndexByGuid[playerGuid] = clipIndex; break; case GruntEventKind.Knocked: PendingKnockedClipIndexByGuid[playerGuid] = clipIndex; break; } } } internal static bool TryPeekPendingClipIndex(GruntEventKind kind, ulong playerGuid, out int clipIndex) { clipIndex = 0; if (playerGuid == 0L) { return false; } return GetPendingMap(kind)?.TryGetValue(playerGuid, out clipIndex) ?? false; } internal static bool TryConsumePendingClipIndex(GruntEventKind kind, ulong playerGuid, out int clipIndex) { clipIndex = -1; if (playerGuid == 0L) { return false; } Dictionary pendingMap = GetPendingMap(kind); if (pendingMap != null && pendingMap.TryGetValue(playerGuid, out clipIndex)) { pendingMap.Remove(playerGuid); return true; } clipIndex = -1; return false; } internal static void SetPendingSwingClipIndex(ulong playerGuid, int clipIndex) { if (playerGuid != 0L) { PendingSwingClipIndexByGuid[playerGuid] = Mathf.Max(0, clipIndex); } } internal static bool TryGetPendingSwingClipIndex(ulong playerGuid, out int clipIndex) { if (playerGuid == 0L) { clipIndex = 0; return false; } return PendingSwingClipIndexByGuid.TryGetValue(playerGuid, out clipIndex); } internal static bool TryConsumePendingSwingClipIndex(ulong playerGuid, out int clipIndex) { if (playerGuid == 0L) { clipIndex = -1; return false; } if (PendingSwingClipIndexByGuid.TryGetValue(playerGuid, out clipIndex)) { PendingSwingClipIndexByGuid.Remove(playerGuid); return true; } clipIndex = -1; return false; } internal static void SetRpcJumpClipIndex(uint netId, int clipIndex) { RpcJumpClipIndexByNetId[netId] = Mathf.Max(0, clipIndex); } internal static bool TryTakeRpcJumpClipIndex(uint netId, out int clipIndex) { if (RpcJumpClipIndexByNetId.TryGetValue(netId, out clipIndex)) { RpcJumpClipIndexByNetId.Remove(netId); return true; } clipIndex = 0; return false; } internal static bool LooksEncoded(float encodedPower) { if (!TryDecode(encodedPower, out var _, out var packId, out var _)) { return false; } if (packId >= 1) { return packId <= 9; } return false; } internal static float EncodeSwing(float normalizedPower, int packId, int clipIndex) { int num = Mathf.Clamp(Mathf.RoundToInt(Mathf.Clamp01(normalizedPower) * 1000f), 0, 1000); packId = Mathf.Clamp(packId, 1, 9); clipIndex = Mathf.Clamp(clipIndex, 0, 9); return (float)(num * 100 + clipIndex * 10 + packId) / 100000f; } internal static float StripPackFromPower(float encodedPower) { TryDecode(encodedPower, out var normalizedPower, out var _, out var _); return normalizedPower; } internal static int DecodeClipIndexFromPower(float encodedPower) { TryDecode(encodedPower, out var _, out var _, out var clipIndex); return clipIndex; } internal static void CaptureFromEncodedPower(ulong playerGuid, float encodedPower) { if (LooksEncoded(encodedPower)) { SetPack(playerGuid, DecodePackFromPower(encodedPower)); SetPendingSwingClipIndex(playerGuid, DecodeClipIndexFromPower(encodedPower)); } } internal static int DecodePackFromPower(float encodedPower) { TryDecode(encodedPower, out var _, out var packId, out var _); return packId; } private static Dictionary GetPendingMap(GruntEventKind kind) { return kind switch { GruntEventKind.Jump => PendingJumpClipIndexByGuid, GruntEventKind.Knocked => PendingKnockedClipIndexByGuid, _ => null, }; } private static bool TryDecode(float encodedPower, out float normalizedPower, out int packId, out int clipIndex) { int num = Mathf.RoundToInt(encodedPower * 100000f); packId = num % 10; clipIndex = num / 10 % 10; int num2 = num / 100; normalizedPower = Mathf.Clamp01((float)num2 / 1000f); if (packId < 1 || packId > 9) { packId = 1; return false; } return true; } } }