Decompiled source of BetterTextChat v0.6.2

BetterTextChat.dll

Decompiled a month ago
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<int>)(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<KeyCode, int> dictionary = new Dictionary<KeyCode, int>
			{
				{
					(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<PlayerAgent> 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 = "<mark=#FFF>_</mark>";
					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 = "<size=80%>" + text3 + "\n</size>" + ((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<SpriteRenderer>();
			component.size = new Vector2(component.size.x, num);
			RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
			((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<bool> AllowForbiddenChars;

		public static readonly ConfigEntry<bool> DiscardDraftMessage;

		public static readonly ConfigEntry<int> ChatHistoryLength;

		public static readonly ConfigEntry<bool> DisableAutoSize;

		public static readonly ConfigEntry<bool> ShowTypingIndicator;

		public static readonly ConfigEntry<bool> SendTypingIndicator;

		public static readonly ConfigEntry<bool> ShowTimestampInPageLoadout;

		public static readonly ConfigEntry<bool> ShowTimestampInPageMap;

		public static readonly ConfigEntry<bool> ShowTimestampInPlayerLayer;

		private static readonly ConfigEntry<bool> _timestampUse24Hours;

		private static readonly ConfigEntry<bool> _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<bool>("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<bool>("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<int>("General", "Chat history length", 15, "Number of messages to show at once in the chat history.");
			DisableAutoSize = configFile.Bind<bool>("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<bool>("Typing indicator", "Display typing indicator", true, "Show a message like [PlayerName is typing...] when other BetterTextChat users are typing in chat.");
			SendTypingIndicator = configFile.Bind<bool>("Typing indicator", "Send typing indicator", true, "Let other BetterTextChat users know when you are typing in chat.");
			ShowTimestampInPageLoadout = configFile.Bind<bool>("Timestamps", "Show timestamps in lobby", false, "Show message timestamps in the lobby menu.");
			ShowTimestampInPageMap = configFile.Bind<bool>("Timestamps", "Show timestamps in map", false, "Show message timestamps in the map.");
			ShowTimestampInPlayerLayer = configFile.Bind<bool>("Timestamps", "Show timestamps in game", false, "Show message timestamps in the in-game chat.");
			bool flag = DateTimeFormatInfo.CurrentInfo.ShortTimePattern.Contains('H');
			_timestampUse24Hours = configFile.Bind<bool>("Timestamps", "Use 24-hour clock", flag, "Use a 24-hour clock in message timestamps, e.g. 16:30 vs 4:30.");
			_timestampShowSeconds = configFile.Bind<bool>("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<PttSoundPlayer>();
			_ = Config.AllowForbiddenChars;
			Utils.LogInfo("Andocas.BetterTextChat is loaded");
		}
	}
	internal static class ScrollableGameEventLog
	{
		private static readonly Dictionary<int, PUI_GameEventLog_ExtensionData> extensionDataForEachInstance = new Dictionary<int, PUI_GameEventLog_ExtensionData>();

		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<PUI_GameEventLog_Item> 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 = "<color=#A1A1A133>[" + text2 + "]</color> " + text;
				}
				else
				{
					text3 = text;
				}
				PUI_GameEventLog_Item val = GOUtil.SpawnChildAndGetComp<PUI_GameEventLog_Item>(__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("<color=orange>[Bot]</color><color=#"))
			{
				Dictionary<string, string> dictionary = new Dictionary<string, string>
				{
					{ "<color=orange>[Bot]</color><color=#C21F4E>> > Woods</color>: ", "<color=#C21F4E>> > <color=orange>[Bot]</color>Woods</color>: " },
					{ "<color=orange>[Bot]</color><color=#18935E>> > Dauda</color>: ", "<color=#18935E>> > <color=orange>[Bot]</color>Dauda</color>: " },
					{ "<color=orange>[Bot]</color><color=#20558C>> > Hackett</color>: ", "<color=#20558C>> > <color=orange>[Bot]</color>Hackett</color>: " },
					{ "<color=orange>[Bot]</color><color=#7A1A8E>> > Bishop</color>: ", "<color=#7A1A8E>> > <color=orange>[Bot]</color>Bishop</color>: " }
				};
				foreach (KeyValuePair<string, string> 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<T>(this List<T> sequence)
		{
			return sequence.Aggregate(487, (int current, T item) => current * 31 + (item?.GetHashCode() ?? 0));
		}
	}
	internal static class TypingIndicator
	{
		private static readonly Dictionary<ulong, SNet_Player> playersWithBetterTextChat = new Dictionary<ulong, SNet_Player>();

		private static readonly Dictionary<ulong, TypingPlayerData> typingPlayers = new Dictionary<ulong, TypingPlayerData>();

		private static readonly Dictionary<ulong, float> lastMessageFromPlayer = new Dictionary<ulong, float>();

		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<SNet_Player> enumerator = SNet.LobbyPlayers.GetEnumerator();
					while (enumerator.MoveNext())
					{
						SNet_Player current = enumerator.Current;
						if (!current.IsLocal)
						{
							Utils.LogInfo("Sending Hello to " + current.NickName);
							NetworkAPI.InvokeEvent<EmptyStruct>("BetterTextChat-Hello", default(EmptyStruct), current, (SNet_ChannelType)0);
						}
					}
				}
				else
				{
					Utils.LogInfo("Sending Hello to " + player.NickName);
					NetworkAPI.InvokeEvent<EmptyStruct>("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<EmptyStruct>("BetterTextChat-Hello", (Action<ulong, EmptyStruct>)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<EmptyStruct>("BetterTextChat-PlayerIsTyping", default(EmptyStruct), playersWithBetterTextChat.Values.ToList(), (SNet_ChannelType)4);
			}
		}

		private static void SetupPlayerIsTyping()
		{
			NetworkAPI.RegisterEvent<EmptyStruct>("BetterTextChat-PlayerIsTyping", (Action<ulong, EmptyStruct>)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<string> list = new List<string>();
			bool flag = (int)LocalizationAPI.CurrentLanguage == 1;
			if (PlayerChatManager.Current.m_dissonanceVoiceBroadcast.IsTransmitting)
			{
				list.Add("<color=red>" + Text.Get(966u) + "</color>");
			}
			if (PlayerChatManager.Current.m_speakingPlayers.Count > 0)
			{
				List<string> list2 = new List<string>();
				Enumerator<VoicePlayerState> 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<string> list3 = new List<string>();
				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("<color=orange>[" + InputMapper.GetBindingName((InputAction)56) + "]</color>"),
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)55) + "]</color>"),
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)33) + "]</color>"),
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)63) + "]</color>")
				});
			}
			else
			{
				((TMP_Text)__instance.m_txtHelp).text = Text.Format(1393u, (Object[])(object)new Object[3]
				{
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)56) + "]</color>"),
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)33) + "]</color>"),
					Object.op_Implicit("<color=orange>[" + InputMapper.GetBindingName((InputAction)63) + "]</color>")
				});
			}
		}

		private static PlayerAgent? GetPlayerAgentFromSNetPlayer(SNet_Player sNetPlayer)
		{
			Enumerator<PlayerAgent> 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<string> 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<string> 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 <null>";
			}
			return o.ToString() ?? $"No string representation for <{o.GetType()}>";
		}
	}
}