Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DeadTTS v1.1.0
REPO-DeadTTS.dll
Decompiled 6 months agousing 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.0")] 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<Type> 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.0")); } 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.0"; } } 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<PlayerAvatar>(); 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<DeadTTSPlayerData>(); 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<DeadTTSPlayerData>() : null); if (Object.op_Implicit((Object)(object)deadTTSPlayerData)) { if (ConfigSettings.fixMissingDeathHeads.Value) { deadTTSPlayerData.ReassignPlayerDeathHead(__instance); } if (GameManager.Multiplayer()) { PhotonView component = ((Component)deadTTSPlayerData.playerDeathHead).GetComponent<PhotonView>(); 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>(); PlayerDeathHead[] array2 = array; foreach (PlayerDeathHead val in array2) { PhotonView component = ((Component)val).GetComponent<PhotonView>(); 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<DeadTTSPlayerData>(); 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<PhotonView>().ViewID; if ((Object)(object)playerAvatar.playerDeathHead != (Object)(object)playerDeathHead || (Object)(object)playerDeathHead.playerAvatar != (Object)(object)playerAvatar || (Object)(object)((Component)playerDeathHead).transform.parent != (Object)(object)((Component)playerAvatar).transform.parent) { playerAvatar.playerDeathHead = 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))) { playerDeathHead.arenaCrown.SetActive(true); } } catch { } } } [HarmonyPatch] public static class UIPatcher { private static HashSet<WorldSpaceUITTS> deadTTSElements = new HashSet<WorldSpaceUITTS>(); private static HashSet<WorldSpaceUITTS> deadTTSElementsFollowLog = new HashSet<WorldSpaceUITTS>(); private static Dictionary<PlayerAvatar, bool> isDisabledStates = new Dictionary<PlayerAvatar, bool>(); 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_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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; } if (!Object.op_Implicit((Object)(object)_player.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(_player.playerDeathHead)) { return; } if (!ConfigSettings.onlyEnableWhenDiscovered.Value || (bool)PlayerPatcher.serverSeenField.GetValue(_player.playerDeathHead) || PlayerPatcher.IsLocalPlayerDead() || !SemiFunc.RunIsLevel()) { try { WorldSpaceUITTS component = Object.Instantiate<GameObject>(__instance.TTSPrefab, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation, ((Component)__instance).transform).GetComponent<WorldSpaceUITTS>(); 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 = "<color=#" + text2 + ">" + ((TMP_Text)val).text + "</color>"; ((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)_player.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(_player.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 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<PlayerAvatar, float> deadPlayersVoicePitch = new Dictionary<PlayerAvatar, float>(); [HarmonyPatch(typeof(PlayerAvatar), "Awake")] [HarmonyPostfix] public static void InitPlayer(ref bool ___isLocal, PlayerAvatar __instance) { if (___isLocal) { localPlayer = __instance; } ((Component)__instance).gameObject.AddComponent<DeadTTSPlayerData>(); } [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<DeadTTSPlayerData>(); 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_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0329: 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<DeadTTSPlayerData>(); if (!(IsPlayerDead(___playerAvatar) & ___inLobbyMixerTTS)) { return; } if (!ConfigSettings.onlyEnableWhenDiscovered.Value || !Object.op_Implicit((Object)(object)___playerAvatar.playerDeathHead) || (bool)serverSeenField.GetValue(___playerAvatar.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)___playerAvatar.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)___playerAvatar.playerDeathHead)) { bool flag = (bool)isSpeakingField.GetValue(__instance.ttsVoice); Material val = (Material)eyeMaterialField.GetValue(___playerAvatar.playerDeathHead); int num = (int)eyeMaterialAmountField.GetValue(___playerAvatar.playerDeathHead); if (flag) { float num2 = (float)eyeFlashLerpField.GetValue(___playerAvatar.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(___playerAvatar.playerDeathHead, num2); } else { val.SetFloat(num, 0f); eyeFlashLerpField.SetValue(___playerAvatar.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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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<DeadTTSPlayerData>(); 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); } if (Object.op_Implicit((Object)(object)___playerAvatar.playerDeathHead)) { Material val2 = (Material)eyeMaterialField.GetValue(___playerAvatar.playerDeathHead); int num = (int)eyeMaterialAmountField.GetValue(___playerAvatar.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_0087: 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_00a8: 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()) && ___inLobbyMixerTTS && IsPlayerDead(___playerAvatar) && Object.op_Implicit((Object)(object)___playerAvatar.playerDeathHead) && Object.op_Implicit((Object)(object)__instance.ttsVoice)) { ((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, ((Component)___playerAvatar.playerDeathHead).transform.position, 30f * Time.deltaTime); } } public static bool IsLocalPlayerDead() { return IsPlayerDead(PlayerAvatar.instance); } public static bool IsPlayerDead(PlayerAvatar playerAvatar) { return Object.op_Implicit((Object)(object)playerAvatar) && (bool)isDisabledField.GetValue(playerAvatar) && (!Object.op_Implicit((Object)(object)playerAvatar.playerDeathHead) || ((Behaviour)playerAvatar.playerDeathHead).isActiveAndEnabled); } } } namespace REPO_DeadTTS.Config { [Serializable] public static class ConfigSettings { public static ConfigEntry<float> minRandomPitch; public static ConfigEntry<float> maxRandomPitch; public static ConfigEntry<float> deadTTSVolume; public static ConfigEntry<bool> displayDeadTTSText; public static ConfigEntry<bool> useSpatialAudioDeadTTS; public static ConfigEntry<bool> onlyEnableWhenDiscovered; public static ConfigEntry<bool> disableWhileDead; public static ConfigEntry<string> deadTTSColor; public static ConfigEntry<bool> fixMissingDeathHeads; public static ConfigEntry<bool> verboseLogs; public static Dictionary<string, ConfigEntryBase> currentConfigEntries = new Dictionary<string, ConfigEntryBase>(); 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<float>(((BaseUnityPlugin)Plugin.instance).Config.Bind<float>("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<float>(0.8f, 2f), Array.Empty<object>()))); maxRandomPitch = AddConfigEntry<float>(((BaseUnityPlugin)Plugin.instance).Config.Bind<float>("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<float>(0.8f, 2f), Array.Empty<object>()))); deadTTSVolume = AddConfigEntry<float>(((BaseUnityPlugin)Plugin.instance).Config.Bind<float>("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<float>(0f, 1f), Array.Empty<object>()))); displayDeadTTSText = AddConfigEntry<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("General", "Display Dead TTS Text", true, "If true, TTS Text will appear from dead players' heads.")); useSpatialAudioDeadTTS = AddConfigEntry<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("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<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("General", "Enable When Discovered", false, "If true, you will only hear DeadTTS from players once their head is \"discovered\".")); disableWhileDead = AddConfigEntry<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("General", "Disable While Dead", false, "Dead TTS will be disabled while you are dead.")); deadTTSColor = AddConfigEntry<string>(((BaseUnityPlugin)Plugin.instance).Config.Bind<string>("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<object>()))); fixMissingDeathHeads = AddConfigEntry<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("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<object>()))); verboseLogs = AddConfigEntry<bool>(((BaseUnityPlugin)Plugin.instance).Config.Bind<bool>("General", "Verbose Logs", false, new ConfigDescription("Enables verbose logs. Useful for debugging.", (AcceptableValueBase)null, Array.Empty<object>()))); 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<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { currentConfigEntries.Add(((ConfigEntryBase)configEntry).Definition.Key, (ConfigEntryBase)(object)configEntry); return configEntry; } } }