using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using AK; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using CellMenu; using Dissonance; using GTFO.API; using GTFO.API.Utilities; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using Microsoft.CodeAnalysis; using Player; using SNetwork; 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: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BetterTextChat")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BetterTextChat")] [assembly: AssemblyTitle("BetterTextChat")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BetterTextChat { internal static class BetterComposition { private static int caretPosition; private static bool caretVisible; private static float caretVisibilityTimer; private static PlayerAgent? customFromPlayer; private static PlayerAgent? customToPlayer; [HarmonyPatch(typeof(PlayerChatManager), "UpdateTextChatInput")] [HarmonyPrefix] private static void PlayerChatManager__UpdateTextChatInput__Prefix(PlayerChatManager __instance, ref bool __runOriginal) { __runOriginal = false; if (!PlayerChatManager.TextChatInputEnabled) { return; } if (PlayerChatManager.Current != null && !PlayerChatManager.InChatMode && InputMapper.GetButtonDownKeyMouseGamepad((InputAction)56, (eFocusState)0)) { PlayerChatManager.Log("Open chat window"); __instance.EnterChatMode(); } else if (InputMapper.GetButtonDownKeyMouseGamepad((InputAction)57, (eFocusState)0) || Input.GetKeyDown((KeyCode)27)) { __instance.ExitChatMode(); } else if (PlayerChatManager.Current != null && PlayerChatManager.InChatMode) { int num = caretPosition; string currentValue = __instance.m_currentValue; ChatInputRepetitionManager.Update(); UpdateCaretPosition(__instance); UpdateMCurrentValue(__instance); if (caretPosition != num || currentValue != __instance.m_currentValue) { UpdateCaretVisibility(forceVisible: true); } if (currentValue != __instance.m_currentValue) { TypingIndicator.OnLocalPlayerTyping(); } UpdateRecipientPlayer(); __instance.m_currentImeCompositionString = Input.compositionString; __instance.UpdateMessage(); } } private static void UpdateCaretPosition(PlayerChatManager __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)ChatInputRepetitionManager.inputActionThisFrame == 17) { caretPosition--; } if ((int)ChatInputRepetitionManager.inputActionThisFrame == 18) { caretPosition++; } if (InputMapper.GetButtonDownKeyMouseGamepad((InputAction)21, (eFocusState)0)) { caretPosition = 0; } if (InputMapper.GetButtonDownKeyMouseGamepad((InputAction)22, (eFocusState)0)) { caretPosition = __instance.m_currentValue.Length; } caretPosition = Mathf.Clamp(caretPosition, 0, __instance.m_currentValue.Length); } private static void UpdateMCurrentValue(PlayerChatManager __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)ChatInputRepetitionManager.inputActionThisFrame == 19 && __instance.m_currentValue.Length > caretPosition) { __instance.m_currentValue = __instance.m_currentValue.Remove(caretPosition, 1); } string inputString = Input.inputString; for (int i = 0; i < inputString.Length; i++) { char c = inputString[i]; if (!Config.AllowForbiddenChars.Value && ((Il2CppArrayBase)(object)__instance.m_forbiddenChars).Contains((int)c)) { continue; } switch (c) { case '\b': if (caretPosition > 0) { __instance.m_currentValue = __instance.m_currentValue.Remove(caretPosition - 1, 1); caretPosition--; } break; default: if (c != '\r') { __instance.m_currentValue = __instance.m_currentValue.Insert(caretPosition, c.ToString()); caretPosition++; break; } goto case '\n'; case '\n': if (Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) { __instance.m_currentValue = __instance.m_currentValue.Insert(caretPosition, "\n"); caretPosition++; } else { __instance.m_currentImeCompositionString = ""; __instance.PostMessage(); } break; } } if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)99)) { GUIUtility.systemCopyBuffer = __instance.m_currentValue; } if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)118)) { string text = GUIUtility.systemCopyBuffer.ReplaceLineEndings("\n"); __instance.m_currentValue = __instance.m_currentValue.Insert(caretPosition, text); caretPosition += text.Length; } } private static void UpdateRecipientPlayer() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); Dictionary dictionary = new Dictionary { { (KeyCode)282, 0 }, { (KeyCode)283, 1 }, { (KeyCode)284, 2 }, { (KeyCode)285, 3 }, { (KeyCode)286, 4 }, { (KeyCode)287, 5 }, { (KeyCode)288, 6 }, { (KeyCode)289, 7 } }; foreach (KeyCode key in dictionary.Keys) { if (!Input.GetKeyDown(key)) { continue; } int num = dictionary[key]; Enumerator enumerator2 = PlayerManager.PlayerAgentsInLevel.GetEnumerator(); while (enumerator2.MoveNext()) { PlayerAgent current2 = enumerator2.Current; if (current2.PlayerSlotIndex == num) { if (flag) { customFromPlayer = (((Object)(object)customFromPlayer == (Object)(object)current2) ? null : current2); } else { customToPlayer = (((Object)(object)customToPlayer == (Object)(object)current2) ? null : current2); } break; } } break; } } [HarmonyPatch(typeof(PlayerChatManager), "EnterChatMode")] [HarmonyPostfix] private static void PlayerChatManager__EnterChatMode__Postfix() { UpdateCaretVisibility(forceVisible: true); } [HarmonyPatch(typeof(PlayerChatManager), "ExitChatMode")] [HarmonyPostfix] private static void PlayerChatManager__ExitChatMode__Postfix(PlayerChatManager __instance) { if (Config.DiscardDraftMessage.Value) { caretPosition = 0; __instance.m_currentValue = ""; __instance.m_currentImeCompositionString = ""; customFromPlayer = null; customToPlayer = null; __instance.UpdateMessage(); } } [HarmonyPatch(typeof(PlayerChatManager), "PostMessage")] [HarmonyPrefix] private static void PlayerChatManager__PostMessage__Prefix(ref bool __runOriginal) { __runOriginal = false; } [HarmonyPatch(typeof(PlayerChatManager), "PostMessage")] [HarmonyPostfix] private static void PlayerChatManager__PostMessage__Postfix(PlayerChatManager __instance) { if (__instance.m_currentValue.Trim('\n').Length > 0) { string text = WordWrap.Wrap(__instance.m_currentValue, __instance.m_maxLen); if (customFromPlayer == null) { customFromPlayer = PlayerManager.GetLocalPlayerAgent(); } string[] array = text.Split('\n'); foreach (string text2 in array) { PlayerChatManager.WantToSentTextMessage(customFromPlayer, text2, customToPlayer); } } caretPosition = 0; __instance.m_currentValue = ""; __instance.m_currentImeCompositionString = ""; customFromPlayer = null; customToPlayer = null; __instance.ExitChatMode(); } [HarmonyPatch(typeof(PUI_GameEventLog), "Update")] [HarmonyPostfix] private static void PUI_GameEventLog__Update__Postfix(PUI_GameEventLog __instance) { //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) if (PlayerChatManager.InChatMode) { ((TMP_Text)__instance.m_txtInput).enableWordWrapping = false; ((TMP_Text)__instance.m_txtInput).overflowMode = (TextOverflowModes)0; ((TMP_Text)__instance.m_txtInput).alignment = (TextAlignmentOptions)1025; UpdateCaretVisibility(); string text = __instance.m_textInputString ?? string.Empty; if (caretPosition == text.Length) { text = WordWrap.Wrap(text, PlayerChatManager.Current.m_maxLen); text = ((!caretVisible) ? (text + " ") : (text + "_")); } else if (caretVisible) { char c = text[caretPosition]; char c2 = (char)WordWrap.GetCharType(c); text = text.Remove(caretPosition, 1); text = text.Insert(caretPosition, c2.ToString()); text = WordWrap.Wrap(text, PlayerChatManager.Current.m_maxLen); string text2 = "_"; if (c2 == '\ue000') { text2 += "\n"; } text = text.Replace(c2.ToString(), text2); } else { text = WordWrap.Wrap(text, PlayerChatManager.Current.m_maxLen); } ((TMP_Text)__instance.m_txtInput).text = text; } else { ((TMP_Text)__instance.m_txtInput).text = ""; } string text3 = ""; if (customFromPlayer != null) { text3 = text3 + "From " + PlayerNameExtentions.GetColoredName(customFromPlayer, (string)null) + "\n"; } if (customToPlayer != null) { text3 = text3 + "To " + PlayerNameExtentions.GetColoredName(customToPlayer, (string)null) + "\n"; } if (text3.Length > 0) { ((TMP_Text)__instance.m_txtInput).text = "" + text3 + "\n" + ((TMP_Text)__instance.m_txtInput).text; } float num = ((TMP_Text)__instance.m_txtInput).preferredHeight + 5.1f; Transform val = __instance.m_chatActiveRoot.transform.FindChild("Input Background Active"); SpriteRenderer component = ((Component)val).GetComponent(); component.size = new Vector2(component.size.x, num); RectTransform component2 = ((Component)val).GetComponent(); ((Transform)component2).localPosition = new Vector3(0f, num / 2f - 1f, 0f); __instance.ShowAndUpdateItemPositions(); } [HarmonyPatch(typeof(PUI_GameEventLog), "ShowAndUpdateItemPositions")] [HarmonyPrefix] private static void PUI_GameEventLog__ShowAndUpdateItemPositions__Prefix(PUI_GameEventLog __instance, ref bool __runOriginal) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) __runOriginal = false; float num = ((TMP_Text)__instance.m_txtInput).preferredHeight + 5.1f; float num2 = (((TMP_Text)__instance.m_txtInput).text.Contains('\n') ? (num - __instance.m_logItemHeight) : 0f); for (int i = 0; i < __instance.m_logItems.Count; i++) { ((Component)__instance.m_logItems[i]).transform.localPosition = new Vector3(0f, num2 + (float)(__instance.m_logItems.Count - i - 1) * __instance.m_logItemHeight); } } private static void UpdateCaretVisibility(bool forceVisible = false) { if (forceVisible) { caretVisible = true; caretVisibilityTimer = Time.time + 0.6f; } else if (Time.time > caretVisibilityTimer) { caretVisible = !caretVisible; caretVisibilityTimer = Time.time + 0.6f; } } } internal static class ChatInputRepetitionManager { private static readonly InputAction[] inputActionsToWatch; private static InputAction repeatInputAction; private static float repeatDelayTimer; private static float repeatRateTimer; public static InputAction inputActionThisFrame; public static void Update() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) inputActionThisFrame = (InputAction)0; if (!PlayerChatManager.InChatMode) { return; } if (InputMapper.GetButtonKeyMouseGamepad(repeatInputAction, (eFocusState)0)) { if (Time.time > repeatDelayTimer && Time.time > repeatRateTimer) { inputActionThisFrame = repeatInputAction; repeatRateTimer = Time.time + 0.05f; } return; } repeatInputAction = (InputAction)0; repeatDelayTimer = 0f; repeatRateTimer = 0f; InputAction[] array = inputActionsToWatch; foreach (InputAction val in array) { if (InputMapper.GetButtonKeyMouseGamepad(val, (eFocusState)0)) { inputActionThisFrame = val; repeatInputAction = val; repeatDelayTimer = Time.time + 0.35f; break; } } } static ChatInputRepetitionManager() { //IL_0017: 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) InputAction[] array = new InputAction[3]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); inputActionsToWatch = (InputAction[])(object)array; repeatInputAction = (InputAction)0; repeatDelayTimer = 0f; repeatRateTimer = 0f; inputActionThisFrame = (InputAction)0; } } internal static class WordWrap { public enum CharType { LineSeparator = 57344, WordSeparator, Other } public static CharType GetCharType(char c) { if (c == '\ue000' || c == '\n') { return CharType.LineSeparator; } if (c == '\ue001' || char.IsSeparator(c)) { return CharType.WordSeparator; } return CharType.Other; } public static string Wrap(string original_text, int width) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = new StringBuilder(); StringBuilder stringBuilder3 = new StringBuilder(); foreach (char c in original_text) { if (GetCharType(c) == CharType.LineSeparator) { if (stringBuilder2.Length + stringBuilder3.Length > width) { stringBuilder.Append(stringBuilder2); stringBuilder.Append('\n'); stringBuilder2.Clear(); } stringBuilder2.Append(stringBuilder3); stringBuilder3.Clear(); stringBuilder.Append(stringBuilder2); stringBuilder.Append(c); stringBuilder2.Clear(); } else if (GetCharType(c) == CharType.WordSeparator) { if (stringBuilder2.Length + stringBuilder3.Length > width) { stringBuilder.Append(stringBuilder2); stringBuilder.Append('\n'); stringBuilder2.Clear(); } stringBuilder2.Append(stringBuilder3); stringBuilder2.Append(c); stringBuilder3.Clear(); } else { stringBuilder3.Append(c); if (stringBuilder3.Length > width) { int num = width - stringBuilder2.Length; stringBuilder2.Append(stringBuilder3.ToString(0, num)); stringBuilder3.Remove(0, num); stringBuilder.Append(stringBuilder2); stringBuilder.Append('\n'); stringBuilder2.Clear(); } } } if (stringBuilder2.Length + stringBuilder3.Length > width) { stringBuilder.Append(stringBuilder2); stringBuilder.Append('\n'); stringBuilder2.Clear(); } stringBuilder2.Append(stringBuilder3); stringBuilder3.Clear(); stringBuilder.Append(stringBuilder2); stringBuilder2.Clear(); if (stringBuilder.Length > 0) { if (stringBuilder[stringBuilder.Length - 1] == '\n') { if (original_text[original_text.Length - 1] != '\n') { stringBuilder.Remove(stringBuilder.Length - 1, 1); } } } return stringBuilder.ToString(); } } internal static class Config { public static readonly ConfigEntry AllowForbiddenChars; public static readonly ConfigEntry DiscardDraftMessage; public static readonly ConfigEntry ChatHistoryLength; public static readonly ConfigEntry DisableAutoSize; public static readonly ConfigEntry ShowTypingIndicator; public static readonly ConfigEntry SendTypingIndicator; public static readonly ConfigEntry ShowTimestampInPageLoadout; public static readonly ConfigEntry ShowTimestampInPageMap; public static readonly ConfigEntry ShowTimestampInPlayerLayer; private static readonly ConfigEntry _timestampUse24Hours; private static readonly ConfigEntry _timestampShowSeconds; public static string TimestampFormatString; static Config() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown TimestampFormatString = string.Empty; ConfigFile configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "BetterTextChat.cfg"), true); AllowForbiddenChars = configFile.Bind("General", "Allow special characters", true, "Makes it possible to type <, >, and = in chat. (Beware: Things can get weird if you use formatting tags, especially across multiple lines.)"); DiscardDraftMessage = configFile.Bind("General", "Discard draft message", false, "If you start typing a message, then close chat without sending it, discard it instead of remembering it next time you open chat."); ChatHistoryLength = configFile.Bind("General", "Chat history length", 15, "Number of messages to show at once in the chat history."); DisableAutoSize = configFile.Bind("General", "Disable shrink-to-fit", true, "Prevent font size decreasing when timestamps are enabled, or when displaying messages from players with long names, or when displaying messages with certain formatting tags."); ShowTypingIndicator = configFile.Bind("Typing indicator", "Display typing indicator", true, "Show a message like [PlayerName is typing...] when other BetterTextChat users are typing in chat."); SendTypingIndicator = configFile.Bind("Typing indicator", "Send typing indicator", true, "Let other BetterTextChat users know when you are typing in chat."); ShowTimestampInPageLoadout = configFile.Bind("Timestamps", "Show timestamps in lobby", false, "Show message timestamps in the lobby menu."); ShowTimestampInPageMap = configFile.Bind("Timestamps", "Show timestamps in map", false, "Show message timestamps in the map."); ShowTimestampInPlayerLayer = configFile.Bind("Timestamps", "Show timestamps in game", false, "Show message timestamps in the in-game chat."); bool flag = DateTimeFormatInfo.CurrentInfo.ShortTimePattern.Contains('H'); _timestampUse24Hours = configFile.Bind("Timestamps", "Use 24-hour clock", flag, "Use a 24-hour clock in message timestamps, e.g. 16:30 vs 4:30."); _timestampShowSeconds = configFile.Bind("Timestamps", "Show seconds", false, "Show seconds in message timestamps."); OnUpdate(); LiveEditListener val = LiveEdit.CreateListener(Path.GetDirectoryName(configFile.ConfigFilePath), Path.GetFileName(configFile.ConfigFilePath), false); val.FileChanged += (LiveEditEventHandler)delegate { configFile.Reload(); OnUpdate(); }; } private static void OnUpdate() { PUI_GameEventLog.s_logItemsMax = ChatHistoryLength.Value; UpdateTimestampFormatString(); if (GuiManager.Current != null) { ScrollableGameEventLog.UpdateMLogItems(GuiManager.MainMenuLayer.PageLoadout.m_gameEventLog, force: true); ScrollableGameEventLog.UpdateMLogItems(GuiManager.MainMenuLayer.PageMap.m_gameEventLog, force: true); ScrollableGameEventLog.UpdateMLogItems(GuiManager.PlayerLayer.m_gameEventLog, force: true); } } private static void UpdateTimestampFormatString() { StringBuilder stringBuilder = new StringBuilder(); if (_timestampUse24Hours.Value) { stringBuilder.Append("HH"); } else { stringBuilder.Append('h'); } stringBuilder.Append(":mm"); if (_timestampShowSeconds.Value) { stringBuilder.Append(":ss"); } TimestampFormatString = stringBuilder.ToString(); } } internal static class PluginInfo { public const string GUID = "Andocas.BetterTextChat"; public const string NAME = "BetterTextChat"; public const string VERSION = "0.6.2"; } [BepInPlugin("Andocas.BetterTextChat", "BetterTextChat", "0.6.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class Plugin : BasePlugin { public override void Load() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Utils.LogInfo("Andocas.BetterTextChat is loading..."); Harmony val = new Harmony("Andocas.BetterTextChat"); val.PatchAll(typeof(BetterComposition)); val.PatchAll(typeof(ScrollableGameEventLog)); TypingIndicator.Setup(); val.PatchAll(typeof(TypingIndicator)); ((BasePlugin)this).AddComponent(); _ = Config.AllowForbiddenChars; Utils.LogInfo("Andocas.BetterTextChat is loaded"); } } internal static class ScrollableGameEventLog { private static readonly Dictionary extensionDataForEachInstance = new Dictionary(); private static PUI_GameEventLog_ExtensionData GetInstanceData(PUI_GameEventLog __instance) { int instanceID = ((Object)__instance).GetInstanceID(); if (!extensionDataForEachInstance.ContainsKey(instanceID)) { extensionDataForEachInstance[instanceID] = new PUI_GameEventLog_ExtensionData(instanceID); } return extensionDataForEachInstance[instanceID]; } [HarmonyPatch(typeof(PUI_GameEventLog), "AddLogItem")] [HarmonyPrefix] private static void PUI_GameEventLog__AddLogItem__Prefix(PUI_GameEventLog __instance, ref bool __runOriginal, string log, eGameEventChatLogType type) { //IL_000c: 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) __runOriginal = false; PUI_GameEventLog_ExtensionData instanceData = GetInstanceData(__instance); log = FixMessagesFromBots(log, type); instanceData.rawEventLog.Add((log, type, DateTime.Now)); UpdateMLogItems(__instance); } [HarmonyPatch(typeof(PUI_GameEventLog), "OnLevelCleanup")] [HarmonyPrefix] private static void PUI_GameEventLog__OnLevelCleanup__Prefix(PUI_GameEventLog __instance, ref bool __runOriginal) { __runOriginal = false; if ((Object)(object)__instance == (Object)(object)GuiManager.PlayerLayer.m_gameEventLog) { PUI_GameEventLog_ExtensionData instanceData = GetInstanceData(__instance); instanceData.rawEventLog.Clear(); } } [HarmonyPatch(typeof(PlayerChatManager), "EnterChatMode")] [HarmonyPatch(typeof(PlayerChatManager), "ExitChatMode")] [HarmonyPostfix] private static void PlayerChatManager__EnterExitChatMode__Postfix() { foreach (PUI_GameEventLog_ExtensionData value in extensionDataForEachInstance.Values) { value.scrollPosition = null; } } [HarmonyPatch(typeof(PUI_GameEventLog), "Update")] [HarmonyPostfix] private static void PUI_GameEventLog__Update__Postfix(PUI_GameEventLog __instance) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) PUI_GameEventLog_ExtensionData instanceData = GetInstanceData(__instance); int num = Math.Min(__instance.m_limitNRLines, PUI_GameEventLog.s_logItemsMax); if (PlayerChatManager.TextChatInputEnabled && PlayerChatManager.InChatMode && Input.mouseScrollDelta.y != 0f) { int? scrollPosition = instanceData.scrollPosition; if (!scrollPosition.HasValue) { instanceData.scrollPosition = instanceData.rawEventLog.Count - num; } instanceData.scrollPosition -= (int)Input.mouseScrollDelta.y; if (instanceData.scrollPosition < 0) { instanceData.scrollPosition = 0; } if (instanceData.scrollPosition >= instanceData.rawEventLog.Count - num) { instanceData.scrollPosition = null; } } UpdateMLogItems(__instance); } internal static void UpdateMLogItems(PUI_GameEventLog __instance, bool force = false) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) PUI_GameEventLog_ExtensionData instanceData = GetInstanceData(__instance); int num = Math.Min(__instance.m_limitNRLines, PUI_GameEventLog.s_logItemsMax); int sequenceHashCode = instanceData.rawEventLog.GetSequenceHashCode(); if (!(instanceData.scrollPosition != instanceData.previousScrollPosition || sequenceHashCode != instanceData.previousRawEventLogHash || force)) { return; } instanceData.previousScrollPosition = instanceData.scrollPosition; instanceData.previousRawEventLogHash = sequenceHashCode; Enumerator enumerator = __instance.m_logItems.GetEnumerator(); while (enumerator.MoveNext()) { PUI_GameEventLog_Item current = enumerator.Current; Object.Destroy((Object)(object)((Component)current).gameObject); } __instance.m_logItems.Clear(); int num2 = instanceData.scrollPosition ?? Mathf.Max(instanceData.rawEventLog.Count - num, 0); int count = Mathf.Min(num, instanceData.rawEventLog.Count - num2); foreach (var (text, type, dateTime) in instanceData.rawEventLog.GetRange(num2, count)) { string text3; if (((Object)(object)__instance == (Object)(object)GuiManager.MainMenuLayer.PageLoadout.m_gameEventLog && Config.ShowTimestampInPageLoadout.Value) || ((Object)(object)__instance == (Object)(object)GuiManager.MainMenuLayer.PageMap.m_gameEventLog && Config.ShowTimestampInPageMap.Value) || ((Object)(object)__instance == (Object)(object)GuiManager.PlayerLayer.m_gameEventLog && Config.ShowTimestampInPlayerLayer.Value)) { string text2 = dateTime.ToString(Config.TimestampFormatString); text3 = "[" + text2 + "] " + text; } else { text3 = text; } PUI_GameEventLog_Item val = GOUtil.SpawnChildAndGetComp(__instance.m_logItemPrefab, __instance.m_itemAlign); ((RectTransformComp)val).Setup(); val.SetText(text3); val.SetType(type); __instance.m_logItems.Add(val); } __instance.ShowAndUpdateItemPositions(); __instance.ResetHideTimer(); } [HarmonyPatch(typeof(PUI_GameEventLog_Item), "Setup")] [HarmonyPostfix] private static void PUI_GameEventLog_Item__Setup__Postfix(PUI_GameEventLog_Item __instance) { if (Config.DisableAutoSize.Value) { ((TMP_Text)__instance.m_text).enableAutoSizing = false; ((TMP_Text)__instance.m_text).enableWordWrapping = false; } } [HarmonyPatch(typeof(CM_PageMap), "UpdateZoomAndPan")] [HarmonyPrefix] private static void CM_PageMap__UpdateZoomAndPan__Prefix(CM_PageMap __instance, ref bool __runOriginal) { if (PlayerChatManager.InChatMode && ((CM_PageBase)__instance).ScrollInput != 0f) { __runOriginal = false; } } private static string FixMessagesFromBots(string message, eGameEventChatLogType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)type == 0 && message.StartsWith("[Bot] dictionary = new Dictionary { { "[Bot]> > Woods: ", "> > [Bot]Woods: " }, { "[Bot]> > Dauda: ", "> > [Bot]Dauda: " }, { "[Bot]> > Hackett: ", "> > [Bot]Hackett: " }, { "[Bot]> > Bishop: ", "> > [Bot]Bishop: " } }; foreach (KeyValuePair item in dictionary) { if (message.StartsWith(item.Key)) { return string.Concat(item.Value, message.AsSpan(item.Value.Length)); } } } return message; } } internal class PUI_GameEventLog_ExtensionData { public int instanceID; public List<(string, eGameEventChatLogType, DateTime)> rawEventLog = new List<(string, eGameEventChatLogType, DateTime)>(); public int previousRawEventLogHash = 0; public int? scrollPosition = null; public int? previousScrollPosition = null; public PUI_GameEventLog_ExtensionData(int instanceID) { this.instanceID = instanceID; } } internal static class Extensions { public static int GetSequenceHashCode(this List sequence) { return sequence.Aggregate(487, (int current, T item) => current * 31 + (item?.GetHashCode() ?? 0)); } } internal static class TypingIndicator { private static readonly Dictionary playersWithBetterTextChat = new Dictionary(); private static readonly Dictionary typingPlayers = new Dictionary(); private static readonly Dictionary lastMessageFromPlayer = new Dictionary(); internal static void Setup() { SetupHello(); SetupPlayerIsTyping(); } [HarmonyPatch(typeof(SNet_GlobalManager), "OnPlayerEvent")] [HarmonyPostfix] private static void SNet_GlobalManager__OnPlayerEvent__Postfix(SNet_Player player, SNet_PlayerEvent playerEvent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Invalid comparison between Unknown and I4 if ((int)playerEvent == 2) { if (player.IsLocal) { Enumerator enumerator = SNet.LobbyPlayers.GetEnumerator(); while (enumerator.MoveNext()) { SNet_Player current = enumerator.Current; if (!current.IsLocal) { Utils.LogInfo("Sending Hello to " + current.NickName); NetworkAPI.InvokeEvent("BetterTextChat-Hello", default(EmptyStruct), current, (SNet_ChannelType)0); } } } else { Utils.LogInfo("Sending Hello to " + player.NickName); NetworkAPI.InvokeEvent("BetterTextChat-Hello", default(EmptyStruct), player, (SNet_ChannelType)0); } } if ((int)playerEvent == 0 && playersWithBetterTextChat.ContainsKey(player.Lookup)) { playersWithBetterTextChat.Remove(player.Lookup); } } private static void SetupHello() { NetworkAPI.RegisterEvent("BetterTextChat-Hello", (Action)OnSNetPlayerHello); } private static void OnSNetPlayerHello(ulong steamID, EmptyStruct emptyStruct) { SNet_Player val = default(SNet_Player); if (SNet.Core.TryGetPlayer(steamID, ref val, false)) { Utils.LogInfo("Received Hello from " + val.NickName); playersWithBetterTextChat[steamID] = val; } else { Utils.LogError($"OnSNetPlayerHello > Could not find SNet_Player for {steamID}"); } } internal static void OnLocalPlayerTyping() { if (Config.SendTypingIndicator.Value) { NetworkAPI.InvokeEvent("BetterTextChat-PlayerIsTyping", default(EmptyStruct), playersWithBetterTextChat.Values.ToList(), (SNet_ChannelType)4); } } private static void SetupPlayerIsTyping() { NetworkAPI.RegisterEvent("BetterTextChat-PlayerIsTyping", (Action)OnSNetPlayerTyping); } private static void OnSNetPlayerTyping(ulong steamID, EmptyStruct emptyStruct) { if (!Config.ShowTypingIndicator.Value || (lastMessageFromPlayer.ContainsKey(steamID) && Time.time < lastMessageFromPlayer[steamID] + 0.5f)) { return; } SNet_Player sNetPlayer = default(SNet_Player); if (typingPlayers.ContainsKey(steamID)) { typingPlayers[steamID].lastSeenTypingAt = Time.time; } else if (SNet.Core.TryGetPlayer(steamID, ref sNetPlayer, false)) { PlayerAgent playerAgentFromSNetPlayer = GetPlayerAgentFromSNetPlayer(sNetPlayer); if (playerAgentFromSNetPlayer != null) { typingPlayers[steamID] = new TypingPlayerData(playerAgentFromSNetPlayer, Time.time); } } } [HarmonyPatch(typeof(PlayerChatManager), "DoSendChatMessage")] [HarmonyPostfix] private static void PlayerChatManager__DoSendChatMessage__Postfix(pChatMessage data) { //IL_0007: 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_003c: Unknown result type (might be due to invalid IL or missing references) lastMessageFromPlayer[data.fromPlayer.lookup] = Time.time; if (typingPlayers.ContainsKey(data.fromPlayer.lookup)) { typingPlayers.Remove(data.fromPlayer.lookup); } } [HarmonyPatch(typeof(PUI_GameEventLog), "UpdateHelpText")] [HarmonyPrefix] private static void PUI_GameEventLog__UpdateHelpText__Prefix(ref bool __runOriginal) { __runOriginal = false; } [HarmonyPatch(typeof(PUI_GameEventLog), "Update")] [HarmonyPostfix] private static void PUI_GameEventLog__Update__Postfix(PUI_GameEventLog __instance) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 __instance.m_checkPushToTalk = CellSettingsManager.SettingsData.VoiceChat.Mode.Value == 1; foreach (ulong item in typingPlayers.Keys.ToList()) { TypingPlayerData typingPlayerData = typingPlayers[item]; if (Time.time > typingPlayerData.lastSeenTypingAt + 2f || typingPlayerData.playerAgent == null) { typingPlayers.Remove(item); } } List list = new List(); bool flag = (int)LocalizationAPI.CurrentLanguage == 1; if (PlayerChatManager.Current.m_dissonanceVoiceBroadcast.IsTransmitting) { list.Add("" + Text.Get(966u) + ""); } if (PlayerChatManager.Current.m_speakingPlayers.Count > 0) { List list2 = new List(); Enumerator enumerator2 = PlayerChatManager.Current.m_speakingPlayers.GetEnumerator(); while (enumerator2.MoveNext()) { VoicePlayerState current2 = enumerator2.Current; SNet_Player sNetPlayerByVoiceState = PlayerChatManager.Current.GetSNetPlayerByVoiceState(current2); PlayerAgent playerAgentFromSNetPlayer = GetPlayerAgentFromSNetPlayer(sNetPlayerByVoiceState); if (playerAgentFromSNetPlayer != null) { list2.Add(PlayerNameExtentions.GetColoredName(playerAgentFromSNetPlayer, (string)null)); } } if (flag) { list.Add($"[{FormatStringList(list2)} {FormatIsArePlural(list2)} speaking]"); } else { foreach (string item2 in list2) { list.Add(Text.Format(965u, (Object[])(object)new Object[1] { Object.op_Implicit(item2) })); } } } if (typingPlayers.Count > 0) { List list3 = new List(); foreach (TypingPlayerData item3 in typingPlayers.Values.OrderBy((TypingPlayerData playerData) => playerData.playerAgent.PlayerSlotIndex)) { list3.Add(PlayerNameExtentions.GetColoredName(item3.playerAgent, (string)null)); } list.Add($"[{FormatStringList(list3)} {FormatIsArePlural(list3)} typing...]"); } if (list.Count > 0) { ((TMP_Text)__instance.m_txtHelp).text = string.Join(" ", list); } else if (__instance.m_checkPushToTalk) { ((TMP_Text)__instance.m_txtHelp).text = Text.Format(1392u, (Object[])(object)new Object[4] { Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)56) + "]"), Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)55) + "]"), Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)33) + "]"), Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)63) + "]") }); } else { ((TMP_Text)__instance.m_txtHelp).text = Text.Format(1393u, (Object[])(object)new Object[3] { Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)56) + "]"), Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)33) + "]"), Object.op_Implicit("[" + InputMapper.GetBindingName((InputAction)63) + "]") }); } } private static PlayerAgent? GetPlayerAgentFromSNetPlayer(SNet_Player sNetPlayer) { Enumerator enumerator = PlayerManager.PlayerAgentsInLevel.GetEnumerator(); while (enumerator.MoveNext()) { PlayerAgent current = enumerator.Current; if ((Object)(object)((Agent)current).m_replicator.OwningPlayer == (Object)(object)sNetPlayer) { return current; } } Utils.LogError("GetPlayerAgentFromSNetPlayer > Could not find PlayerAgent for " + ((Object)sNetPlayer).name); return null; } private static string FormatStringList(List strings) { switch (strings.Count) { case 0: return string.Empty; case 1: return strings[0]; case 2: return strings[0] + " and " + strings[1]; default: return string.Join(", ", strings.GetRange(0, strings.Count - 1)) + ", and " + strings[strings.Count - 1]; } } private static string FormatIsArePlural(List strings) { return (strings.Count == 1) ? "is" : "are"; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct EmptyStruct { } internal class TypingPlayerData { public PlayerAgent playerAgent; public float lastSeenTypingAt; public TypingPlayerData(PlayerAgent playerAgent, float lastSeenTypingAt) { this.playerAgent = playerAgent; this.lastSeenTypingAt = lastSeenTypingAt; } } internal class PttSoundPlayer : MonoBehaviour { private bool previousTransmittingStatus = false; private void Update() { bool flag = CellSettingsManager.SettingsData.VoiceChat.Mode.Value == 1; bool value = CellSettingsManager.SettingsData.VoiceChat.PushToTalk_EnableSound.Value; bool isTransmitting = PlayerChatManager.Current.m_dissonanceVoiceBroadcast.IsTransmitting; if (flag && value) { if (isTransmitting && !previousTransmittingStatus) { CM_PageBase.PostSound(EVENTS.PUSH_TO_TALK_ON, ""); } if (previousTransmittingStatus && !isTransmitting) { CM_PageBase.PostSound(EVENTS.PUSH_TO_TALK_OFF, ""); } } previousTransmittingStatus = isTransmitting; } } internal static class Utils { private static readonly ManualLogSource logger = Logger.CreateLogSource("Andocas.BetterTextChat"); public static void LogInfo(object? o) { logger.LogInfo((object)GetMessage(o)); } public static void LogError(object? o) { logger.LogError((object)GetMessage(o)); } private static string GetMessage(object? o) { if (o == null) { return "No string representation for "; } return o.ToString() ?? $"No string representation for <{o.GetType()}>"; } } }