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