Decompiled source of uhmVoiceGrunts v1.4.12

VoiceGrunts.dll

Decompiled 4 days ago
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<CursorManager>.HasInstance)
					{
						ApplyCursorLockMethod?.Invoke(SingletonBehaviour<CursorManager>.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<CodeInstruction> TranspileAppendClipByteAfterWriterGet(IEnumerable<CodeInstruction> instructions, MethodInfo writeMethod, GruntEventKind kind, bool passPlayerInfoArg = false)
		{
			List<CodeInstruction> codes = new List<CodeInstruction>(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<CodeInstruction> TranspileReadKnockedClipBeforeUserCode(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> codes = new List<CodeInstruction>(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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<PlayerMovement>(), 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<string, List<SoundVariant>> _swingPools = new Dictionary<string, List<SoundVariant>>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<int, List<SoundVariant>> _jumpPools = new Dictionary<int, List<SoundVariant>>();

		private static readonly Dictionary<int, List<SoundVariant>> _knockedPools = new Dictionary<int, List<SoundVariant>>();

		private static readonly Random _rng = new Random();

		private static bool _poolsReady;

		private static int _installedPackCount;

		private static ConfigEntry<int> _selectedVoicePack;

		private static FieldRef<PlayerAudio, PlayerInfo> _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<int>("Voice", "SelectedPack", 1, new ConfigDescription("Your voice pack (1–9) for your own grunts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), Array.Empty<object>()));
			_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<PlayerAudio, PlayerInfo>(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<PlayerInfo>();
			}
			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<string> list = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).Where(IsSupportedAudioFile).OrderBy<string, string>((string result) => result, StringComparer.OrdinalIgnoreCase)
						.ToList();
					if (list.Count == 0)
					{
						continue;
					}
					string text3 = text + "/" + item;
					List<SoundVariant> list2 = new List<SoundVariant>();
					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<int> DiscoverPackIds(string soundsDir)
		{
			if (!Directory.Exists(soundsDir))
			{
				yield break;
			}
			List<int> list = new List<int>();
			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<int, List<SoundVariant>> targetPools)
		{
			string path = Path.Combine(packRootPath, poolFolderName);
			if (!Directory.Exists(path))
			{
				return;
			}
			List<string> list = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).Where(IsSupportedAudioFile).OrderBy<string, string>((string result) => result, StringComparer.OrdinalIgnoreCase)
				.ToList();
			List<SoundVariant> list2 = new List<SoundVariant>();
			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<int, string> FileNamesByPackId = new Dictionary<int, string>();

		private static ConfigEntry<string>[] _configOverrides;

		internal static void Initialize(ConfigFile config)
		{
			_configOverrides = new ConfigEntry<string>[10];
			for (int i = 1; i <= 9; i++)
			{
				int num = i;
				_configOverrides[num] = config.Bind<string>("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<string>[] 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<KeyboardShortcut> _overlayHotkey;

		private static readonly KeyboardShortcut CloseOverlayKey = new KeyboardShortcut((KeyCode)27, Array.Empty<KeyCode>());

		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<KeyboardShortcut>("UI", "OverlayHotkey", new KeyboardShortcut((KeyCode)283, Array.Empty<KeyCode>()), "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<VoicePackOverlay>();
			}
		}

		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<GUILayoutOption>());
			}
		}

		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<GUILayoutOption>());
			GUILayout.Label($"Installed: 1–{Mathf.Max(1, Plugin.InstalledPackCount)}", Array.Empty<GUILayoutOption>());
			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<GUILayoutOption>()))
				{
					Plugin.SetSelectedVoicePack(i);
				}
			}
			GUI.backgroundColor = Color.white;
			GUILayout.Space(6f);
			GUILayout.Label("Press F2 or Esc to close", Array.Empty<GUILayoutOption>());
			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<ulong, int> PackByGuid = new Dictionary<ulong, int>();

		private static readonly Dictionary<ulong, int> PendingSwingClipIndexByGuid = new Dictionary<ulong, int>();

		private static readonly Dictionary<ulong, int> PendingJumpClipIndexByGuid = new Dictionary<ulong, int>();

		private static readonly Dictionary<ulong, int> PendingKnockedClipIndexByGuid = new Dictionary<ulong, int>();

		private static readonly Dictionary<uint, int> RpcJumpClipIndexByNetId = new Dictionary<uint, int>();

		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<ulong, int> 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<ulong, int> 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;
		}
	}
}