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";
}
}