using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using REPO_DeadTTS.Config; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("REPO-DeadTTS")] [assembly: AssemblyDescription("Mod created by flipf17")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("REPO-DeadTTS")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8e05cd18-c8aa-419a-b430-33faaf371490")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace REPO_DeadTTS { [BepInPlugin("flipf17.DeadTTS", "DeadTTS", "1.1.1")] public class Plugin : BaseUnityPlugin { private Harmony _harmony; public static Plugin instance; private static ManualLogSource logger; private void Awake() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown instance = this; CreateCustomLogger(); ConfigSettings.BindConfigSettings(); _harmony = new Harmony("DeadTTS"); PatchAll(); Log("DeadTTS loaded"); } private void PatchAll() { IEnumerable enumerable; try { enumerable = Assembly.GetExecutingAssembly().GetTypes(); } catch (ReflectionTypeLoadException ex) { enumerable = ex.Types.Where((Type t) => t != null); } foreach (Type item in enumerable) { _harmony.PatchAll(item); } } private void CreateCustomLogger() { try { logger = Logger.CreateLogSource(string.Format("{0}-{1}", "DeadTTS", "1.1.1")); } catch { logger = ((BaseUnityPlugin)this).Logger; } } internal static void Log(string message) { logger.LogInfo((object)message); } internal static void LogError(string message) { logger.LogError((object)message); } internal static void LogWarning(string message) { logger.LogWarning((object)message); } internal static void LogVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogInfo((object)("[VERBOSE] " + message)); } } internal static void LogErrorVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogError((object)("[VERBOSE] " + message)); } } internal static void LogWarningVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogWarning((object)("[VERBOSE] " + message)); } } } public static class PluginInfo { public const string PLUGIN_GUID = "flipf17.DeadTTS"; public const string PLUGIN_NAME = "DeadTTS"; public const string PLUGIN_VERSION = "1.1.1"; } } namespace REPO_DeadTTS.Patches { [HarmonyPatch] public class DeadTTSPlayerData : MonoBehaviour { internal static PlayerAvatar localPlayer; internal static FieldInfo playerNameField = typeof(PlayerAvatar).GetField("playerName", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo steamIdField = typeof(PlayerAvatar).GetField("steamId", BindingFlags.Instance | BindingFlags.NonPublic); internal static MethodInfo setupClient = typeof(PlayerDeathHead).GetMethod("SetupClient", BindingFlags.Instance | BindingFlags.NonPublic); public bool isLocalPlayer = false; public PlayerAvatar playerAvatar; private string _playerName; private string _steamId; public PlayerDeathHead playerDeathHead; public PhotonView photonView => Object.op_Implicit((Object)(object)playerAvatar) ? playerAvatar.photonView : null; public string playerName { get { if (string.IsNullOrEmpty(_playerName)) { _playerName = (string)playerNameField.GetValue(playerAvatar); } return _playerName; } internal set { _playerName = value; } } public string steamId { get { if (string.IsNullOrEmpty(_steamId)) { _steamId = (string)steamIdField.GetValue(playerAvatar); } return _steamId; } internal set { _steamId = value; } } private void Awake() { playerAvatar = ((Component)this).GetComponent(); isLocalPlayer = Object.op_Implicit((Object)(object)playerAvatar) && (Object)(object)playerAvatar == (Object)(object)PlayerPatcher.localPlayer; } [HarmonyPatch(typeof(PlayerAvatar), "AddToStatsManagerRPC")] [HarmonyPostfix] public static void OnAddToStatsManagerRPC(string _playerName, string _steamID, PlayerAvatar __instance) { DeadTTSPlayerData component = ((Component)__instance).GetComponent(); if (Object.op_Implicit((Object)(object)component)) { component.playerName = _playerName; component.steamId = _steamID; } } [HarmonyPatch(typeof(PlayerDeathHead), "SetupDone")] [HarmonyPostfix] public static void OnDeathHeadSetupDone(PlayerDeathHead __instance) { if (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient) { return; } PlayerAvatar val = __instance.playerAvatar; DeadTTSPlayerData deadTTSPlayerData = ((val != null) ? ((Component)val).GetComponent() : null); if (Object.op_Implicit((Object)(object)deadTTSPlayerData)) { if (ConfigSettings.fixMissingDeathHeads.Value) { deadTTSPlayerData.ReassignPlayerDeathHead(__instance); } if (GameManager.Multiplayer()) { PhotonView component = ((Component)deadTTSPlayerData.playerDeathHead).GetComponent(); deadTTSPlayerData.photonView.RPC("ReassignPlayerDeathHeadRPC", (RpcTarget)4, new object[1] { component.ViewID }); Plugin.Log($"Sending PlayerDeathHead assignment assurance to clients for player: {deadTTSPlayerData.playerName} ({val.photonView.Owner.ActorNumber}) ..."); } else { Plugin.LogError("[OnDeathHeadSetupDone] Could not find PlayerAvatar assigned to PlayerDeathHead."); } } } [PunRPC] public void ReassignPlayerDeathHeadRPC(int deathHeadViewId, PhotonMessageInfo _info = default(PhotonMessageInfo)) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.MasterOnlyRPC(_info) || !ConfigSettings.fixMissingDeathHeads.Value) { return; } PlayerDeathHead[] array = Object.FindObjectsOfType(); PlayerDeathHead[] array2 = array; foreach (PlayerDeathHead val in array2) { PhotonView component = ((Component)val).GetComponent(); if (Object.op_Implicit((Object)(object)component) && component.ViewID == deathHeadViewId) { ReassignPlayerDeathHead(val); ((MonoBehaviour)this).StartCoroutine((IEnumerator)setupClient.Invoke(playerDeathHead, null)); return; } } Plugin.LogError(string.Format("Unknown error while trying to reassign PlayerDeathHead component to player: {0} ({1}) with death head view id: ", (!string.IsNullOrEmpty(playerName)) ? playerName : (Object.op_Implicit((Object)(object)playerAvatar) ? ((Object)playerAvatar).name : ""), photonView.Owner.ActorNumber) + deathHeadViewId); } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] [HarmonyPrefix] public static void ReassignPlayerDeathHeadBackup(PlayerAvatar __instance) { if (ConfigSettings.fixMissingDeathHeads.Value) { DeadTTSPlayerData component = ((Component)__instance).GetComponent(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.playerDeathHead)) { component.ReassignPlayerDeathHead(component.playerDeathHead); } } } internal void ReassignPlayerDeathHead(PlayerDeathHead deathHead) { playerDeathHead = deathHead; int viewID = ((Component)playerDeathHead).GetComponent().ViewID; if ((Object)(object)PlayerPatcher.GetPlayerDeathHead(playerAvatar) != (Object)(object)playerDeathHead || (Object)(object)playerDeathHead.playerAvatar != (Object)(object)playerAvatar || (Object)(object)((Component)playerDeathHead).transform.parent != (Object)(object)((Component)playerAvatar).transform.parent) { PlayerPatcher.playerDeathHeadField.SetValue(playerAvatar, playerDeathHead); playerDeathHead.playerAvatar = playerAvatar; ((Component)playerDeathHead).transform.SetParent(((Component)playerAvatar).transform.parent); Plugin.Log(string.Format("Reassigned PlayerDeathHead for player: {0} ({1}) - Assigned death head view id: ", (!string.IsNullOrEmpty(playerName)) ? playerName : (Object.op_Implicit((Object)(object)playerAvatar) ? ((Object)playerAvatar).name : ""), photonView.Owner.ActorNumber) + viewID); } if (!GameManager.Multiplayer() || !Object.op_Implicit((Object)(object)playerAvatar)) { return; } try { string text = (string)steamIdField.GetValue(playerAvatar); PlayerAvatar val = SessionManager.instance.CrownedPlayerGet(); string text2 = (Object.op_Implicit((Object)(object)val) ? ((string)steamIdField.GetValue(val)) : ""); if (SemiFunc.IsMultiplayer() && ((Object)(object)playerAvatar == (Object)(object)val || (!string.IsNullOrEmpty(text) && text == text2))) { ((Component)playerDeathHead.playerCrown).gameObject.SetActive(true); } } catch { } } } [HarmonyPatch] public static class UIPatcher { private static HashSet deadTTSElements = new HashSet(); private static HashSet deadTTSElementsFollowLog = new HashSet(); private static Dictionary isDisabledStates = new Dictionary(); private static FieldInfo textField = typeof(WorldSpaceUITTS).GetField("text", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo playerAvatarField = typeof(WorldSpaceUITTS).GetField("playerAvatar", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo followTransformField = typeof(WorldSpaceUITTS).GetField("followTransform", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo worldPositionField = typeof(WorldSpaceUITTS).GetField("worldPosition", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo followPositionField = typeof(WorldSpaceUITTS).GetField("followPosition", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo wordTimeField = typeof(WorldSpaceUITTS).GetField("wordTime", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo ttsVoiceField = typeof(WorldSpaceUITTS).GetField("ttsVoice", BindingFlags.Instance | BindingFlags.NonPublic); [HarmonyPatch(typeof(WorldSpaceUIParent), "TTS")] [HarmonyPrefix] public static void OnTTSUI(PlayerAvatar _player, string _text, float _time, WorldSpaceUIParent __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)_player) || !GameManager.Multiplayer() || (int)GameDirector.instance.currentState != 2 || (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena()) || !PlayerPatcher.IsPlayerDead(_player) || !ConfigSettings.useSpatialAudioDeadTTS.Value || !ConfigSettings.displayDeadTTSText.Value) { return; } PlayerDeathHead playerDeathHead = PlayerPatcher.GetPlayerDeathHead(_player); if (!Object.op_Implicit((Object)(object)playerDeathHead)) { Plugin.LogErrorVerbose("Failed to re-create TTS UI component. PlayerDeathHead was not set by the game."); } else if (string.IsNullOrEmpty(_text)) { Plugin.LogErrorVerbose("Failed to re-create TTS UI component. Passed text value was null or empty."); } else { if ((bool)PlayerPatcher.spectatedField.GetValue(playerDeathHead)) { return; } if (!ConfigSettings.onlyEnableWhenDiscovered.Value || (bool)PlayerPatcher.serverSeenField.GetValue(playerDeathHead) || PlayerPatcher.IsLocalPlayerDead() || !SemiFunc.RunIsLevel()) { try { WorldSpaceUITTS component = Object.Instantiate(__instance.TTSPrefab, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation, ((Component)__instance).transform).GetComponent(); if (!Object.op_Implicit((Object)(object)component)) { Plugin.LogError("Failed to create Dead TTS WorldSpaceUITTS component. Invalid prefab?"); return; } TextMeshProUGUI val = (TextMeshProUGUI)textField.GetValue(component); ((TMP_Text)val).text = _text; if (!PlayerPatcher.IsLocalPlayerDead()) { string text = ((TMP_Text)val).text; string text2 = ConfigSettings.deadTTSColor.Value.Trim(new char[1] { ' ' }).TrimStart(new char[1] { '#' }); if (text2.Length == 6) { ((TMP_Text)val).richText = true; try { text = "" + ((TMP_Text)val).text + ""; ((TMP_Text)val).text = text; } catch (Exception ex) { Plugin.LogError("Failed to apply dead TTS color: " + ConfigSettings.deadTTSColor.Value + "\n" + ex); } } } playerAvatarField.SetValue(component, _player); Transform transform = ((Component)playerDeathHead).transform; followTransformField.SetValue(component, transform); worldPositionField.SetValue(component, transform.position); followPositionField.SetValue(component, transform.position); wordTimeField.SetValue(component, _time); PlayerVoiceChat val2 = (PlayerVoiceChat)PlayerPatcher.voiceChatField.GetValue(_player); if (Object.op_Implicit((Object)(object)val2)) { ttsVoiceField.SetValue(component, val2.ttsVoice); } deadTTSElements.Add(component); try { deadTTSElements.RemoveWhere((WorldSpaceUITTS obj) => (Object)(object)obj == (Object)null); deadTTSElementsFollowLog.RemoveWhere((WorldSpaceUITTS obj) => (Object)(object)obj == (Object)null); return; } catch { return; } } catch (Exception ex2) { Plugin.LogError("Error initializing dead TTS UI:\n" + ex2); return; } } try { Plugin.LogWarningVerbose("Not creating TTS UI Element for dead player. ConfigEnableWhenDiscovered: " + ConfigSettings.onlyEnableWhenDiscovered.Value + " | PlayerDiscovered: " + (ConfigSettings.onlyEnableWhenDiscovered.Value ? ((bool)PlayerPatcher.serverSeenField.GetValue(playerDeathHead)).ToString() : "N/A") + " | IsLocalPlayerDead: " + PlayerPatcher.IsLocalPlayerDead() + " | RunIsLevel: " + SemiFunc.RunIsLevel()); } catch (Exception ex3) { Plugin.LogErrorVerbose("Error logging state for OnTTSUI: " + ex3); } } } [HarmonyPatch(typeof(WorldSpaceUITTS), "Update")] [HarmonyPrefix] public static void UpdateUIPositionPrefix(WorldSpaceUITTS __instance) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown if (!deadTTSElements.Contains(__instance)) { return; } try { PlayerAvatar val = (PlayerAvatar)playerAvatarField.GetValue(__instance); bool value = (bool)PlayerPatcher.isDisabledField.GetValue(val); isDisabledStates[val] = value; PlayerPatcher.isDisabledField.SetValue(val, false); if (!deadTTSElementsFollowLog.Contains(__instance)) { deadTTSElementsFollowLog.Add(__instance); TextMeshProUGUI val2 = (TextMeshProUGUI)textField.GetValue(__instance); Transform val3 = (Transform)followTransformField.GetValue(__instance); TTSVoice val4 = (TTSVoice)ttsVoiceField.GetValue(__instance); bool flag = Object.op_Implicit((Object)(object)val4) && (bool)PlayerPatcher.isSpeakingField.GetValue(val4); if (!Object.op_Implicit((Object)(object)val4)) { Plugin.LogWarningVerbose("FollowTransform: " + (((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "NONE") + " | TTSVoice.IsSpeaking: " + (((Object)(object)val4 != (Object)null) ? flag.ToString() : "TTSVoice Not Set!")); } } } catch (Exception ex) { Plugin.LogError("Error (A) updating dead TTS UI location:\n" + ex); deadTTSElements.Remove(__instance); } } [HarmonyPatch(typeof(WorldSpaceUITTS), "Update")] [HarmonyPostfix] public static void UpdateUIPositionPostfix(WorldSpaceUITTS __instance) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (!deadTTSElements.Contains(__instance)) { return; } try { PlayerAvatar val = (PlayerAvatar)playerAvatarField.GetValue(__instance); if (isDisabledStates.TryGetValue(val, out var value)) { PlayerPatcher.isDisabledField.SetValue(val, value); } } catch (Exception ex) { Plugin.LogError("Error (B) updating dead TTS UI location:\n" + ex); deadTTSElements.Remove(__instance); } } } [HarmonyPatch] public static class PlayerPatcher { internal static PlayerAvatar localPlayer; internal static FieldInfo voiceChatField = typeof(PlayerAvatar).GetField("voiceChat", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo isDisabledField = typeof(PlayerAvatar).GetField("isDisabled", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo playerDeathHeadField = typeof(PlayerAvatar).GetField("playerDeathHead", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(PlayerAvatar).GetField("playerDeathHead", BindingFlags.Instance | BindingFlags.Public); internal static FieldInfo serverSeenField = typeof(PlayerDeathHead).GetField("serverSeen", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo eyeMaterialField = typeof(PlayerDeathHead).GetField("eyeMaterial", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo eyeMaterialAmountField = typeof(PlayerDeathHead).GetField("eyeMaterialAmount", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo eyeFlashLerpField = typeof(PlayerDeathHead).GetField("eyeFlashLerp", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo isSpeakingField = typeof(TTSVoice).GetField("isSpeaking", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo spectatedField = typeof(PlayerDeathHead).GetField("spectated", BindingFlags.Instance | BindingFlags.NonPublic); internal static Dictionary deadPlayersVoicePitch = new Dictionary(); internal static PlayerDeathHead GetPlayerDeathHead(PlayerAvatar avatar) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) return (!Object.op_Implicit((Object)(object)avatar)) ? ((PlayerDeathHead)null) : ((PlayerDeathHead)playerDeathHeadField.GetValue(avatar)); } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] [HarmonyPostfix] public static void InitPlayer(ref bool ___isLocal, PlayerAvatar __instance) { if (___isLocal) { localPlayer = __instance; } ((Component)__instance).gameObject.AddComponent(); } [HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")] [HarmonyPrefix] public static void RandomizeTTSPitch(int value, RoundDirector __instance) { int num = value; deadPlayersVoicePitch.Clear(); for (int i = 0; i < GameDirector.instance.PlayerList.Count; i++) { PlayerAvatar val = GameDirector.instance.PlayerList[i]; DeadTTSPlayerData component = ((Component)val).GetComponent(); if (!Object.op_Implicit((Object)(object)val)) { continue; } float num2 = 1f; if (GameManager.Multiplayer() && value > 0) { int num3 = -1; int seed = -1; try { num3 = val.photonView.Owner.ActorNumber; } catch (Exception ex) { Plugin.LogWarning("Failed to get player id for player: " + ((Object.op_Implicit((Object)(object)component) && !string.IsNullOrEmpty(component.playerName)) ? component.playerName : ((Object)val).name) + " when calculating random seed. Don't worry about this."); Plugin.LogWarningVerbose("Error: " + ex); } if (num3 != -1) { seed = num + num3; Random random = new Random(seed); float value2 = ConfigSettings.minRandomPitch.Value; float value3 = ConfigSettings.maxRandomPitch.Value; num2 = (float)(random.NextDouble() * (double)(value3 - value2) + (double)value2); } if (deadPlayersVoicePitch.TryGetValue(val, out var value4) && num2 != value4) { Plugin.Log("Setting dead TTS pitch for player with id: " + num3 + " to: " + num2); } Plugin.LogVerbose("DeadTTS Pitch BaseSeed: " + num + " PlayerId: " + num3 + " PlayerSeed: " + seed); } deadPlayersVoicePitch[val] = num2; } } [HarmonyPatch(typeof(PlayerVoiceChat), "TtsFollowVoiceSettings")] [HarmonyPostfix] public static void OnTtsFollowVoiceSettings(ref PlayerAvatar ___playerAvatar, ref AudioLowPassLogic ___lowPassLogicTTS, ref bool ___inLobbyMixerTTS, ref float ___clipLoudnessTTS, PlayerVoiceChat __instance) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)___playerAvatar) || !GameManager.Multiplayer() || (int)GameDirector.instance.currentState != 2 || (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena())) { return; } DeadTTSPlayerData component = ((Component)___playerAvatar).GetComponent(); PlayerDeathHead playerDeathHead = GetPlayerDeathHead(___playerAvatar); if (!(IsPlayerDead(___playerAvatar) & ___inLobbyMixerTTS)) { return; } if (!ConfigSettings.onlyEnableWhenDiscovered.Value || !Object.op_Implicit((Object)(object)playerDeathHead) || (bool)serverSeenField.GetValue(playerDeathHead) || IsLocalPlayerDead() || !SemiFunc.RunIsLevel()) { if (Object.op_Implicit((Object)(object)__instance.ttsAudioSource) && Object.op_Implicit((Object)(object)__instance.mixerTTSSound)) { if ((Object)(object)__instance.ttsAudioSource.outputAudioMixerGroup != (Object)(object)__instance.mixerTTSSound) { __instance.ttsAudioSource.outputAudioMixerGroup = __instance.mixerTTSSound; if (!Object.op_Implicit((Object)(object)playerDeathHead)) { Plugin.LogError("Game did not assign player a death head."); } if (!Object.op_Implicit((Object)(object)__instance.ttsVoice)) { Plugin.LogError("Game did not assign player a tts voice."); } else { Plugin.LogVerbose($"The game has toggled ON lobby chat for player: {((Object.op_Implicit((Object)(object)component) && !string.IsNullOrEmpty(component.playerName)) ? component.playerName : ((Object)___playerAvatar).name)} ({___playerAvatar.photonView.Owner.ActorNumber}). Disabling TTS lobby mixer."); __instance.ttsVoice.setVoice(1); __instance.ttsVoice.StopAndClearVoice(); } } float pitch = (deadPlayersVoicePitch.ContainsKey(___playerAvatar) ? deadPlayersVoicePitch[___playerAvatar] : 1f); __instance.ttsAudioSource.pitch = pitch; if ((Object)(object)___playerAvatar != (Object)(object)localPlayer && (!ConfigSettings.disableWhileDead.Value || !IsLocalPlayerDead())) { __instance.ttsAudioSource.volume = ConfigSettings.deadTTSVolume.Value; if (ConfigSettings.useSpatialAudioDeadTTS.Value) { __instance.ttsAudioSource.spatialBlend = 1f; } } else { __instance.ttsAudioSource.volume = 1f; } } else { string text = (((Object)(object)__instance.ttsAudioSource == (Object)null) ? "Game did not assign player a ttsAudioSource. " : ""); text += (((Object)(object)__instance.mixerTTSSound == (Object)null) ? "Game did not assign player a mixerTTSSound." : ""); if (!string.IsNullOrEmpty(text)) { Plugin.LogError(text.TrimEnd(new char[1] { ' ' })); } } } if (Object.op_Implicit((Object)(object)playerDeathHead)) { bool flag = (bool)isSpeakingField.GetValue(__instance.ttsVoice); Material val = (Material)eyeMaterialField.GetValue(playerDeathHead); int num = (int)eyeMaterialAmountField.GetValue(playerDeathHead); if (flag) { float num2 = (float)eyeFlashLerpField.GetValue(playerDeathHead); float num3 = Mathf.Clamp01(Mathf.Max(___clipLoudnessTTS, 0f) / 0.2f); num2 = Mathf.Lerp(num2, num3, 20f * Time.deltaTime); val.SetFloat(num, Mathf.Pow(num2, 0.5f)); eyeFlashLerpField.SetValue(playerDeathHead, num2); } else { val.SetFloat(num, 0f); eyeFlashLerpField.SetValue(playerDeathHead, 0); } } } [HarmonyPatch(typeof(PlayerVoiceChat), "ToggleMixer")] [HarmonyPostfix] public static void OnToggleOffLobbyChat(bool _lobby, bool _distorted, ref PlayerAvatar ___playerAvatar, PlayerVoiceChat __instance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown if (!GameManager.Multiplayer() || (int)GameDirector.instance.currentState != 2 || (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena())) { return; } DeadTTSPlayerData component = ((Component)___playerAvatar).GetComponent(); PhotonView val = (Object.op_Implicit((Object)(object)component) ? component.photonView : ___playerAvatar.photonView); if (!_lobby) { Plugin.LogVerbose($"The game has toggled OFF lobby chat for player: {((Object.op_Implicit((Object)(object)component) && !string.IsNullOrEmpty(component.playerName)) ? component.playerName : ((Object)___playerAvatar).name)} ({___playerAvatar.photonView.Owner.ActorNumber})"); if (Object.op_Implicit((Object)(object)__instance.ttsAudioSource) && Object.op_Implicit((Object)(object)__instance.mixerTTSSound)) { __instance.ttsAudioSource.volume = 1f; __instance.ttsAudioSource.pitch = 1f; __instance.ttsVoice.setVoice(0); } PlayerDeathHead playerDeathHead = GetPlayerDeathHead(___playerAvatar); if (Object.op_Implicit((Object)(object)playerDeathHead)) { Material val2 = (Material)eyeMaterialField.GetValue(playerDeathHead); int num = (int)eyeMaterialAmountField.GetValue(playerDeathHead); val2.SetFloat(num, 0f); } } } [HarmonyPatch(typeof(PlayerVoiceChat), "LateUpdate")] [HarmonyPrefix] public static void MoveTTSAudioTransform(ref PlayerAvatar ___playerAvatar, ref bool ___inLobbyMixerTTS, PlayerVoiceChat __instance) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)___playerAvatar) && GameManager.Multiplayer() && (int)GameDirector.instance.currentState == 2 && (SemiFunc.RunIsLevel() || SemiFunc.RunIsShop() || SemiFunc.RunIsArena())) { PlayerDeathHead playerDeathHead = GetPlayerDeathHead(___playerAvatar); if (___inLobbyMixerTTS && IsPlayerDead(___playerAvatar) && Object.op_Implicit((Object)(object)playerDeathHead) && Object.op_Implicit((Object)(object)__instance.ttsVoice)) { ((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, ((Component)playerDeathHead).transform.position, 30f * Time.deltaTime); } } } public static bool IsLocalPlayerDead() { return IsPlayerDead(PlayerAvatar.instance); } public static bool IsPlayerDead(PlayerAvatar playerAvatar) { if (!Object.op_Implicit((Object)(object)playerAvatar) || !(bool)isDisabledField.GetValue(playerAvatar)) { return false; } PlayerDeathHead playerDeathHead = GetPlayerDeathHead(playerAvatar); return !Object.op_Implicit((Object)(object)playerDeathHead) || ((Behaviour)playerDeathHead).isActiveAndEnabled; } } } namespace REPO_DeadTTS.Config { [Serializable] public static class ConfigSettings { public static ConfigEntry minRandomPitch; public static ConfigEntry maxRandomPitch; public static ConfigEntry deadTTSVolume; public static ConfigEntry displayDeadTTSText; public static ConfigEntry useSpatialAudioDeadTTS; public static ConfigEntry onlyEnableWhenDiscovered; public static ConfigEntry disableWhileDead; public static ConfigEntry deadTTSColor; public static ConfigEntry fixMissingDeathHeads; public static ConfigEntry verboseLogs; public static Dictionary currentConfigEntries = new Dictionary(); internal static void BindConfigSettings() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Expected O, but got Unknown Plugin.Log("Binding Configs"); minRandomPitch = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Dead TTS Random Pitch Min", 0.8f, new ConfigDescription("The lower range limit when randomizing dead players pitch.\nValues will be clamped between 0.8 and 2.0", (AcceptableValueBase)(object)new AcceptableValueRange(0.8f, 2f), Array.Empty()))); maxRandomPitch = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Dead TTS Random Pitch Max", 1.5f, new ConfigDescription("The upper range limit when randomizing dead players pitch.\nValues will be clamped between 0.8 and 2.0", (AcceptableValueBase)(object)new AcceptableValueRange(0.8f, 2f), Array.Empty()))); deadTTSVolume = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Dead TTS Volume", 0.5f, new ConfigDescription("Affects the TTS volume of all dead players.\nSet to 0 to mute the TTS of dead players. If muted, the TTS text will still appear.\nValues will be clamped between 0.0 and 1.0", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty()))); displayDeadTTSText = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Display Dead TTS Text", true, "If true, TTS Text will appear from dead players' heads.")); useSpatialAudioDeadTTS = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Use Spatial Audio", true, "If true, TTS audio from dead players should be 3D directional.\nIf false, the audio should appear as if it's in your head all the time.")); onlyEnableWhenDiscovered = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Enable When Discovered", false, "If true, you will only hear DeadTTS from players once their head is \"discovered\".")); disableWhileDead = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Disable While Dead", false, "Dead TTS will be disabled while you are dead.")); deadTTSColor = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Dead TTS Text Color Hex", "CC3333", new ConfigDescription("Hex color value for dead TTS text color. Hex string must be 6 characters long. Leave this field blank to use the vanilla TTS color.", (AcceptableValueBase)null, Array.Empty()))); fixMissingDeathHeads = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Fix Missing Death Heads", true, new ConfigDescription("Fixes an uncommon bug where the game may not assign a player a death head.", (AcceptableValueBase)null, Array.Empty()))); verboseLogs = AddConfigEntry(((BaseUnityPlugin)Plugin.instance).Config.Bind("General", "Verbose Logs", false, new ConfigDescription("Enables verbose logs. Useful for debugging.", (AcceptableValueBase)null, Array.Empty()))); if (minRandomPitch.Value < 0.8f) { minRandomPitch.Value = (float)((ConfigEntryBase)minRandomPitch).DefaultValue; } if (maxRandomPitch.Value > 2f) { maxRandomPitch.Value = (float)((ConfigEntryBase)maxRandomPitch).DefaultValue; } minRandomPitch.Value = Mathf.Clamp(minRandomPitch.Value, 0.8f, 2f); maxRandomPitch.Value = Mathf.Max(maxRandomPitch.Value, minRandomPitch.Value); deadTTSVolume.Value = Mathf.Clamp(deadTTSVolume.Value, 0f, 2f); deadTTSColor.Value = deadTTSColor.Value.Trim(new char[1] { ' ' }); } internal static ConfigEntry AddConfigEntry(ConfigEntry configEntry) { currentConfigEntries.Add(((ConfigEntryBase)configEntry).Definition.Key, (ConfigEntryBase)(object)configEntry); return configEntry; } } }