Decompiled source of ChatX v1.9.2

ChatX.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Nessie.ATLYSS.EasySettings;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ChatX")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.9.2.0")]
[assembly: AssemblyInformationalVersion("1.9.2")]
[assembly: AssemblyProduct("ChatX")]
[assembly: AssemblyTitle("ChatX")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.9.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChatX
{
	[BepInPlugin("ChatX", "ChatX", "1.9.2")]
	public class ChatX : BaseUnityPlugin
	{
		internal sealed class ChatScrollbarState
		{
			public CanvasGroup Scrollbar;

			public GameObject ScrollbarObject;

			public Scrollbar ScrollbarComponent;

			public ScrollRect ScrollRect;

			public GameObject ChatContainer;

			public bool? CachedChatContainerActive;

			public bool? CachedChannelDockActive;
		}

		private static class ChatWindowResizer
		{
			private enum VerticalAnchorMode
			{
				PreserveCenter,
				PreserveBottom,
				PreserveTop,
				ShiftByDelta
			}

			private sealed class RectSnapshot
			{
				public RectTransform Rect;

				public Vector2 OffsetMin;

				public Vector2 OffsetMax;

				public Vector2 AnchoredPosition;

				public bool StretchY;

				public float Height;

				public bool Captured;

				public float PivotY;

				public VerticalAnchorMode AnchorMode;

				public bool AllowResize;

				public float ShiftScale;
			}

			private sealed class TargetGroup
			{
				public WeakReference<ChatBehaviourAssets> Asset;

				public RectSnapshot ChatContainer;

				public RectSnapshot ChatBackdrop;

				public RectSnapshot ChatMask;

				public RectSnapshot ChatGroup;

				public RectSnapshot ChatScroll;

				public RectSnapshot ChatViewport;

				public RectSnapshot ChatScrollbar;

				public RectSnapshot ChatChannelDock;

				public RectSnapshot LogicGroup;

				public RectSnapshot LogicScroll;

				public RectSnapshot LogicViewport;

				public RectSnapshot LogicScrollbar;
			}

			private const float TallMultiplier = 1.75f;

			private const float ChannelDockShiftScale = 0.69f;

			private const float TallBackdropShiftDown = 44f;

			private const float TallBackdropExtraHeight = 55f;

			private const float TallMaskShiftDown = 44f;

			private const float TallMaskExtraHeight = 55f;

			private const float TallScrollShiftDown = 86f;

			private const float TallScrollExtraHeight = -49f;

			private const float TallViewportExtraHeight = -49f;

			private const float TallScrollbarShiftDown = 80f;

			private const float TallScrollbarExtraHeight = -40f;

			private static readonly List<TargetGroup> _targets = new List<TargetGroup>();

			public static void Register(ChatBehaviourAssets assets)
			{
				if ((Object)(object)assets == (Object)null)
				{
					return;
				}
				Cleanup();
				foreach (TargetGroup target2 in _targets)
				{
					if (target2.Asset.TryGetTarget(out var target) && target == assets)
					{
						CaptureSnapshots(target2, force: true);
						Apply(target2);
						return;
					}
				}
				TargetGroup targetGroup = CreateTargetGroup(assets);
				_targets.Add(targetGroup);
				CaptureSnapshots(targetGroup, force: true);
				Apply(targetGroup);
			}

			public static void ApplyAll()
			{
				Cleanup();
				foreach (TargetGroup target2 in _targets)
				{
					if (target2.Asset.TryGetTarget(out var target) && !((Object)(object)target == (Object)null))
					{
						CaptureSnapshots(target2);
						Apply(target2);
					}
				}
			}

			private static TargetGroup CreateTargetGroup(ChatBehaviourAssets assets)
			{
				ScrollRect val = (Object.op_Implicit((Object)(object)assets._chatText) ? ((Component)assets._chatText).GetComponentInParent<ScrollRect>() : null);
				ScrollRect val2 = (Object.op_Implicit((Object)(object)assets._gameLogicText) ? ((Component)assets._gameLogicText).GetComponentInParent<ScrollRect>() : null);
				RectTransform val3 = FindRectTransform(((Component)assets).transform, "_chatbox_textField");
				RectTransform rect = GetRect(assets._chatboxTextGroup);
				RectTransform val4 = (((Object)(object)rect != (Object)null && ((Object)rect).name == "_chatbox_backdrop") ? rect : (((Object)(object)val3 != (Object)null) ? FindRectTransform((Transform)(object)val3, "_chatbox_backdrop") : FindRectTransform(((Component)assets).transform, "_chatbox_backdrop")));
				if ((Object)(object)val4 != (Object)null && (Object)(object)rect != (Object)null && val4 == rect)
				{
					val4 = null;
				}
				RectTransform rect2 = (((Object)(object)val3 != (Object)null) ? FindRectTransform((Transform)(object)val3, "_chatbox_mask") : FindRectTransform(((Component)assets).transform, "_chatbox_mask"));
				Transform root = (Transform)(val4 ?? rect ?? ((object)val3) ?? ((object)((Component)assets).transform));
				RectTransform rect3 = FindRectTransform(root, "_dolly_chatChannelDock");
				RegisterScrollbarTargets(assets, val);
				return new TargetGroup
				{
					Asset = new WeakReference<ChatBehaviourAssets>(assets),
					ChatContainer = CaptureSnapshot(val3, VerticalAnchorMode.PreserveBottom),
					ChatBackdrop = CaptureSnapshot(val4, VerticalAnchorMode.PreserveBottom),
					ChatMask = CaptureSnapshot(rect2, VerticalAnchorMode.PreserveBottom),
					ChatGroup = CaptureSnapshot(rect, VerticalAnchorMode.PreserveBottom),
					ChatScroll = CaptureSnapshot(Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<RectTransform>() : null, VerticalAnchorMode.PreserveBottom),
					ChatViewport = CaptureSnapshot(Object.op_Implicit((Object)(object)val) ? val.viewport : null, VerticalAnchorMode.PreserveBottom),
					ChatScrollbar = CaptureSnapshot((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.verticalScrollbar)) ? ((Component)val.verticalScrollbar).GetComponent<RectTransform>() : null, VerticalAnchorMode.PreserveBottom),
					ChatChannelDock = CaptureSnapshot(rect3, VerticalAnchorMode.ShiftByDelta, allowResize: false, 0.69f),
					LogicGroup = CaptureSnapshot(GetRect(assets._gameLogicGroup)),
					LogicScroll = CaptureSnapshot(Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponent<RectTransform>() : null),
					LogicViewport = CaptureSnapshot(Object.op_Implicit((Object)(object)val2) ? val2.viewport : null),
					LogicScrollbar = CaptureSnapshot((Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.verticalScrollbar)) ? ((Component)val2.verticalScrollbar).GetComponent<RectTransform>() : null)
				};
			}

			private static RectSnapshot CaptureSnapshot(RectTransform rect, VerticalAnchorMode mode = VerticalAnchorMode.PreserveCenter, bool allowResize = true, float shiftScale = 1f)
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)rect == (Object)null)
				{
					return null;
				}
				RectSnapshot obj = new RectSnapshot
				{
					Rect = rect,
					OffsetMin = rect.offsetMin,
					OffsetMax = rect.offsetMax,
					AnchoredPosition = rect.anchoredPosition,
					StretchY = !Mathf.Approximately(rect.anchorMin.y, rect.anchorMax.y)
				};
				Rect rect2 = rect.rect;
				obj.Height = ((Rect)(ref rect2)).height;
				obj.Captured = true;
				obj.PivotY = rect.pivot.y;
				obj.AnchorMode = mode;
				obj.AllowResize = allowResize;
				obj.ShiftScale = shiftScale;
				return obj;
			}

			private static IEnumerable<RectSnapshot> All(TargetGroup group)
			{
				yield return group.ChatContainer;
				yield return group.ChatBackdrop;
				yield return group.ChatMask;
				yield return group.ChatGroup;
				yield return group.ChatScroll;
				yield return group.ChatViewport;
				yield return group.ChatScrollbar;
				yield return group.ChatChannelDock;
				yield return group.LogicGroup;
				yield return group.LogicScroll;
				yield return group.LogicViewport;
				yield return group.LogicScrollbar;
			}

			private static void CaptureSnapshots(TargetGroup group, bool force = false)
			{
				foreach (RectSnapshot item in All(group))
				{
					RefreshSnapshot(item, force);
				}
			}

			private static void Apply(TargetGroup group)
			{
				bool flag = chatTallWindow?.Value ?? false;
				float multiplier = (flag ? 1.75f : 1f);
				float delta = GetDelta(group.ChatViewport, multiplier);
				float delta2 = GetDelta(group.LogicViewport, multiplier);
				RectSnapshot[] array = new RectSnapshot[8] { group.ChatContainer, group.ChatBackdrop, group.ChatMask, group.ChatGroup, group.ChatScroll, group.ChatViewport, group.ChatScrollbar, group.ChatChannelDock };
				foreach (RectSnapshot snapshot in array)
				{
					ApplySnapshot(snapshot, delta);
				}
				RectSnapshot[] array2 = new RectSnapshot[4] { group.LogicGroup, group.LogicScroll, group.LogicViewport, group.LogicScrollbar };
				foreach (RectSnapshot snapshot2 in array2)
				{
					ApplySnapshot(snapshot2, delta2);
				}
				if (flag)
				{
					ApplyTallOffsets(group);
				}
			}

			private static void RefreshSnapshot(RectSnapshot snapshot, bool force)
			{
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: 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_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				if (snapshot == null || (Object)(object)snapshot.Rect == (Object)null)
				{
					return;
				}
				Rect rect;
				if (!snapshot.Captured || force)
				{
					snapshot.OffsetMin = snapshot.Rect.offsetMin;
					snapshot.OffsetMax = snapshot.Rect.offsetMax;
					snapshot.AnchoredPosition = snapshot.Rect.anchoredPosition;
					snapshot.StretchY = !Mathf.Approximately(snapshot.Rect.anchorMin.y, snapshot.Rect.anchorMax.y);
					rect = snapshot.Rect.rect;
					snapshot.Height = ((Rect)(ref rect)).height;
					snapshot.PivotY = snapshot.Rect.pivot.y;
					snapshot.Captured = true;
				}
				else
				{
					if (snapshot.Height <= 0f)
					{
						rect = snapshot.Rect.rect;
						snapshot.Height = ((Rect)(ref rect)).height;
					}
					if ((Object)(object)snapshot.Rect != (Object)null)
					{
						snapshot.PivotY = snapshot.Rect.pivot.y;
					}
				}
			}

			private static void ApplyTallOffsets(TargetGroup group)
			{
				(RectSnapshot, float, float)[] array = new(RectSnapshot, float, float)[5]
				{
					(group.ChatBackdrop, 44f, 55f),
					(group.ChatMask, 44f, 55f),
					(group.ChatScroll, 86f, -49f),
					(group.ChatViewport, 86f, -49f),
					(group.ChatScrollbar, 80f, -40f)
				};
				(RectSnapshot, float, float)[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					var (snapshot, shiftDown, extraHeight) = array2[i];
					AdjustRect(snapshot, shiftDown, extraHeight);
				}
			}

			private static void AdjustRect(RectSnapshot snapshot, float shiftDown, float extraHeight)
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_010b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0143: Unknown result type (might be due to invalid IL or missing references)
				//IL_0153: Unknown result type (might be due to invalid IL or missing references)
				//IL_015f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0192: Unknown result type (might be due to invalid IL or missing references)
				//IL_0197: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)snapshot?.Rect == (Object)null || (Mathf.Abs(shiftDown) < 0.01f && Mathf.Abs(extraHeight) < 0.01f))
				{
					return;
				}
				if (snapshot.StretchY)
				{
					Vector2 offsetMin = snapshot.Rect.offsetMin;
					Vector2 offsetMax = snapshot.Rect.offsetMax;
					if (Mathf.Abs(shiftDown) >= 0.01f)
					{
						offsetMin.y -= shiftDown;
						offsetMax.y -= shiftDown;
					}
					if (Mathf.Abs(extraHeight) >= 0.01f)
					{
						offsetMin.y -= extraHeight;
					}
					snapshot.Rect.offsetMin = offsetMin;
					snapshot.Rect.offsetMax = offsetMax;
					return;
				}
				if (snapshot.AllowResize && Mathf.Abs(extraHeight) >= 0.01f)
				{
					RectTransform rect = snapshot.Rect;
					Rect rect2 = snapshot.Rect.rect;
					rect.SetSizeWithCurrentAnchors((Axis)1, ((Rect)(ref rect2)).height + extraHeight);
				}
				if (Mathf.Abs(shiftDown) >= 0.01f)
				{
					snapshot.Rect.anchoredPosition = new Vector2(snapshot.Rect.anchoredPosition.x, snapshot.Rect.anchoredPosition.y - shiftDown);
				}
				if (!snapshot.AllowResize && Mathf.Abs(extraHeight) >= 0.01f)
				{
					Vector2 offsetMin2 = snapshot.Rect.offsetMin;
					offsetMin2.y -= extraHeight;
					snapshot.Rect.offsetMin = offsetMin2;
				}
			}

			private static float GetDelta(RectSnapshot snapshot, float multiplier)
			{
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				if (snapshot == null || (Object)(object)snapshot.Rect == (Object)null)
				{
					return 0f;
				}
				float height;
				if (!(snapshot.Height > 0f))
				{
					Rect rect = snapshot.Rect.rect;
					height = ((Rect)(ref rect)).height;
				}
				else
				{
					height = snapshot.Height;
				}
				float num = height;
				if (num <= 0f)
				{
					return 0f;
				}
				return num * multiplier - num;
			}

			private static void ApplySnapshot(RectSnapshot snapshot, float delta)
			{
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_017e: Unknown result type (might be due to invalid IL or missing references)
				//IL_018c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0258: Unknown result type (might be due to invalid IL or missing references)
				if (snapshot == null || (Object)(object)snapshot.Rect == (Object)null)
				{
					return;
				}
				if (Mathf.Abs(delta) < 0.01f)
				{
					if (snapshot.StretchY)
					{
						snapshot.Rect.offsetMin = snapshot.OffsetMin;
						snapshot.Rect.offsetMax = snapshot.OffsetMax;
						return;
					}
					if (snapshot.AllowResize)
					{
						snapshot.Rect.SetSizeWithCurrentAnchors((Axis)1, snapshot.Height);
					}
					snapshot.Rect.anchoredPosition = snapshot.AnchoredPosition;
				}
				else if (snapshot.StretchY)
				{
					Vector2 offsetMin = snapshot.OffsetMin;
					Vector2 offsetMax = snapshot.OffsetMax;
					switch (snapshot.AnchorMode)
					{
					case VerticalAnchorMode.PreserveBottom:
						offsetMax.y = snapshot.OffsetMax.y + delta;
						break;
					case VerticalAnchorMode.PreserveTop:
						offsetMin.y = snapshot.OffsetMin.y - delta;
						break;
					case VerticalAnchorMode.ShiftByDelta:
						offsetMin.y = snapshot.OffsetMin.y + delta * snapshot.ShiftScale;
						offsetMax.y = snapshot.OffsetMax.y + delta * snapshot.ShiftScale;
						break;
					default:
						offsetMin.y = snapshot.OffsetMin.y - delta * 0.5f;
						offsetMax.y = snapshot.OffsetMax.y + delta * 0.5f;
						break;
					}
					snapshot.Rect.offsetMin = offsetMin;
					snapshot.Rect.offsetMax = offsetMax;
				}
				else
				{
					if (snapshot.AllowResize)
					{
						snapshot.Rect.SetSizeWithCurrentAnchors((Axis)1, snapshot.Height + delta);
					}
					float num = snapshot.AnchoredPosition.y;
					switch (snapshot.AnchorMode)
					{
					case VerticalAnchorMode.PreserveBottom:
						num = snapshot.AnchoredPosition.y + delta * snapshot.PivotY;
						break;
					case VerticalAnchorMode.PreserveTop:
						num = snapshot.AnchoredPosition.y - delta * (1f - snapshot.PivotY);
						break;
					case VerticalAnchorMode.ShiftByDelta:
						num = snapshot.AnchoredPosition.y + delta * snapshot.ShiftScale;
						break;
					default:
						num = snapshot.AnchoredPosition.y;
						break;
					case VerticalAnchorMode.PreserveCenter:
						break;
					}
					snapshot.Rect.anchoredPosition = new Vector2(snapshot.AnchoredPosition.x, num);
				}
			}

			private static RectTransform GetRect(CanvasGroup group)
			{
				return (group != null) ? ((Component)group).GetComponent<RectTransform>() : null;
			}

			private static RectTransform FindRectTransform(Transform root, string name)
			{
				if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name))
				{
					return null;
				}
				Queue<Transform> queue = new Queue<Transform>();
				queue.Enqueue(root);
				while (queue.Count > 0)
				{
					Transform val = queue.Dequeue();
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (((Object)val).name == name)
					{
						RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null);
						if (val2 != null)
						{
							return val2;
						}
					}
					for (int i = 0; i < val.childCount; i++)
					{
						queue.Enqueue(val.GetChild(i));
					}
				}
				return null;
			}

			private static void Cleanup()
			{
				_targets.RemoveAll((TargetGroup t) => !t.Asset.TryGetTarget(out var target) || (Object)(object)target == (Object)null);
			}
		}

		public enum MentionClip
		{
			LexiconBell,
			UIClick01,
			UIHover,
			Lockout
		}

		[HarmonyPatch(typeof(ChatBehaviour), "New_ChatMessage")]
		private static class ChatBehaviour_New_ChatMessage_PrefixAndBlock
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static bool Prefix(ref string __0)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				if (blockChat.Value)
				{
					return false;
				}
				ConfigEntry<bool> asteriskItalic = ChatX.asteriskItalic;
				if (asteriskItalic != null && asteriskItalic.Value)
				{
					__0 = ApplyAsteriskFormatting(__0);
				}
				if (chatPrefix.Value && !LooksPrefixed(__0) && TryExtractChatChannel(__0, out var channel))
				{
					string text = BuildMessagePrefix(channel);
					if (!string.IsNullOrEmpty(text))
					{
						__0 = InsertPrefix(__0, text);
					}
				}
				ApplyMentionEffects(ref __0);
				return true;
			}

			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix(ChatBehaviour __instance)
			{
				if (Object.op_Implicit((Object)(object)__instance))
				{
					ApplyOpacityCap(__instance._chatAssets);
				}
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "Init_GameLogicMessage")]
		private static class ChatBehaviour_Init_GameLogicMessage_Block
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static bool Prefix()
			{
				return !blockGameFeed.Value;
			}
		}

		[HarmonyPatch(typeof(PatternInstanceManager), "On_DungeonKeyChange")]
		private static class PatternInstanceManager_OnDungeonKeyChange_Reroute
		{
			private static readonly MethodInfo NewChatMessage = AccessTools.Method(typeof(ChatBehaviour), "New_ChatMessage", (Type[])null, (Type[])null);

			private static readonly MethodInfo PushGameFeed = AccessTools.Method(typeof(ChatX), "PushGameFeedMessage", (Type[])null, (Type[])null);

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				foreach (CodeInstruction code in instructions)
				{
					if (CodeInstructionExtensions.Calls(code, NewChatMessage))
					{
						code.opcode = OpCodes.Call;
						code.operand = PushGameFeed;
					}
					yield return code;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerQuesting), "Accept_Quest")]
		private static class PlayerQuesting_AcceptQuest_RerouteObjectivePickup
		{
			private static readonly MethodInfo NewChatMessage = AccessTools.Method(typeof(ChatBehaviour), "New_ChatMessage", (Type[])null, (Type[])null);

			private static readonly MethodInfo PushGameFeed = AccessTools.Method(typeof(ChatX), "PushGameFeedMessage", (Type[])null, (Type[])null);

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				foreach (CodeInstruction code in instructions)
				{
					if (CodeInstructionExtensions.Calls(code, NewChatMessage))
					{
						code.opcode = OpCodes.Call;
						code.operand = PushGameFeed;
					}
					yield return code;
				}
			}
		}

		[HarmonyPatch(typeof(WorldPortalWaypoint), "OnTriggerEnter")]
		private static class WorldPortalWaypoint_OnTriggerEnter_RerouteAttuneChat
		{
			private static readonly MethodInfo NewChatMessage = AccessTools.Method(typeof(ChatBehaviour), "New_ChatMessage", (Type[])null, (Type[])null);

			private static readonly MethodInfo RouteAttune = AccessTools.Method(typeof(ChatX), "RouteWaypointAttuneMessage", (Type[])null, (Type[])null);

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				foreach (CodeInstruction code in instructions)
				{
					if (CodeInstructionExtensions.Calls(code, NewChatMessage))
					{
						code.opcode = OpCodes.Call;
						code.operand = RouteAttune;
					}
					yield return code;
				}
			}
		}

		[HarmonyPatch(typeof(NetTrigger))]
		private static class NetTrigger_OnTriggerStay_RerouteDungeonKeyMessage
		{
			private static readonly MethodInfo TargetReceive = AccessTools.Method(typeof(ChatBehaviour), "Target_RecieveMessage", (Type[])null, (Type[])null);

			private static readonly MethodInfo RouteTarget = AccessTools.Method(typeof(ChatX), "PushTargetedGameFeedMessage", (Type[])null, (Type[])null);

			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(NetTrigger), "OnTriggerStay", (Type[])null, (Type[])null);
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				foreach (CodeInstruction code in instructions)
				{
					if (CodeInstructionExtensions.Calls(code, TargetReceive))
					{
						code.opcode = OpCodes.Call;
						code.operand = RouteTarget;
					}
					yield return code;
				}
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "UserCode_Target_RecieveMessage__String")]
		private static class ChatBehaviour_TargetReceiveMessage_RerouteDungeonKey
		{
			private const string DoorLockedMessage = "The door is locked. It requires a Dungeon Key.";

			private static bool Prefix(ChatBehaviour __instance, string _message)
			{
				if (!ShouldPushGameFeed())
				{
					return true;
				}
				if (!Object.op_Implicit((Object)(object)__instance) || string.IsNullOrEmpty(_message))
				{
					return true;
				}
				if (!string.Equals(_message, "The door is locked. It requires a Dungeon Key.", StringComparison.Ordinal))
				{
					return true;
				}
				PushGameFeedMessage(__instance, _message);
				return false;
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "Display_Chat")]
		private static class ChatBehaviourDisplayPatch
		{
			private static void Postfix(ChatBehaviour __instance)
			{
				if (Object.op_Implicit((Object)(object)__instance))
				{
					ApplyOpacityCap(__instance._chatAssets);
				}
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "Client_HandleChatboxUpdate")]
		private static class ChatBehaviour_CapOpacity_Postfix
		{
			[HarmonyPostfix]
			private static void Postfix(ChatBehaviour __instance)
			{
				if (Object.op_Implicit((Object)(object)__instance))
				{
					ApplyOpacityCap(__instance._chatAssets);
				}
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "<Client_HandleChatboxUpdate>g__Handle_ChatBoxControls|54_1")]
		private static class ChatBehaviour_HandleChatControls_PreOpen
		{
			private static void Prefix(ChatBehaviour __instance)
			{
				if (Object.op_Implicit((Object)(object)__instance) && ChatBehaviour._current == __instance && (Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271)))
				{
					ChatBehaviourAssets chatAssets = __instance._chatAssets;
					InputField val = (Object.op_Implicit((Object)(object)chatAssets) ? chatAssets._chatInput : null);
					bool flag = Object.op_Implicit((Object)(object)val) && val.isFocused;
					CanvasGroup val2 = (Object.op_Implicit((Object)(object)chatAssets) ? chatAssets._generalCanvasGroup : null);
					bool flag2 = Object.op_Implicit((Object)(object)val2) && val2.blocksRaycasts && val2.interactable;
					bool flag3 = Object.op_Implicit((Object)(object)val2) && val2.alpha <= 0.001f && !flag2;
					if (!(__instance._focusedInChat || flag) && flag3)
					{
						pendingRestore = true;
						ResetChatFocus();
					}
				}
			}
		}

		private static class ChatInputCharacterLimiter
		{
			private static InputField _trackedInput;

			private static Text _charCounter;

			private static RectTransform _counterRect;

			private const float CounterOffsetX = 405f;

			private const float CounterVerticalSpacing = 6f;

			private const int CounterFontSize = 20;

			private static readonly Color CounterColorStart = Color32.op_Implicit(new Color32((byte)90, byte.MaxValue, (byte)90, byte.MaxValue));

			private static readonly Color CounterColorEnd = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)85, (byte)85, byte.MaxValue));

			private static InputField TryGetInputField()
			{
				return Player._mainPlayer?._chatBehaviour?._chatAssets?._chatInput ?? ChatBehaviourAssets._current?._chatInput;
			}

			public static void ApplyCharacterLimit()
			{
				InputField val = TryGetInputField();
				if ((Object)(object)val == (Object)null)
				{
					ResetCounter();
					ChatWindowResizer.ApplyAll();
					return;
				}
				EnsureCounter(val);
				int maxMessageLength = GetMaxMessageLength();
				if (val.characterLimit != maxMessageLength)
				{
					val.characterLimit = maxMessageLength;
				}
				UpdateCounter(val);
				ChatWindowResizer.ApplyAll();
			}

			public static IEnumerator ApplyWhenReady(ChatBehaviour chat)
			{
				float end = Time.time + 3f;
				while (Time.time < end)
				{
					InputField input = chat?._chatAssets?._chatInput ?? ChatBehaviourAssets._current?._chatInput;
					if ((Object)(object)input != (Object)null)
					{
						EnsureCounter(input);
						int desired = GetMaxMessageLength();
						if (input.characterLimit != desired)
						{
							input.characterLimit = desired;
						}
						UpdateCounter(input);
						ChatWindowResizer.ApplyAll();
						yield break;
					}
					yield return null;
				}
				ApplyCharacterLimit();
			}

			private static void EnsureCounter(InputField input)
			{
				//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
				//IL_0230: Unknown result type (might be due to invalid IL or missing references)
				//IL_0235: Unknown result type (might be due to invalid IL or missing references)
				//IL_0249: Expected O, but got Unknown
				//IL_0219: Unknown result type (might be due to invalid IL or missing references)
				//IL_0295: Unknown result type (might be due to invalid IL or missing references)
				//IL_041a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0434: Unknown result type (might be due to invalid IL or missing references)
				//IL_044e: Unknown result type (might be due to invalid IL or missing references)
				//IL_046b: Unknown result type (might be due to invalid IL or missing references)
				//IL_048d: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)input == (Object)null)
				{
					return;
				}
				if ((Object)(object)_trackedInput != (Object)(object)input)
				{
					if ((Object)(object)_trackedInput != (Object)null)
					{
						((UnityEvent<string>)(object)_trackedInput.onValueChanged).RemoveListener((UnityAction<string>)OnInputValueChanged);
						((UnityEvent<string>)(object)_trackedInput.onEndEdit).RemoveListener((UnityAction<string>)OnInputEndEdit);
					}
					_trackedInput = input;
					((UnityEvent<string>)(object)_trackedInput.onValueChanged).AddListener((UnityAction<string>)OnInputValueChanged);
					((UnityEvent<string>)(object)_trackedInput.onEndEdit).AddListener((UnityAction<string>)OnInputEndEdit);
				}
				RectTransform component = ((Component)input).GetComponent<RectTransform>();
				RectTransform val = null;
				if ((Object)(object)component != (Object)null)
				{
					Transform parent = ((Transform)component).parent;
					val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
					if ((Object)(object)val != (Object)null && ((Object)(object)((Component)val).GetComponent<Mask>() != (Object)null || (Object)(object)((Component)val).GetComponent<RectMask2D>() != (Object)null))
					{
						Transform parent2 = ((Transform)val).parent;
						RectTransform val2 = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null);
						if ((Object)(object)val2 != (Object)null)
						{
							val = val2;
						}
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					val = (RectTransform)(((object)component) ?? ((object)/*isinst with value type is only supported in some contexts*/));
				}
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				if ((Object)(object)_charCounter == (Object)null || ((object)_charCounter).Equals((object?)null))
				{
					_charCounter = null;
					_counterRect = null;
					Transform obj = ((Transform)val).Find("ChatX_CharCounter");
					Text val3 = ((obj != null) ? ((Component)obj).GetComponent<Text>() : null);
					if ((Object)(object)val3 != (Object)null && !((object)val3).Equals((object?)null))
					{
						_charCounter = val3;
						_counterRect = ((Graphic)val3).rectTransform;
						val3.fontSize = 20;
						((Graphic)val3).color = CounterColorStart;
					}
					else
					{
						GameObject val4 = new GameObject("ChatX_CharCounter")
						{
							layer = ((Component)input).gameObject.layer
						};
						_counterRect = val4.AddComponent<RectTransform>();
						((Transform)_counterRect).SetParent((Transform)(object)val, false);
						Text val5 = val4.AddComponent<Text>();
						Text textComponent = input.textComponent;
						if ((Object)(object)textComponent != (Object)null)
						{
							val5.font = textComponent.font;
							val5.fontStyle = textComponent.fontStyle;
							((Graphic)val5).material = ((Graphic)textComponent).material;
							val5.lineSpacing = textComponent.lineSpacing;
						}
						else
						{
							val5.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
							val5.lineSpacing = 1f;
						}
						val5.fontSize = 20;
						((Graphic)val5).color = CounterColorStart;
						val5.alignment = (TextAnchor)5;
						val5.horizontalOverflow = (HorizontalWrapMode)1;
						val5.verticalOverflow = (VerticalWrapMode)0;
						((Graphic)val5).raycastTarget = false;
						_charCounter = val5;
					}
				}
				else if ((Object)(object)_counterRect == (Object)null && (Object)(object)_charCounter != (Object)null && !((object)_charCounter).Equals((object?)null))
				{
					_counterRect = ((Graphic)_charCounter).rectTransform;
				}
				if (!((Object)(object)_counterRect == (Object)null))
				{
					((Transform)_counterRect).SetParent((Transform)(object)val, false);
					((Transform)_counterRect).SetAsLastSibling();
					float num = 0f;
					float num2 = 0f;
					if ((Object)(object)component != (Object)null)
					{
						Rect rect = component.rect;
						num = Mathf.Abs(((Rect)(ref rect)).height);
						rect = component.rect;
						num2 = Mathf.Abs(((Rect)(ref rect)).width);
					}
					if (num <= 1f)
					{
						num = 24f;
					}
					if (num2 <= 1f)
					{
						num2 = 240f;
					}
					_counterRect.anchorMin = new Vector2(1f, 0f);
					_counterRect.anchorMax = new Vector2(1f, 0f);
					_counterRect.pivot = new Vector2(1f, 1f);
					_counterRect.anchoredPosition = new Vector2(405f, 0f - (num + 6f));
					_counterRect.sizeDelta = new Vector2(Mathf.Max(num2 * 0.35f, 110f), num);
					if ((Object)(object)_charCounter != (Object)null && !((object)_charCounter).Equals((object?)null))
					{
						_charCounter.text = string.Empty;
						((Component)_charCounter).gameObject.SetActive(false);
					}
				}
			}

			private static bool IsLive(Behaviour b)
			{
				return Object.op_Implicit((Object)(object)b) && b.isActiveAndEnabled && ((Component)b).gameObject.activeInHierarchy;
			}

			private static void UpdateCounter(InputField input)
			{
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)input))
				{
					return;
				}
				EnsureCounter(input);
				if (Object.op_Implicit((Object)(object)_charCounter))
				{
					bool flag = !string.IsNullOrEmpty(input.text) && IsLive((Behaviour)(object)input);
					((Component)_charCounter).gameObject.SetActive(flag);
					if (!flag)
					{
						_charCounter.text = string.Empty;
						return;
					}
					int maxMessageLength = GetMaxMessageLength();
					int num = Mathf.Clamp(input.text?.Length ?? 0, 0, maxMessageLength);
					float num2 = ((maxMessageLength > 0) ? ((float)num / (float)maxMessageLength) : 0f);
					((Graphic)_charCounter).color = Color.Lerp(CounterColorStart, CounterColorEnd, num2);
					_charCounter.text = (maxMessageLength - num).ToString();
				}
			}

			private static void OnInputValueChanged(string _)
			{
				if ((Object)(object)_trackedInput != (Object)null)
				{
					UpdateCounter(_trackedInput);
				}
			}

			private static void OnInputEndEdit(string _)
			{
				if ((Object)(object)_trackedInput != (Object)null)
				{
					UpdateCounter(_trackedInput);
				}
				else if ((Object)(object)_charCounter != (Object)null && !((object)_charCounter).Equals((object?)null))
				{
					((Component)_charCounter).gameObject.SetActive(false);
				}
			}

			public static void ResetCounter()
			{
				if ((Object)(object)_trackedInput != (Object)null)
				{
					((UnityEvent<string>)(object)_trackedInput.onValueChanged).RemoveListener((UnityAction<string>)OnInputValueChanged);
					((UnityEvent<string>)(object)_trackedInput.onEndEdit).RemoveListener((UnityAction<string>)OnInputEndEdit);
					_trackedInput = null;
				}
				if ((Object)(object)_charCounter != (Object)null && !((object)_charCounter).Equals((object?)null))
				{
					_charCounter.text = string.Empty;
					((Component)_charCounter).gameObject.SetActive(false);
				}
				_charCounter = null;
				_counterRect = null;
			}
		}

		[HarmonyPatch(typeof(SettingsManager), "Load_SettingsData")]
		private static class ChatInputLimit_OnLoad
		{
			private static void Postfix()
			{
				ChatInputCharacterLimiter.ApplyCharacterLimit();
				ChatWindowResizer.ApplyAll();
			}
		}

		[HarmonyPatch(typeof(SettingsManager), "Save_SettingsData")]
		private static class ChatInputLimit_OnSave
		{
			private static void Postfix()
			{
				ChatInputCharacterLimiter.ApplyCharacterLimit();
				ChatWindowResizer.ApplyAll();
			}
		}

		[HarmonyPatch(typeof(ChatBehaviourAssets), "Start")]
		private static class ChatInputLimit_OnChatAssetsStart
		{
			private static void Postfix(ChatBehaviourAssets __instance)
			{
				ChatWindowResizer.Register(__instance);
				if ((Object)(object)__instance?._chatInput != (Object)null)
				{
					__instance._chatInput.characterLimit = GetMaxMessageLength();
				}
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "OnStartAuthority")]
		private static class ChatBehaviour_OnStartAuthority_Patch
		{
			private static void Postfix(ChatBehaviour __instance)
			{
				((MonoBehaviour)__instance).StartCoroutine(ChatInputCharacterLimiter.ApplyWhenReady(__instance));
			}
		}

		[HarmonyPatch(typeof(InGameUI), "RefreshParams_LocatePlayer")]
		private static class ApplyLimit_AfterPlayerLocated
		{
			private static void Postfix()
			{
				ChatInputCharacterLimiter.ApplyCharacterLimit();
				ChatWindowResizer.ApplyAll();
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "Send_ChatMessage")]
		private static class ChatBehaviour_SendMessage_Transpiler
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instrs)
			{
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Expected O, but got Unknown
				List<CodeInstruction> list = new List<CodeInstruction>(instrs);
				MethodInfo methodInfo = AccessTools.Method(typeof(ChatX), "GetMaxMessageLength", (Type[])null, (Type[])null);
				for (int i = 0; i < list.Count; i++)
				{
					if (list[i].opcode == OpCodes.Ldc_I4_S && (sbyte)list[i].operand == 125)
					{
						list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
					}
				}
				return list.AsEnumerable();
			}
		}

		[HarmonyPatch(typeof(ChatBehaviour), "UserCode_Cmd_SendChatMessage__String__ChatChannel")]
		private static class ChatBehaviour_CmdSendMessage_Transpiler
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instrs)
			{
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Expected O, but got Unknown
				List<CodeInstruction> list = new List<CodeInstruction>(instrs);
				MethodInfo methodInfo = AccessTools.Method(typeof(ChatX), "GetMaxMessageLength", (Type[])null, (Type[])null);
				for (int i = 0; i < list.Count; i++)
				{
					if ((list[i].opcode == OpCodes.Ldc_I4_S && (sbyte)list[i].operand == 125) || (list[i].opcode == OpCodes.Ldc_I4 && (int)list[i].operand == 125))
					{
						list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
					}
				}
				return list.AsEnumerable();
			}
		}

		internal static readonly ConditionalWeakTable<ChatBehaviourAssets, ChatScrollbarState> _scrollbarStates = new ConditionalWeakTable<ChatBehaviourAssets, ChatScrollbarState>();

		private static bool _dialogSuppressed;

		private static bool pendingRestore;

		private static bool _chatWasHidden;

		private static readonly FieldInfo ChatInputBufferField = typeof(ChatBehaviour).GetField("_inputBuffer", BindingFlags.Instance | BindingFlags.NonPublic);

		internal static ConfigEntry<float> chatOpacity;

		internal static ConfigEntry<bool> blockChat;

		internal static ConfigEntry<bool> blockGameFeed;

		internal static ConfigEntry<bool> chatPrefix;

		internal static ConfigEntry<bool> chatTimestamp;

		internal static ConfigEntry<bool> timestampFormat;

		internal static ConfigEntry<bool> asteriskItalic;

		internal static ConfigEntry<bool> chatTallWindow;

		internal static ConfigEntry<KeyCode> clearChatKey;

		internal static ConfigEntry<KeyCode> toggleChatVisibilityKey;

		internal static ConfigEntry<bool> messageLimit;

		internal static ConfigEntry<bool> pushGameMessage;

		internal static ConfigEntry<bool> mentionUnderline;

		internal static ConfigEntry<bool> mentionPing;

		internal static ConfigEntry<string> mentionPingClip;

		internal static ConfigEntry<MentionClip> mentionPingClipEnum;

		internal static ConfigEntry<float> mentionPingVolume;

		internal static ConfigEntry<bool> transparentScrollbar;

		internal static AudioClip _mentionAudioClip;

		internal static ManualLogSource Log;

		private static bool _chatHidden;

		private static readonly string[] ClipNames = new string[4] { "_lexiconBell", "_uiClick01", "_uiHover", "lockout" };

		private static float _nextMentionPingAllowedAt;

		private static ChatScrollbarState GetScrollbarState(ChatBehaviourAssets assets)
		{
			if (!Object.op_Implicit((Object)(object)assets))
			{
				return null;
			}
			if (!_scrollbarStates.TryGetValue(assets, out var value))
			{
				value = new ChatScrollbarState();
				_scrollbarStates.Add(assets, value);
			}
			if ((Object)(object)value.ScrollRect == (Object)null)
			{
				value.ScrollRect = TryGetChatScrollRect(assets);
			}
			if ((Object)(object)value.Scrollbar == (Object)null || (Object)(object)value.ScrollbarObject == (Object)null || (Object)(object)value.ScrollbarComponent == (Object)null)
			{
				CanvasGroup val = TryGetScrollbarGroup(assets, value);
				if ((Object)(object)val != (Object)null)
				{
					value.Scrollbar = val;
				}
			}
			if ((Object)(object)value.ChatContainer == (Object)null)
			{
				value.ChatContainer = ResolveChatContainer(assets);
			}
			return value;
		}

		private static void RegisterScrollbarTargets(ChatBehaviourAssets assets, ScrollRect chatScrollRect)
		{
			if (!Object.op_Implicit((Object)(object)assets))
			{
				return;
			}
			ChatScrollbarState orCreateValue = _scrollbarStates.GetOrCreateValue(assets);
			if (!((Object)(object)chatScrollRect != (Object)null))
			{
				return;
			}
			orCreateValue.ScrollRect = chatScrollRect;
			Scrollbar verticalScrollbar = chatScrollRect.verticalScrollbar;
			if ((Object)(object)verticalScrollbar != (Object)null)
			{
				orCreateValue.ScrollbarComponent = verticalScrollbar;
				orCreateValue.ScrollbarObject = ((Component)verticalScrollbar).gameObject;
				if ((Object)(object)orCreateValue.Scrollbar == (Object)null)
				{
					orCreateValue.Scrollbar = ((Component)verticalScrollbar).GetComponent<CanvasGroup>() ?? orCreateValue.ScrollbarObject.AddComponent<CanvasGroup>();
				}
			}
		}

		private static ScrollRect TryGetChatScrollRect(ChatBehaviourAssets assets)
		{
			TextMeshProUGUI val = assets?._chatText;
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			try
			{
				return ((Component)val).GetComponentInParent<ScrollRect>();
			}
			catch
			{
				return null;
			}
		}

		private static GameObject ResolveChatContainer(ChatBehaviourAssets assets)
		{
			if (!Object.op_Implicit((Object)(object)assets))
			{
				return null;
			}
			if ((Object)(object)assets._chatboxTextGroup != (Object)null)
			{
				return ((Component)assets._chatboxTextGroup).gameObject;
			}
			GameObject val = FindChildObject(((Component)assets).transform, "_chatbox_textField");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			TextMeshProUGUI chatText = assets._chatText;
			return ((Object)(object)chatText != (Object)null) ? ((Component)chatText).gameObject : null;
		}

		private static GameObject FindChildObject(Transform root, string name)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Queue<Transform> queue = new Queue<Transform>();
			queue.Enqueue(root);
			while (queue.Count > 0)
			{
				Transform val = queue.Dequeue();
				if (!((Object)(object)val == (Object)null))
				{
					if (((Object)val).name == name)
					{
						return ((Component)val).gameObject;
					}
					for (int i = 0; i < val.childCount; i++)
					{
						queue.Enqueue(val.GetChild(i));
					}
				}
			}
			return null;
		}

		private static bool ShouldHoldChatForExternalUI()
		{
			DialogManager current = DialogManager._current;
			if (Object.op_Implicit((Object)(object)current) && current._isDialogEnabled)
			{
				_dialogSuppressed = true;
				return true;
			}
			if (_dialogSuppressed)
			{
				_dialogSuppressed = false;
				pendingRestore = true;
			}
			return false;
		}

		private static void ResetChatFocus()
		{
			ChatBehaviour current = ChatBehaviour._current;
			if (!Object.op_Implicit((Object)(object)current))
			{
				return;
			}
			current._focusedInChat = false;
			InputField val = (Object.op_Implicit((Object)(object)current._chatAssets) ? current._chatAssets._chatInput : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				val.DeactivateInputField();
			}
			if (ChatInputBufferField != null)
			{
				try
				{
					ChatInputBufferField.SetValue(current, 0f);
				}
				catch
				{
				}
			}
			current.Display_Chat(true);
			current._textFadeTimer = Mathf.Max(current._textFadeTimer, 1f);
			current._logicFadeTimer = Mathf.Max(current._logicFadeTimer, 1f);
			if (Object.op_Implicit((Object)(object)current._chatAssets))
			{
				CanvasGroup generalCanvasGroup = current._chatAssets._generalCanvasGroup;
				if (Object.op_Implicit((Object)(object)generalCanvasGroup))
				{
					generalCanvasGroup.alpha = 1f;
					generalCanvasGroup.blocksRaycasts = true;
					generalCanvasGroup.interactable = true;
				}
			}
		}

		private static void SyncChatVisibility(ChatBehaviourAssets assets, ChatScrollbarState state, bool chatHidden, bool allFaded)
		{
			if (state == null)
			{
				return;
			}
			GameObject val = state.ChatContainer;
			if ((Object)(object)val == (Object)null)
			{
				val = (state.ChatContainer = ResolveChatContainer(assets));
			}
			if ((Object)(object)val != (Object)null)
			{
				if (chatHidden)
				{
					if (!state.CachedChatContainerActive.HasValue)
					{
						state.CachedChatContainerActive = val.activeSelf;
					}
					if (val.activeSelf)
					{
						val.SetActive(false);
					}
				}
				else if (state.CachedChatContainerActive.HasValue)
				{
					bool value = state.CachedChatContainerActive.Value;
					state.CachedChatContainerActive = null;
					if (val.activeSelf != value)
					{
						val.SetActive(value);
					}
				}
			}
			GameObject val2 = assets?._chatChannelDockObject;
			if ((Object)(object)val2 != (Object)null)
			{
				if (chatHidden)
				{
					if (!state.CachedChannelDockActive.HasValue)
					{
						state.CachedChannelDockActive = val2.activeSelf;
					}
					if (val2.activeSelf)
					{
						val2.SetActive(false);
					}
				}
				else if (state.CachedChannelDockActive.HasValue)
				{
					bool value2 = state.CachedChannelDockActive.Value;
					state.CachedChannelDockActive = null;
					if (val2.activeSelf != value2)
					{
						val2.SetActive(value2);
					}
				}
			}
			CanvasGroup val3 = assets?._generalCanvasGroup;
			if ((Object)(object)val3 != (Object)null)
			{
				bool interactable = (val3.blocksRaycasts = !allFaded);
				val3.interactable = interactable;
			}
			if ((Object)(object)state.ScrollRect != (Object)null)
			{
				bool enabled = !allFaded;
				((Behaviour)state.ScrollRect).enabled = enabled;
			}
		}

		private static CanvasGroup TryGetScrollbarGroup(ChatBehaviourAssets assets, ChatScrollbarState state)
		{
			if (state == null)
			{
				return null;
			}
			ScrollRect val = (state.ScrollRect = state.ScrollRect ?? TryGetChatScrollRect(assets));
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Scrollbar verticalScrollbar = val.verticalScrollbar;
			if ((Object)(object)verticalScrollbar == (Object)null)
			{
				return null;
			}
			state.ScrollbarComponent = verticalScrollbar;
			state.ScrollbarObject = ((Component)verticalScrollbar).gameObject;
			CanvasGroup val2 = ((Component)verticalScrollbar).GetComponent<CanvasGroup>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = state.ScrollbarObject.AddComponent<CanvasGroup>();
			}
			return val2;
		}

		private static void UpdateScrollbarState(ChatBehaviourAssets assets, ChatScrollbarState state, bool chatHidden, float visualAlpha, bool transparentOverride, bool allFaded)
		{
			if (state == null)
			{
				return;
			}
			if ((Object)(object)state.ScrollRect == (Object)null)
			{
				state.ScrollRect = TryGetChatScrollRect(assets);
			}
			if ((Object)(object)state.ScrollRect == (Object)null)
			{
				return;
			}
			if ((Object)(object)state.Scrollbar == (Object)null || (Object)(object)state.ScrollbarObject == (Object)null || (Object)(object)state.ScrollbarComponent == (Object)null)
			{
				state.Scrollbar = TryGetScrollbarGroup(assets, state);
				if ((Object)(object)state.ScrollRect == (Object)null)
				{
					return;
				}
			}
			bool flag = !chatHidden && !allFaded && ShouldShowScrollbar(state);
			float num = (transparentOverride ? 0f : visualAlpha);
			if ((Object)(object)state.Scrollbar != (Object)null)
			{
				state.Scrollbar.alpha = (flag ? num : 0f);
				state.Scrollbar.blocksRaycasts = flag;
				state.Scrollbar.interactable = flag;
			}
			if ((Object)(object)state.ScrollbarComponent != (Object)null)
			{
				((Selectable)state.ScrollbarComponent).interactable = flag;
			}
		}

		private static void UpdateEffectiveAlpha(ref float alpha, ref bool sawNonZero, float candidate)
		{
			if (candidate > 0.001f)
			{
				sawNonZero = true;
				alpha = Mathf.Min(alpha, candidate);
			}
		}

		private static bool ShouldShowScrollbar(ChatScrollbarState state)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			ScrollRect val = state?.ScrollRect;
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			RectTransform content = val.content;
			RectTransform val2 = (((Object)(object)val.viewport != (Object)null) ? val.viewport : ((Component)val).GetComponent<RectTransform>());
			if ((Object)(object)content == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return false;
			}
			Rect rect = content.rect;
			float num = ((Rect)(ref rect)).height;
			rect = val2.rect;
			float height = ((Rect)(ref rect)).height;
			if (num <= 0f)
			{
				num = content.sizeDelta.y;
			}
			return num - height > 1f;
		}

		internal static void ApplyOpacityCap(ChatBehaviourAssets assets, bool force = false)
		{
			if (!Object.op_Implicit((Object)(object)assets))
			{
				return;
			}
			bool flag = ShouldHoldChatForExternalUI();
			bool flag2 = _chatHidden || flag;
			bool chatWasHidden = _chatWasHidden;
			_chatWasHidden = flag2;
			if (chatWasHidden && !flag2)
			{
				pendingRestore = true;
			}
			bool flag3 = pendingRestore && !flag2;
			if (flag3)
			{
				pendingRestore = false;
			}
			float num = (flag2 ? 0f : Mathf.Clamp01(chatOpacity?.Value ?? 1f));
			float alpha = num;
			bool sawNonZero = false;
			CanvasGroup generalCanvasGroup = assets._generalCanvasGroup;
			float num2 = (flag2 ? 0f : 1f);
			CanvasGroup[] chatTextGroups = assets._chatTextGroups;
			if (chatTextGroups != null && !flag2)
			{
				foreach (CanvasGroup val in chatTextGroups)
				{
					if (Object.op_Implicit((Object)(object)val))
					{
						float alpha2 = val.alpha;
						float num3 = (flag3 ? num : Mathf.Min(alpha2, num));
						if (force || flag3 || !Mathf.Approximately(alpha2, num3))
						{
							val.alpha = num3;
						}
						UpdateEffectiveAlpha(ref alpha, ref sawNonZero, val.alpha);
					}
				}
			}
			if (Object.op_Implicit((Object)(object)assets._chatboxTextGroup) && !flag2)
			{
				float alpha3 = assets._chatboxTextGroup.alpha;
				float num4 = (flag3 ? num : Mathf.Min(alpha3, num));
				if (force || flag3 || !Mathf.Approximately(alpha3, num4))
				{
					assets._chatboxTextGroup.alpha = num4;
				}
				UpdateEffectiveAlpha(ref alpha, ref sawNonZero, assets._chatboxTextGroup.alpha);
			}
			if (Object.op_Implicit((Object)(object)assets._gameLogicGroup) && !flag2)
			{
				float alpha4 = assets._gameLogicGroup.alpha;
				float num5 = (flag3 ? num : Mathf.Min(alpha4, num));
				if (force || flag3 || !Mathf.Approximately(alpha4, num5))
				{
					assets._gameLogicGroup.alpha = num5;
				}
				UpdateEffectiveAlpha(ref alpha, ref sawNonZero, assets._gameLogicGroup.alpha);
			}
			bool flag4 = flag2 || !sawNonZero;
			if (flag4)
			{
				alpha = 0f;
			}
			alpha = Mathf.Clamp01(alpha);
			bool transparentOverride = transparentScrollbar?.Value ?? false;
			if (Object.op_Implicit((Object)(object)generalCanvasGroup))
			{
				float num6 = (flag4 ? 0f : num2);
				if (force || !Mathf.Approximately(generalCanvasGroup.alpha, num6))
				{
					generalCanvasGroup.alpha = num6;
				}
				bool flag5 = !flag4;
				if (generalCanvasGroup.blocksRaycasts != flag5)
				{
					generalCanvasGroup.blocksRaycasts = flag5;
				}
				if (generalCanvasGroup.interactable != flag5)
				{
					generalCanvasGroup.interactable = flag5;
				}
			}
			ChatScrollbarState scrollbarState = GetScrollbarState(assets);
			if (scrollbarState != null)
			{
				UpdateScrollbarState(assets, scrollbarState, flag2, alpha, transparentOverride, flag4);
				SyncChatVisibility(assets, scrollbarState, flag2, flag4);
			}
		}

		private void Awake()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			InitConfig();
			Log = ((BaseUnityPlugin)this).Logger;
			Harmony val = new Harmony("ChatX");
			val.PatchAll();
			Settings.OnInitialized.AddListener(new UnityAction(AddSettings));
			Settings.OnApplySettings.AddListener((UnityAction)delegate
			{
				((BaseUnityPlugin)this).Config.Save();
				ChatInputCharacterLimiter.ApplyCharacterLimit();
				ChatWindowResizer.ApplyAll();
			});
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ChatX loaded!");
		}

		private void Update()
		{
			TryHandleHotkeys();
		}

		private static string ClipFromEnum(MentionClip e)
		{
			return ClipNames[(int)e];
		}

		private static MentionClip EnumFromClip(string s)
		{
			int num = Array.IndexOf(ClipNames, s);
			return (num >= 0) ? ((MentionClip)num) : MentionClip.LexiconBell;
		}

		private void InitConfig()
		{
			chatOpacity = B<float>("Chat Opacity", 1f, "Sets the desired opacity", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			blockChat = B<bool>("Block Chat", v: false, "Completely blocks chat messages from appearing");
			blockGameFeed = B<bool>("Block Game Feed", v: false, "Completely blocks messages in the Game Feed");
			chatPrefix = B<bool>("Chat Prefix", v: true, "Show a (G)/(P)/(R) prefix that inherits the channel color");
			chatTimestamp = B<bool>("Chat Timestamp", v: false, "Show a [HH:MM] timestamp before chat prefixes");
			timestampFormat = B<bool>("Timestamp 12h/24h", v: true, "Switch between 12h/24h time format");
			asteriskItalic = B<bool>("Asterisk Italics", v: true, "Enable *italic* chat formatting");
			chatTallWindow = B<bool>("Tall Chat Window", v: false, "Increase chat log height to show more lines");
			clearChatKey = B<KeyCode>("Clear Chat", (KeyCode)279, "Keybind that clears chat");
			toggleChatVisibilityKey = B<KeyCode>("Toggle Chat Visibility", (KeyCode)278, "Keybind that toggles the chat opacity between visible and hidden");
			messageLimit = B<bool>("Extend Message Limit", v: true, "Toggle the max input length between 125 and 500 characters");
			pushGameMessage = B<bool>("Push game chat to feed", v: true, "When enabled, reroute gameplay system messages to the Game Feed instead of chat");
			mentionUnderline = B<bool>("Mention Underline", v: true, "Underlines your name when someone mentions you in chat");
			mentionPing = B<bool>("Mention Ping", v: true, "Play a sound when your name appears in chat");
			mentionPingClip = B<string>("Selected Clip", "_lexiconBell", "Audio clip to use for mention ping");
			mentionPingClipEnum = B<MentionClip>("Mention Ping Clip", MentionClip.LexiconBell, "Select the mention ping clip from a list");
			mentionPingVolume = B<float>("Volume", 0.5f, "Volume for the ping sound", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			transparentScrollbar = B<bool>("Transparent Scrollbar", v: false, "Hide the chat scrollbar while keeping it functional.");
			chatTallWindow.SettingChanged += delegate
			{
				ChatWindowResizer.ApplyAll();
			};
			transparentScrollbar.SettingChanged += delegate
			{
				ApplyOpacityCap(ChatBehaviourAssets._current, force: true);
			};
			mentionPing.SettingChanged += delegate
			{
				ReloadMentionClip();
			};
			mentionPingClipEnum.SettingChanged += delegate
			{
				string text = ClipFromEnum(mentionPingClipEnum.Value);
				if (mentionPingClip.Value != text)
				{
					mentionPingClip.Value = text;
				}
				ReloadMentionClip();
				TryPlayMentionPingThrottled(0.5f);
			};
			mentionPingClipEnum.Value = EnumFromClip(mentionPingClip.Value);
			mentionPingVolume.SettingChanged += delegate
			{
				TryPlayMentionPingThrottled(1f);
			};
			ConfigEntry<T> B<T>(string k, T v, string d, AcceptableValueBase r = null)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Expected O, but got Unknown
				//IL_0036: Expected O, but got Unknown
				return ((BaseUnityPlugin)this).Config.Bind<T>(new ConfigDefinition("ChatX", k), v, (r == null) ? new ConfigDescription(d, (AcceptableValueBase)null, Array.Empty<object>()) : new ConfigDescription(d, r, Array.Empty<object>()));
			}
		}

		private void AddSettings()
		{
			SettingsTab tab = Settings.ModTab;
			tab.AddHeader("ChatX Client Settings");
			S("Chat Opacity", chatOpacity);
			T("Transparent Scrollbar", transparentScrollbar);
			T("Chat Prefix", chatPrefix);
			T("Chat Timestamp", chatTimestamp);
			T("Timestamp Format (12h/24h)", timestampFormat);
			T("Asterisk Italic", asteriskItalic);
			T("Tall Chat Window", chatTallWindow);
			T("Push game chat to feed", pushGameMessage);
			T("Block Chat", blockChat);
			T("Block Game Feed", blockGameFeed);
			K("Clear Chat and Feed", clearChatKey);
			K("Toggle Chat Visibility", toggleChatVisibilityKey);
			tab.AddHeader("Mention Options");
			T("Mention Underline", mentionUnderline);
			T("Mention Ping", mentionPing);
			S("Mention Ping Volume", mentionPingVolume);
			D<MentionClip>("Mention Ping Clip", mentionPingClipEnum);
			tab.AddHeader("Both Host + Client");
			T("Message Limit 125/500", messageLimit);
			void D<T>(string label, ConfigEntry<T> e) where T : Enum
			{
				tab.AddDropdown<T>(label, e);
			}
			void K(string label, ConfigEntry<KeyCode> e)
			{
				tab.AddKeyButton(label, e);
			}
			void S(string label, ConfigEntry<float> e)
			{
				tab.AddAdvancedSlider(label, e, false);
			}
			void T(string label, ConfigEntry<bool> e)
			{
				tab.AddToggle(label, e);
			}
		}

		public static int GetMaxMessageLength()
		{
			return messageLimit.Value ? 500 : 125;
		}

		public static void ClearChatNow()
		{
			ChatBehaviour val = Player._mainPlayer?._chatBehaviour;
			ChatBehaviourAssets val2 = val?._chatAssets;
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2))
			{
				val._chatMessages?.Clear();
				if (Object.op_Implicit((Object)(object)val2._chatText))
				{
					((TMP_Text)val2._chatText).text = string.Empty;
				}
				ApplyOpacityCap(val2);
			}
		}

		public static void ClearGameFeedNow()
		{
			ChatBehaviour val = Player._mainPlayer?._chatBehaviour;
			ChatBehaviourAssets val2 = val?._chatAssets;
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2))
			{
				val._gameLogicMessages?.Clear();
				if (Object.op_Implicit((Object)(object)val2._gameLogicText))
				{
					val2._gameLogicText.text = string.Empty;
				}
				ApplyOpacityCap(val2);
			}
		}

		public static void ClearAllNow()
		{
			ClearChatNow();
			ClearGameFeedNow();
		}

		private static void TryHandleHotkeys()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Player mainPlayer = Player._mainPlayer;
			if (Object.op_Implicit((Object)(object)mainPlayer))
			{
				InputField val = mainPlayer?._chatBehaviour?._chatAssets?._chatInput ?? ChatBehaviourAssets._current?._chatInput;
				bool flag = Object.op_Implicit((Object)(object)val) && val.isFocused;
				ConfigEntry<KeyCode> obj = clearChatKey;
				KeyCode val2 = (KeyCode)((obj != null) ? ((int)obj.Value) : 0);
				if ((int)val2 != 0 && !flag && Input.GetKeyDown(val2))
				{
					ClearAllNow();
				}
				ConfigEntry<KeyCode> obj2 = toggleChatVisibilityKey;
				KeyCode val3 = (KeyCode)((obj2 != null) ? ((int)obj2.Value) : 0);
				if ((int)val3 != 0 && !flag && Input.GetKeyDown(val3))
				{
					_chatHidden = !_chatHidden;
					ChatBehaviourAssets assets = mainPlayer?._chatBehaviour?._chatAssets ?? ChatBehaviourAssets._current;
					ApplyOpacityCap(assets, force: true);
				}
			}
		}

		private static bool ShouldPushGameFeed()
		{
			return pushGameMessage?.Value ?? false;
		}

		private static string StripRichTextTags(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return message;
			}
			StringBuilder stringBuilder = new StringBuilder(message.Length);
			bool flag = false;
			foreach (char c in message)
			{
				if (c == '<')
				{
					flag = true;
				}
				else if (c == '>' && flag)
				{
					flag = false;
				}
				else if (!flag)
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		internal static void RouteWaypointAttuneMessage(ChatBehaviour chat, string message)
		{
			if (!ShouldPushGameFeed())
			{
				if (chat != null)
				{
					chat.New_ChatMessage(message);
				}
			}
			else if (Object.op_Implicit((Object)(object)chat) && !string.IsNullOrEmpty(message))
			{
				PushGameFeedMessage(chat, StripRichTextTags(message));
			}
		}

		internal static void PushGameFeedMessage(ChatBehaviour chat, string message)
		{
			if (Object.op_Implicit((Object)(object)chat) && !string.IsNullOrEmpty(message))
			{
				if (ShouldPushGameFeed())
				{
					chat.Init_GameLogicMessage(message);
				}
				else
				{
					chat.New_ChatMessage(message);
				}
			}
		}

		internal static void PushTargetedGameFeedMessage(ChatBehaviour chat, string message)
		{
			if (Object.op_Implicit((Object)(object)chat) && !string.IsNullOrEmpty(message))
			{
				if (ShouldPushGameFeed())
				{
					chat.Target_GameLogicMessage(message);
				}
				else
				{
					chat.Target_RecieveMessage(message);
				}
			}
		}

		internal static void TryPlayMentionPingThrottled(float minInterval = 0.25f)
		{
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime < _nextMentionPingAllowedAt))
			{
				_nextMentionPingAllowedAt = unscaledTime + minInterval;
				TryPlayMentionPing();
			}
		}

		private static AudioSource ResolveMentionAudioSource()
		{
			ChatBehaviour val = Player._mainPlayer?._chatBehaviour;
			if ((Object)(object)val != (Object)null && (Object)(object)val.aSrc != (Object)null)
			{
				return val.aSrc;
			}
			ChatBehaviour current = ChatBehaviour._current;
			return ((Object)(object)current != (Object)null) ? current.aSrc : null;
		}

		private static float GetMentionVolume()
		{
			return Mathf.Clamp01(mentionPingVolume?.Value ?? 1f);
		}

		private static AudioClip FindAudioClipByName(string clipName)
		{
			if (string.IsNullOrWhiteSpace(clipName))
			{
				return null;
			}
			clipName = clipName.Trim();
			try
			{
				AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>();
				foreach (AudioClip val in array)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)$"ChatX mention clip search via Resources failed: {arg}");
				}
			}
			AudioClip val2 = TryLoadFromResources(clipName);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			PlayerSound val3 = Player._mainPlayer?._pSound;
			if ((Object)(object)val3 != (Object)null)
			{
				FieldInfo[] fields = ((object)val3).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo[] array2 = fields;
				foreach (FieldInfo fieldInfo in array2)
				{
					if (fieldInfo.FieldType == typeof(AudioClip))
					{
						object? value = fieldInfo.GetValue(val3);
						AudioClip val4 = (AudioClip)((value is AudioClip) ? value : null);
						if (val4 != null && (Object)(object)val4 != (Object)null && ((Object)val4).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
						{
							return val4;
						}
					}
					else
					{
						if (!fieldInfo.FieldType.IsArray || !(fieldInfo.FieldType.GetElementType() == typeof(AudioClip)) || !(fieldInfo.GetValue(val3) is AudioClip[] array3))
						{
							continue;
						}
						AudioClip[] array4 = array3;
						foreach (AudioClip val5 in array4)
						{
							if ((Object)(object)val5 != (Object)null && ((Object)val5).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
							{
								return val5;
							}
						}
					}
				}
			}
			AudioSource[] array5 = Resources.FindObjectsOfTypeAll<AudioSource>();
			foreach (AudioSource val6 in array5)
			{
				if ((Object)(object)val6 != (Object)null && (Object)(object)val6.clip != (Object)null && ((Object)val6.clip).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
				{
					return val6.clip;
				}
			}
			ButtonSound[] array6 = Resources.FindObjectsOfTypeAll<ButtonSound>();
			if (array6 != null)
			{
				ButtonSound[] array7 = array6;
				foreach (ButtonSound val7 in array7)
				{
					if (!((Object)(object)val7 == (Object)null))
					{
						Type type = ((object)val7).GetType();
						object? obj = type.GetField("_hoverSoundClip", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val7);
						AudioClip val8 = (AudioClip)((obj is AudioClip) ? obj : null);
						if ((Object)(object)val8 != (Object)null && ((Object)val8).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
						{
							return val8;
						}
						object? obj2 = type.GetField("_clickSoundClip", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val7);
						AudioClip val9 = (AudioClip)((obj2 is AudioClip) ? obj2 : null);
						if ((Object)(object)val9 != (Object)null && ((Object)val9).name.Equals(clipName, StringComparison.OrdinalIgnoreCase))
						{
							return val9;
						}
					}
				}
			}
			return null;
		}

		private static bool ClipMatches(AudioClip clip, string clipName)
		{
			if ((Object)(object)clip == (Object)null || string.IsNullOrEmpty(clipName))
			{
				return false;
			}
			return ((Object)clip).name.Equals(clipName, StringComparison.OrdinalIgnoreCase);
		}

		private static AudioClip TryLoadFromResources(string clipName)
		{
			AudioClip val = Attempt(clipName);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if (clipName.StartsWith("_", StringComparison.Ordinal))
			{
				val = Attempt(clipName.TrimStart('_'));
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			string text = clipName.ToLowerInvariant();
			val = Attempt(text);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if (text.StartsWith("_", StringComparison.Ordinal))
			{
				val = Attempt(text.TrimStart('_'));
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
			static AudioClip Attempt(string path)
			{
				try
				{
					return Resources.Load<AudioClip>(path);
				}
				catch (Exception ex)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogDebug((object)("ChatX Resources.Load failed for '" + path + "': " + ex.Message));
					}
					return null;
				}
			}
		}

		internal static void ReloadMentionClip()
		{
			_mentionAudioClip = null;
			if (mentionPing == null || !mentionPing.Value)
			{
				return;
			}
			string text = mentionPingClip?.Value?.Trim();
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			AudioClip val = FindAudioClipByName(text);
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("ChatX mention clip '" + text + "' was not found among loaded audio clips."));
				}
				return;
			}
			_mentionAudioClip = val;
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("ChatX mention clip set to '" + ((Object)_mentionAudioClip).name + "'."));
			}
		}

		private static void TryPlayMentionPing()
		{
			ConfigEntry<bool> obj = mentionPing;
			if (obj == null || !obj.Value)
			{
				return;
			}
			AudioSource val = ResolveMentionAudioSource();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)_mentionAudioClip == (Object)null)
			{
				ReloadMentionClip();
				if ((Object)(object)_mentionAudioClip == (Object)null)
				{
					return;
				}
			}
			val.PlayOneShot(_mentionAudioClip, GetMentionVolume());
		}

		private static bool TryExtractSpeakerNickname(string message, out string nickname)
		{
			nickname = string.Empty;
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}
			int num = message.IndexOf("<color=#afeeee>[", StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				return false;
			}
			num += "<color=#afeeee>[".Length;
			int num2 = message.IndexOf("]</color>", num, StringComparison.OrdinalIgnoreCase);
			if (num2 < 0)
			{
				return false;
			}
			int num3 = num;
			nickname = message.Substring(num3, num2 - num3).Trim();
			return nickname.Length > 0;
		}

		private static string GetLocalPlayerNickname()
		{
			try
			{
				Player mainPlayer = Player._mainPlayer;
				if ((Object)(object)mainPlayer == (Object)null)
				{
					return string.Empty;
				}
				string network_nickname = mainPlayer.Network_nickname;
				return string.IsNullOrWhiteSpace(network_nickname) ? string.Empty : network_nickname.Trim();
			}
			catch
			{
				return string.Empty;
			}
		}

		private static bool ShouldPlayMentionPing(string message)
		{
			ConfigEntry<bool> obj = mentionPing;
			if (obj == null || !obj.Value)
			{
				return false;
			}
			string localPlayerNickname = GetLocalPlayerNickname();
			if (string.IsNullOrEmpty(localPlayerNickname))
			{
				return false;
			}
			string nickname;
			return TryExtractSpeakerNickname(message, out nickname) && !nickname.Equals(localPlayerNickname, StringComparison.OrdinalIgnoreCase);
		}

		private static void ApplyMentionEffects(ref string message)
		{
			message = ApplyMentionUnderline(message, out var mentionDetected);
			if (mentionDetected && ShouldPlayMentionPing(message))
			{
				TryPlayMentionPingThrottled(1f);
			}
		}

		private static string NormalizeChannelColorToken(string token)
		{
			if (string.IsNullOrWhiteSpace(token))
			{
				return string.Empty;
			}
			token = token.Trim();
			if (token.StartsWith("#", StringComparison.Ordinal) && token.Length >= 7)
			{
				token = token.Substring(0, 7);
			}
			return token.ToUpperInvariant();
		}

		private static bool TryExtractChatChannel(string message, out ChatChannel channel)
		{
			channel = (ChatChannel)0;
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}
			int num = message.IndexOf("</color>: <color=", StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				return false;
			}
			int num2 = num + "</color>: <color=".Length;
			if (num2 >= message.Length)
			{
				return false;
			}
			int num3 = message.IndexOf('>', num2);
			if (num3 < 0)
			{
				return false;
			}
			int num4 = num2;
			string token = message.Substring(num4, num3 - num4);
			string text = NormalizeChannelColorToken(token);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			switch (text)
			{
			case "YELLOW":
			case "#FFFF00":
			case "#FFD700":
				channel = (ChatChannel)0;
				return true;
			case "#B2EC5D":
				channel = (ChatChannel)1;
				return true;
			case "#FF8A90":
				channel = (ChatChannel)2;
				return true;
			default:
				return false;
			}
		}

		private static int SkipTimestampPrefix(string msg, int startIndex = 0)
		{
			if (string.IsNullOrEmpty(msg) || startIndex >= msg.Length)
			{
				return startIndex;
			}
			if (msg[startIndex] != '[')
			{
				return startIndex;
			}
			int num = startIndex + 1;
			if (num >= msg.Length || !char.IsDigit(msg[num]))
			{
				return startIndex;
			}
			num++;
			if (num < msg.Length && char.IsDigit(msg[num]))
			{
				num++;
			}
			if (num >= msg.Length || msg[num] != ':')
			{
				return startIndex;
			}
			num++;
			if (num + 1 >= msg.Length || !char.IsDigit(msg[num]) || !char.IsDigit(msg[num + 1]))
			{
				return startIndex;
			}
			num += 2;
			if (num < msg.Length && msg[num] == ' ')
			{
				if (num + 2 >= msg.Length || (msg[num + 1] != 'A' && msg[num + 1] != 'a' && msg[num + 1] != 'P' && msg[num + 1] != 'p') || (msg[num + 2] != 'M' && msg[num + 2] != 'm'))
				{
					return startIndex;
				}
				num += 3;
			}
			if (num >= msg.Length || msg[num] != ']')
			{
				return startIndex;
			}
			num++;
			if (num < msg.Length && msg[num] == ' ')
			{
				num++;
			}
			return num;
		}

		public static string BuildColoredPrefixFromEnum(ChatChannel ch)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected I4, but got Unknown
			if (1 == 0)
			{
			}
			string result = (int)ch switch
			{
				0 => "<color=yellow>(G)</color>", 
				1 => "<color=#B2EC5D>(P)</color>", 
				2 => "<color=#FF8A90>(R)</color>", 
				_ => string.Empty, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static string BuildMessagePrefix(ChatChannel ch)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			ConfigEntry<bool> obj = chatTimestamp;
			if (obj != null && obj.Value)
			{
				string text = (timestampFormat.Value ? "HH:mm" : "h:mm tt");
				string text2 = DateTime.Now.ToString(text, CultureInfo.InvariantCulture);
				list.Add("[" + text2 + "]");
			}
			string text3 = BuildColoredPrefixFromEnum(ch);
			if (!string.IsNullOrEmpty(text3))
			{
				list.Add(text3);
			}
			return (list.Count == 0) ? string.Empty : (string.Join(" ", list) + " ");
		}

		public static bool LooksPrefixed(string msg)
		{
			if (string.IsNullOrEmpty(msg))
			{
				return false;
			}
			int num = SkipTimestampPrefix(msg);
			if (msg.Length - num >= 4)
			{
				string text = msg.Substring(num, 4);
				if (text == "(G) " || text == "(P) " || text == "(R) ")
				{
					return true;
				}
			}
			if (num < msg.Length && msg.IndexOf("<color", num, StringComparison.OrdinalIgnoreCase) == num)
			{
				int num2 = msg.IndexOf('>', num);
				if (num2 >= 0)
				{
					int num3 = msg.IndexOf("</color>", num2 + 1, StringComparison.OrdinalIgnoreCase);
					if (num3 > num2 + 1)
					{
						int num4 = num2 + 1;
						string text2 = msg.Substring(num4, num3 - num4);
						if (text2.StartsWith("(G)") || text2.StartsWith("(P)") || text2.StartsWith("(R)"))
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		public static string InsertPrefix(string line, string prefix)
		{
			if (string.IsNullOrEmpty(prefix) || string.IsNullOrEmpty(line))
			{
				return line;
			}
			return prefix + line;
		}

		public static string ApplyAsteriskFormatting(string s)
		{
			ConfigEntry<bool> obj = asteriskItalic;
			if (obj == null || !obj.Value || string.IsNullOrEmpty(s) || s.IndexOf('*') < 0)
			{
				return s;
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 16);
			ReadOnlySpan<char> readOnlySpan = s.AsSpan();
			bool flag = false;
			bool flag2 = false;
			int num = -1;
			for (int i = 0; i < readOnlySpan.Length; i++)
			{
				char c = readOnlySpan[i];
				if (c == '<')
				{
					flag2 = true;
					stringBuilder.Append(c);
				}
				else if (c == '>' && flag2)
				{
					flag2 = false;
					stringBuilder.Append(c);
				}
				else if (c == '\\' && i + 1 < readOnlySpan.Length && readOnlySpan[i + 1] == '*')
				{
					stringBuilder.Append('*');
					i++;
				}
				else if (c == '*' && !flag2)
				{
					int j;
					for (j = i + 1; j < readOnlySpan.Length && readOnlySpan[j] == '*'; j++)
					{
					}
					int num2 = j - i;
					if (num2 == 1)
					{
						if (flag)
						{
							stringBuilder.Append("</i>");
							flag = false;
							num = -1;
						}
						else
						{
							num = stringBuilder.Length;
							stringBuilder.Append("<i>");
							flag = true;
						}
					}
					else
					{
						stringBuilder.Append('*', num2);
					}
					i = j - 1;
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			if (flag && num >= 0)
			{
				stringBuilder.Remove(num, 3).Insert(num, "*");
			}
			return stringBuilder.ToString();
		}

		private static bool IsAsciiLetter(char c)
		{
			return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
		}

		private static bool HasLetterNeighbor(string s, int start, int len, int leftBound)
		{
			int num = ((start > leftBound) ? (start - 1) : (-1));
			int num2 = ((start + len < s.Length) ? (start + len) : (-1));
			char? c = ((num >= 0) ? new char?(s[num]) : null);
			char? c2 = ((num2 >= 0) ? new char?(s[num2]) : null);
			bool flag = c.HasValue && IsAsciiLetter(c.Value);
			bool flag2 = c2.HasValue && IsAsciiLetter(c2.Value);
			return flag || flag2;
		}

		private static string ApplyMentionUnderline(string message, out bool mentionDetected)
		{
			mentionDetected = false;
			if (!mentionUnderline.Value)
			{
				return message;
			}
			if (string.IsNullOrEmpty(message))
			{
				return message;
			}
			string localPlayerNickname = GetLocalPlayerNickname();
			if (string.IsNullOrEmpty(localPlayerNickname))
			{
				return message;
			}
			int num = message.IndexOf("</color>: ", StringComparison.OrdinalIgnoreCase);
			num = ((num >= 0) ? (num + "</color>: ".Length) : 0);
			StringBuilder stringBuilder = new StringBuilder(message.Length + 16);
			if (num > 0)
			{
				stringBuilder.Append(message, 0, num);
			}
			int num2 = num;
			while (num2 < message.Length)
			{
				int num3 = message.IndexOf(localPlayerNickname, num2, StringComparison.OrdinalIgnoreCase);
				if (num3 < 0)
				{
					stringBuilder.Append(message, num2, message.Length - num2);
					break;
				}
				stringBuilder.Append(message, num2, num3 - num2);
				if (HasLetterNeighbor(message, num3, localPlayerNickname.Length, num))
				{
					stringBuilder.Append(message[num3]);
					num2 = num3 + 1;
					continue;
				}
				int num4 = message.LastIndexOf("<u>", num3, StringComparison.OrdinalIgnoreCase);
				if (num4 >= 0)
				{
					int num5 = message.IndexOf("</u>", num4, StringComparison.OrdinalIgnoreCase);
					if (num5 >= 0 && num5 >= num3 + localPlayerNickname.Length)
					{
						stringBuilder.Append(message, num3, localPlayerNickname.Length);
						num2 = num3 + localPlayerNickname.Length;
						continue;
					}
				}
				stringBuilder.Append("<u>");
				stringBuilder.Append(message, num3, localPlayerNickname.Length);
				stringBuilder.Append("</u>");
				mentionDetected = true;
				num2 = num3 + localPlayerNickname.Length;
			}
			return stringBuilder.ToString();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ChatX";

		public const string PLUGIN_NAME = "ChatX";

		public const string PLUGIN_VERSION = "1.9.2";
	}
}