using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppTMPro;
using LabFusion.Data;
using LabFusion.Entities;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.SDK.Modules;
using LabFusion.UI;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using RealTimeHeadChat;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(/*Could not decode attribute arguments.*/)]
[assembly: MelonInfo(typeof(RealTimeHeadChatMod), "Real-Time Head Chat", "1.5.0", "Local Utility", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : System.Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(/*Could not decode attribute arguments.*/)]
internal sealed class RefSafetyRulesAttribute : System.Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace RealTimeHeadChat
{
public sealed class RealTimeHeadChatMod : MelonMod
{
private sealed class Bubble
{
public NetworkPlayer Player;
public RigNameTag Element;
public float LastUpdate;
public string Text;
public float BaseFontSize;
public TextMeshProUGUI TextInstance;
}
[CompilerGenerated]
private static class _003C_003EO
{
public static Action _003C0_003E__CreateMenu;
public static ServerEvent _003C1_003E__ClearAll;
public static PlayerUpdate _003C2_003E__OnPlayerLeft;
public static Action _003C3_003E__ClearLocalText;
}
[Serializable]
[CompilerGenerated]
private sealed class _003C_003Ec
{
public static readonly _003C_003Ec _003C_003E9 = new _003C_003Ec();
public static Action<bool> _003C_003E9__35_0;
public static Action<bool> _003C_003E9__35_1;
public static Action<bool> _003C_003E9__35_2;
public static Action _003C_003E9__35_3;
public static Action<float> _003C_003E9__36_0;
public static Action<float> _003C_003E9__36_1;
public static Action<float> _003C_003E9__36_2;
internal void _003CCreateMenu_003Eb__35_0(bool value)
{
_enabled.Value = value;
MelonPreferences.Save();
if (!value)
{
ClearLocalText();
}
}
internal void _003CCreateMenu_003Eb__35_1(bool disabled)
{
SetInputCapture(!disabled);
}
internal void _003CCreateMenu_003Eb__35_2(bool value)
{
_showHud.Value = value;
MelonPreferences.Save();
if ((Object)(object)_hudRoot != (Object)null)
{
_hudRoot.SetActive(_enabled.Value && value);
}
}
internal void _003CCreateMenu_003Eb__35_3()
{
Menu.DisplayDialog("REAL-TIME HEAD CHAT", "Head Chat Enabled is the master switch. Disable Text Input stops only local typing and paste capture; toggle the same setting anytime with F2 or Page Up. Show VR HUD controls only your headset preview. Backspace edits; Enter clears; Ctrl+V pastes. Text clears after five seconds without typing. Everyone who should see overhead text needs this mod.", (Texture2D)null, (Action)null, (Action)null);
}
internal void _003CBuildPlacementMenu_003Eb__36_0(float value)
{
_hudX.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
internal void _003CBuildPlacementMenu_003Eb__36_1(float value)
{
_hudY.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
internal void _003CBuildPlacementMenu_003Eb__36_2(float value)
{
_hudDistance.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
}
[CompilerGenerated]
private static class <>O
{
public static Action <0>__CreateMenu;
public static ServerEvent <1>__ClearAll;
public static PlayerUpdate <2>__OnPlayerLeft;
public static Action <3>__ClearLocalText;
}
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Action<bool> <>9__39_0;
public static Action<bool> <>9__39_1;
public static Action<bool> <>9__39_2;
public static Action <>9__39_3;
public static Action<float> <>9__40_0;
public static Action<float> <>9__40_1;
public static Action<float> <>9__40_2;
internal void <CreateMenu>b__39_0(bool value)
{
_enabled.Value = value;
MelonPreferences.Save();
if (!value)
{
ClearLocalText();
}
}
internal void <CreateMenu>b__39_1(bool disabled)
{
SetInputCapture(!disabled);
}
internal void <CreateMenu>b__39_2(bool value)
{
_showHud.Value = value;
MelonPreferences.Save();
if ((Object)(object)_hudRoot != (Object)null)
{
_hudRoot.SetActive(_enabled.Value && value);
}
}
internal void <CreateMenu>b__39_3()
{
Menu.DisplayDialog("REAL-TIME HEAD CHAT", "Head Chat Enabled is the master switch. Disable Text Input stops only local typing and paste capture; toggle the same setting anytime with F2 or Page Up. Show VR HUD controls only your headset preview. Backspace edits; Enter clears; Ctrl+V pastes. Text clears after five seconds without typing. Everyone who should see overhead text needs this mod.", (Texture2D)null, (Action)null, (Action)null);
}
internal void <BuildPlacementMenu>b__40_0(float value)
{
_hudX.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
internal void <BuildPlacementMenu>b__40_1(float value)
{
_hudY.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
internal void <BuildPlacementMenu>b__40_2(float value)
{
_hudDistance.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
}
}
private static float _canonicalNametagFontSize;
internal const int MaxCharacters = 160;
internal const int MaxUtf8Bytes = 4096;
private const int SecondLineThreshold = 80;
private const int ThirdLineThreshold = 120;
private const float SendInterval = 0.1f;
private const float RemoteTimeout = 12f;
private const float LocalIdleTimeout = 5f;
private const float DefaultHudXCm = -10.8f;
private const float DefaultHudYCm = -7.2f;
private const float DefaultHudDistanceCm = 15f;
private static readonly Dictionary<byte, Bubble> Bubbles = new Dictionary<byte, Bubble>();
private static MelonPreferences_Entry<bool> _enabled;
private static MelonPreferences_Entry<bool> _captureInput;
private static MelonPreferences_Entry<bool> _showHud;
private static MelonPreferences_Entry<float> _hudX;
private static MelonPreferences_Entry<float> _hudY;
private static MelonPreferences_Entry<float> _hudDistance;
private static string _pendingText = string.Empty;
private static string _lastSentText = string.Empty;
private static float _nextSendTime;
private static float _lastKeyTime;
private static bool _menuCreated;
private static BoolElement _disableInputElement;
private static GameObject _hudRoot;
private static TMP_Text _hudText;
public override void OnInitializeMelon()
{
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Expected O, but got Unknown
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_0161: Expected O, but got Unknown
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0104: Expected O, but got Unknown
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01a6: Expected O, but got Unknown
//IL_013e: 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_0149: Expected O, but got Unknown
//IL_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_0188: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Expected O, but got Unknown
MelonPreferences_Category val = MelonPreferences.CreateCategory("RealTimeHeadChat");
_enabled = val.CreateEntry<bool>("Enabled", true, "Head Chat Enabled", "Controls keyboard capture and broadcasting overhead text to other modded players.", false, false, (ValueValidator)null, (string)null);
_captureInput = val.CreateEntry<bool>("CaptureKeyboardInput", true, "Capture Keyboard Input", "Controls only local typing and paste capture. Toggle with F2 or Page Up.", false, false, (ValueValidator)null, (string)null);
_showHud = val.CreateEntry<bool>("ShowTypingHud", true, "Show VR HUD", "Controls only the local lower-left headset preview. Head chat remains active when hidden.", false, false, (ValueValidator)null, (string)null);
_hudX = val.CreateEntry<float>("HudHorizontalCm", -10.8f, "HUD Horizontal Position (cm)", "Negative values move left.", false, false, (ValueValidator)null, (string)null);
_hudY = val.CreateEntry<float>("HudVerticalCm", -7.2f, "HUD Vertical Position (cm)", "Negative values move down.", false, false, (ValueValidator)null, (string)null);
_hudDistance = val.CreateEntry<float>("HudDistanceCm", 15f, "HUD Distance (cm)", "Distance in centimeters from the headset.", false, false, (ValueValidator)null, (string)null);
ModuleManager.RegisterModule<HeadChatModule>();
object obj = _003C_003EO._003C0_003E__CreateMenu;
if (obj == null)
{
object obj2 = <>O.<0>__CreateMenu;
if (obj2 == null)
{
Action val2 = CreateMenu;
<>O.<0>__CreateMenu = val2;
obj2 = (object)val2;
}
obj = (_003C_003EO._003C0_003E__CreateMenu = (Action)obj2);
}
Hooking.OnUIRigCreated += (Action)obj;
object obj3 = _003C_003EO._003C1_003E__ClearAll;
if (obj3 == null)
{
object obj4 = <>O.<1>__ClearAll;
if (obj4 == null)
{
ServerEvent val3 = ClearAll;
<>O.<1>__ClearAll = val3;
obj4 = (object)val3;
}
obj3 = (_003C_003EO._003C1_003E__ClearAll = (ServerEvent)obj4);
}
MultiplayerHooking.OnDisconnected += (ServerEvent)obj3;
object obj5 = _003C_003EO._003C2_003E__OnPlayerLeft;
if (obj5 == null)
{
object obj6 = <>O.<2>__OnPlayerLeft;
if (obj6 == null)
{
PlayerUpdate val4 = OnPlayerLeft;
<>O.<2>__OnPlayerLeft = val4;
obj6 = (object)val4;
}
obj5 = (_003C_003EO._003C2_003E__OnPlayerLeft = (PlayerUpdate)obj6);
}
MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj5;
}
public override void OnDeinitializeMelon()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Expected O, but got Unknown
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Expected O, but got Unknown
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Expected O, but got Unknown
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Expected O, but got Unknown
object obj = _003C_003EO._003C0_003E__CreateMenu;
if (obj == null)
{
object obj2 = <>O.<0>__CreateMenu;
if (obj2 == null)
{
Action val = CreateMenu;
<>O.<0>__CreateMenu = val;
obj2 = (object)val;
}
obj = (_003C_003EO._003C0_003E__CreateMenu = (Action)obj2);
}
Hooking.OnUIRigCreated -= (Action)obj;
object obj3 = _003C_003EO._003C1_003E__ClearAll;
if (obj3 == null)
{
object obj4 = <>O.<1>__ClearAll;
if (obj4 == null)
{
ServerEvent val2 = ClearAll;
<>O.<1>__ClearAll = val2;
obj4 = (object)val2;
}
obj3 = (_003C_003EO._003C1_003E__ClearAll = (ServerEvent)obj4);
}
MultiplayerHooking.OnDisconnected -= (ServerEvent)obj3;
object obj5 = _003C_003EO._003C2_003E__OnPlayerLeft;
if (obj5 == null)
{
object obj6 = <>O.<2>__OnPlayerLeft;
if (obj6 == null)
{
PlayerUpdate val3 = OnPlayerLeft;
<>O.<2>__OnPlayerLeft = val3;
obj6 = (object)val3;
}
obj5 = (_003C_003EO._003C2_003E__OnPlayerLeft = (PlayerUpdate)obj6);
}
MultiplayerHooking.OnPlayerLeft -= (PlayerUpdate)obj5;
DestroyHud();
ClearAll();
}
public override void OnUpdate()
{
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
EnsureVrHud();
if (IsInputTogglePressed())
{
SetInputCapture(!_captureInput.Value);
}
CaptureKeyboardInput();
if (_pendingText.Length != 0 && Time.unscaledTime - _lastKeyTime >= 5f)
{
ClearLocalText();
}
if (Time.unscaledTime >= _nextSendTime && _pendingText != _lastSentText)
{
Publish(_pendingText);
_nextSendTime = Time.unscaledTime + 0.1f;
}
if (Bubbles.Count == 0)
{
return;
}
List<byte> val = null;
Enumerator<byte, Bubble> enumerator = Bubbles.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
KeyValuePair<byte, Bubble> current = enumerator.Current;
UpdateBubbleAppearance(current.Value);
if (Time.unscaledTime - current.Value.LastUpdate > 12f)
{
if (val == null)
{
val = new List<byte>();
}
val.Add(current.Key);
}
}
}
finally
{
((System.IDisposable)enumerator).Dispose();
}
if (val == null)
{
return;
}
Enumerator<byte> enumerator2 = val.GetEnumerator();
try
{
while (enumerator2.MoveNext())
{
byte current2 = enumerator2.Current;
RemoveBubble(current2);
}
}
finally
{
((System.IDisposable)enumerator2).Dispose();
}
}
public override void OnLateUpdate()
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
Enumerator<byte, Bubble> enumerator = Bubbles.Values.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
Bubble current = enumerator.Current;
StabilizeBubbleScale(current);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
}
private static void EnsureVrHud()
{
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Expected O, but got Unknown
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
if (_enabled != null && RigData.HasPlayer)
{
if ((Object)(object)_hudRoot == (Object)null)
{
CreateVrHud(RigData.Refs.Head);
}
if (!((Object)(object)_hudRoot == (Object)null) && !((Object)_hudText == (Object)null))
{
_hudRoot.SetActive(_enabled.Value && _showHud.Value);
ApplyHudPlacement();
bool flag = string.IsNullOrEmpty(_pendingText);
_hudText.text = ((!_captureInput.Value) ? "Input disabled (F2 / Page Up)" : (flag ? "Start typing..." : _pendingText));
_hudText.fontSize = 24f;
((Graphic)_hudText).color = (flag ? new Color(0.65f, 0.68f, 0.72f, 1f) : new Color(0.88f, 0.97f, 1f, 1f));
}
}
}
private static void CreateVrHud(Transform head)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: 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_00b7: Expected O, but got Unknown
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
//IL_0158: Unknown result type (might be due to invalid IL or missing references)
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
//IL_0190: Unknown result type (might be due to invalid IL or missing references)
//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)head == (Object)null))
{
_hudRoot = new GameObject("Real-Time Head Chat VR HUD");
_hudRoot.transform.SetParent(head, false);
ApplyHudPlacement();
_hudRoot.transform.localRotation = Quaternion.identity;
_hudRoot.transform.localScale = Vector3.one * 0.000145f;
Canvas val = _hudRoot.AddComponent<Canvas>();
val.renderMode = (RenderMode)2;
val.sortingOrder = 500;
_hudRoot.GetComponent<RectTransform>().sizeDelta = new Vector2(480f, 92f);
GameObject val2 = new GameObject("Background");
val2.transform.SetParent(_hudRoot.transform, false);
Image val3 = val2.AddComponent<Image>();
((Graphic)val3).color = new Color(0.025f, 0.03f, 0.04f, 0.78f);
((Graphic)val3).raycastTarget = false;
SetRect(((Graphic)val3).rectTransform, Vector2.zero, new Vector2(480f, 92f));
TMP_Text val4 = CreateHudText("HEAD CHAT", 17f, new Color(0.9f, 0.92f, 0.95f, 1f));
SetRect(val4.rectTransform, new Vector2(0f, 27f), new Vector2(450f, 24f));
_hudText = CreateHudText("Start typing...", 24f, new Color(0.65f, 0.68f, 0.72f, 1f));
_hudText.enableWordWrapping = true;
SetRect(_hudText.rectTransform, new Vector2(0f, -13f), new Vector2(450f, 52f));
}
}
private static TMP_Text CreateHudText(string value, float size, Color color)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Expected O, but got Unknown
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("Text - " + value);
val.transform.SetParent(_hudRoot.transform, false);
TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null)
{
((TMP_Text)val2).font = TMP_Settings.defaultFontAsset;
}
((TMP_Text)val2).text = value;
((TMP_Text)val2).fontSize = size;
((TMP_Text)val2).alignment = (TextAlignmentOptions)513;
((TMP_Text)val2).richText = false;
((Graphic)val2).color = color;
((Graphic)val2).raycastTarget = false;
return (TMP_Text)(object)val2;
}
private static void SetRect(RectTransform rect, Vector2 position, Vector2 size)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: 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_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
rect.anchorMin = new Vector2(0.5f, 0.5f);
rect.anchorMax = new Vector2(0.5f, 0.5f);
rect.pivot = new Vector2(0.5f, 0.5f);
rect.anchoredPosition = position;
rect.sizeDelta = size;
((Transform)rect).localScale = Vector3.one;
((Transform)rect).localRotation = Quaternion.identity;
}
private static void DestroyHud()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
if ((Object)(object)_hudRoot != (Object)null)
{
Object.Destroy((Object)_hudRoot);
}
_hudRoot = null;
_hudText = null;
}
private static void ApplyHudPlacement()
{
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_hudRoot == (Object)null) && _hudX != null && _hudY != null && _hudDistance != null)
{
_hudRoot.transform.localPosition = new Vector3(Mathf.Clamp(_hudX.Value, -50f, 50f) / 100f, Mathf.Clamp(_hudY.Value, -50f, 50f) / 100f, Mathf.Clamp(_hudDistance.Value, 5f, 75f) / 100f);
}
}
private static void CreateMenu()
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0149: Expected O, but got Unknown
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: 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_019b: Unknown result type (might be due to invalid IL or missing references)
//IL_01a5: Expected O, but got Unknown
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Expected O, but got Unknown
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Expected O, but got Unknown
if (_menuCreated)
{
return;
}
Page val = Page.Root.CreatePage("Real-Time Head Chat", Color.cyan, 0, true);
val.CreateBool("Head Chat Enabled", Color.white, _enabled.Value, (Action<bool>)delegate(bool value)
{
_enabled.Value = value;
MelonPreferences.Save();
if (!value)
{
ClearLocalText();
}
});
_disableInputElement = val.CreateBool("Disable Text Input", Color.yellow, !_captureInput.Value, (Action<bool>)delegate(bool disabled)
{
SetInputCapture(!disabled);
});
val.CreateBool("Show VR HUD", Color.cyan, _showHud.Value, (Action<bool>)delegate(bool value)
{
_showHud.Value = value;
MelonPreferences.Save();
if ((Object)(object)_hudRoot != (Object)null)
{
_hudRoot.SetActive(_enabled.Value && value);
}
});
Page page = val.CreatePage("VR HUD Placement", Color.yellow, 0, true);
BuildPlacementMenu(page);
Color red = Color.red;
object obj = _003C_003EO._003C3_003E__ClearLocalText;
if (obj == null)
{
object obj2 = <>O.<3>__ClearLocalText;
if (obj2 == null)
{
Action val2 = ClearLocalText;
<>O.<3>__ClearLocalText = val2;
obj2 = (object)val2;
}
obj = (_003C_003EO._003C3_003E__ClearLocalText = (Action)obj2);
}
val.CreateFunction("Clear my text", red, (Action)obj);
Color gray = Color.gray;
object obj3 = _003C_003Ec._003C_003E9__35_3;
if (obj3 == null)
{
object obj4 = <>c.<>9__39_3;
if (obj4 == null)
{
Action val3 = delegate
{
Menu.DisplayDialog("REAL-TIME HEAD CHAT", "Head Chat Enabled is the master switch. Disable Text Input stops only local typing and paste capture; toggle the same setting anytime with F2 or Page Up. Show VR HUD controls only your headset preview. Backspace edits; Enter clears; Ctrl+V pastes. Text clears after five seconds without typing. Everyone who should see overhead text needs this mod.", (Texture2D)null, (Action)null, (Action)null);
};
<>c.<>9__39_3 = val3;
obj4 = (object)val3;
}
obj3 = (_003C_003Ec._003C_003E9__35_3 = (Action)obj4);
}
val.CreateFunction("How to use", gray, (Action)obj3);
_menuCreated = true;
}
private static void BuildPlacementMenu(Page page)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: 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_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Expected O, but got Unknown
page.RemoveAll();
page.CreateFloat("Horizontal (cm)", Color.white, _hudX.Value, 0.5f, -50f, 50f, (Action<float>)delegate(float value)
{
_hudX.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
});
page.CreateFloat("Vertical (cm)", Color.white, _hudY.Value, 0.5f, -50f, 50f, (Action<float>)delegate(float value)
{
_hudY.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
});
page.CreateFloat("Distance from headset (cm)", Color.white, _hudDistance.Value, 1f, 5f, 75f, (Action<float>)delegate(float value)
{
_hudDistance.Value = value;
MelonPreferences.Save();
ApplyHudPlacement();
});
page.CreateFunction("Reset placement", Color.red, (Action)delegate
{
_hudX.Value = -10.8f;
_hudY.Value = -7.2f;
_hudDistance.Value = 15f;
MelonPreferences.Save();
ApplyHudPlacement();
BuildPlacementMenu(page);
});
}
private static void CaptureKeyboardInput()
{
if (_enabled == null || !_enabled.Value || _captureInput == null || !_captureInput.Value || !NetworkInfo.HasServer)
{
return;
}
if (IsPastePressed())
{
string text = Normalize(GUIUtility.systemCopyBuffer);
int num = 160 - GetTextElementCount(_pendingText);
if (num > 0 && text.Length > 0)
{
_pendingText += TruncateTextElements(text, num);
_lastKeyTime = Time.unscaledTime;
}
return;
}
string inputString = Input.inputString;
if (string.IsNullOrEmpty(inputString))
{
return;
}
bool flag = false;
string text2 = inputString;
for (int i = 0; i < text2.Length; i++)
{
char c = text2[i];
switch (c)
{
case '\b':
if (_pendingText.Length > 0)
{
int[] array = StringInfo.ParseCombiningCharacters(_pendingText);
_pendingText = ((array.Length <= 1) ? string.Empty : _pendingText.Substring(0, array[array.Length - 1]));
flag = true;
}
continue;
default:
if (c != '\r')
{
if (!char.IsControl(c) && GetTextElementCount(_pendingText) < 160)
{
_pendingText += c;
flag = true;
}
continue;
}
break;
case '\n':
break;
}
ClearLocalText();
return;
}
if (flag)
{
_lastKeyTime = Time.unscaledTime;
}
}
private static bool IsPastePressed()
{
if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)118))
{
return true;
}
Keyboard current = Keyboard.current;
return current != null && (((ButtonControl)current.leftCtrlKey).isPressed || ((ButtonControl)current.rightCtrlKey).isPressed) && ((ButtonControl)current.vKey).wasPressedThisFrame;
}
private static bool IsInputTogglePressed()
{
if (_captureInput == null)
{
return false;
}
if (Input.GetKeyDown((KeyCode)283) || Input.GetKeyDown((KeyCode)280))
{
return true;
}
Keyboard current = Keyboard.current;
return current != null && (((ButtonControl)current.f2Key).wasPressedThisFrame || ((ButtonControl)current.pageUpKey).wasPressedThisFrame);
}
private static void SetInputCapture(bool enabled)
{
if (_captureInput != null)
{
_captureInput.Value = enabled;
if (_disableInputElement != null && _disableInputElement.Value == enabled)
{
_disableInputElement.Value = !enabled;
}
MelonPreferences.Save();
if (!enabled)
{
ClearLocalText();
}
MelonLogger.Msg("Keyboard input capture " + (enabled ? "enabled" : "disabled") + ".");
}
}
private static void ClearLocalText()
{
_pendingText = string.Empty;
if (_lastSentText.Length != 0)
{
Publish(string.Empty);
}
}
private static void Publish(string text)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
text = Normalize(text);
_lastSentText = text;
if (NetworkInfo.HasServer)
{
MessageRelay.RelayModule<HeadChatMessage, HeadChatData>(new HeadChatData
{
Text = text
}, CommonMessageRoutes.ReliableToClients);
}
}
private static string Normalize(string text)
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected O, but got Unknown
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
text = text.Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ');
StringBuilder val = new StringBuilder(text.Length);
string text2 = text;
string text3 = text2;
foreach (char c in text3)
{
if (!char.IsControl(c))
{
val.Append(c);
}
}
return TruncateTextElements(((object)val).ToString(), 160);
}
private static int GetTextElementCount(string text)
{
return (!string.IsNullOrEmpty(text)) ? StringInfo.ParseCombiningCharacters(text).Length : 0;
}
private static string TruncateTextElements(string text, int maximum)
{
if (string.IsNullOrEmpty(text) || maximum <= 0)
{
return string.Empty;
}
int[] array = StringInfo.ParseCombiningCharacters(text);
if (array.Length <= maximum)
{
return text;
}
return text.Substring(0, array[maximum]);
}
private static List<string> GetTextElements(string text)
{
List<string> val = new List<string>();
TextElementEnumerator textElementEnumerator = StringInfo.GetTextElementEnumerator(text ?? string.Empty);
while (textElementEnumerator.MoveNext())
{
val.Add(textElementEnumerator.GetTextElement());
}
return val;
}
private static string FormatDisplayText(string text)
{
//IL_015c: 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_004d: Expected O, but got Unknown
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
int textElementCount = GetTextElementCount(text);
if (string.IsNullOrEmpty(text) || textElementCount <= 80)
{
return text ?? string.Empty;
}
int num = ((textElementCount > 120) ? 3 : 2);
StringBuilder val = new StringBuilder(text.Length + num - 1);
List<string> textElements = GetTextElements(text);
for (int num2 = num; num2 > 1; num2--)
{
int target = textElements.Count / num2;
int num3 = FindWordSplit(textElements, target);
for (int i = 0; i < num3; i++)
{
val.Append(textElements[i]);
}
while (val.Length > 0 && val[val.Length - 1] == ' ')
{
int length = val.Length;
val.Length = length - 1;
}
val.Append('\n');
textElements.RemoveRange(0, num3);
while (textElements.Count > 0 && textElements[0] == " ")
{
textElements.RemoveAt(0);
}
}
Enumerator<string> enumerator = textElements.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
string current = enumerator.Current;
val.Append(current);
}
}
finally
{
((System.IDisposable)enumerator).Dispose();
}
return ((object)val).ToString();
}
private static int FindWordSplit(List<string> elements, int target)
{
int num = Math.Max(1, target / 2);
for (int num2 = Math.Min(target, elements.Count - 1); num2 >= num; num2--)
{
if (elements[num2] == " ")
{
return num2;
}
}
for (int i = Math.Min(target, elements.Count - 1); i < elements.Count; i++)
{
if (elements[i] == " ")
{
return i;
}
}
return Math.Min(target, elements.Count - 1);
}
private static void UpdateBubbleAppearance(Bubble bubble)
{
if (bubble == null || bubble.Element == null)
{
return;
}
StabilizeBubbleScale(bubble);
string text = FormatDisplayText(bubble.Text);
if (!string.Equals(bubble.Element.Username, text, (StringComparison)4))
{
bubble.Element.Username = text;
}
TextMeshProUGUI text2 = bubble.Element.Text;
if ((Object)(object)text2 == (Object)null)
{
return;
}
if ((Object)(object)bubble.TextInstance != (Object)(object)text2)
{
bubble.TextInstance = text2;
if (_canonicalNametagFontSize <= 0f)
{
_canonicalNametagFontSize = ((TMP_Text)text2).fontSize;
}
bubble.BaseFontSize = _canonicalNametagFontSize;
}
int textElementCount = GetTextElementCount(bubble.Text);
bool flag = (((TMP_Text)text2).enableWordWrapping = textElementCount > 80);
bool flag3 = flag;
float num = ((textElementCount > 120) ? 0.74f : (flag3 ? 0.82f : 1f));
((TMP_Text)text2).enableAutoSizing = false;
((TMP_Text)text2).fontSize = bubble.BaseFontSize * num;
}
private static void StabilizeBubbleScale(Bubble bubble)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
if (bubble != null && bubble.Element != null && !((Object)(object)bubble.Element.Transform == (Object)null))
{
bubble.Element.Transform.localScale = Vector3.one;
}
}
private static void RestoreBubbleFont(Bubble bubble)
{
if (bubble != null && !((Object)(object)bubble.TextInstance == (Object)null) && !(bubble.BaseFontSize <= 0f))
{
((TMP_Text)bubble.TextInstance).fontSize = bubble.BaseFontSize;
StabilizeBubbleScale(bubble);
}
}
internal static void Receive(byte sender, string text)
{
//IL_007a: 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_0087: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Expected O, but got Unknown
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Expected O, but got Unknown
text = Normalize(text);
NetworkPlayer val = null;
if (text.Length == 0)
{
RemoveBubble(sender);
}
else if (NetworkPlayerManager.TryGetPlayer(sender, ref val) && !val.NetworkEntity.IsOwner)
{
Bubble bubble = null;
if (!Bubbles.TryGetValue(sender, ref bubble) || bubble.Player != val)
{
RemoveBubble(sender);
RigNameTag val2 = new RigNameTag
{
CrownVisible = false,
Color = new Color(0.88f, 0.97f, 1f, 1f),
Visible = true,
Username = FormatDisplayText(text)
};
val.HeadUI.RegisterElement((IPopupLayoutElement)val2);
bubble = new Bubble
{
Player = val,
Element = val2,
Text = text
};
Bubbles[sender] = bubble;
}
else
{
bubble.Text = text;
}
UpdateBubbleAppearance(bubble);
bubble.LastUpdate = Time.unscaledTime;
}
}
private static void OnPlayerLeft(PlayerID player)
{
if (player != null)
{
RemoveBubble(player.SmallID);
}
}
private static void RemoveBubble(byte sender)
{
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
Bubble bubble = null;
if (Bubbles.TryGetValue(sender, ref bubble))
{
RestoreBubbleFont(bubble);
if (bubble.Player != null && bubble.Player.HeadUI != null)
{
bubble.Player.HeadUI.UnregisterElement((IPopupLayoutElement)bubble.Element);
}
Bubbles.Remove(sender);
}
}
private static void ClearAll()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
Enumerator<byte, Bubble> enumerator = Bubbles.Values.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
Bubble current = enumerator.Current;
RestoreBubbleFont(current);
if (current.Player != null && current.Player.HeadUI != null)
{
current.Player.HeadUI.UnregisterElement((IPopupLayoutElement)current.Element);
}
}
}
finally
{
((System.IDisposable)enumerator).Dispose();
}
Bubbles.Clear();
_pendingText = string.Empty;
_lastSentText = string.Empty;
}
}
public sealed class HeadChatData : INetSerializable
{
public string Text = string.Empty;
public void Serialize(INetSerializer serializer)
{
serializer.SerializeValue(ref Text);
}
}
public sealed class HeadChatMessage : ModuleMessageHandler
{
protected override bool OnPreRelayMessage(ReceivedMessage received)
{
return ((ReceivedMessage)(ref received)).Bytes != null && ((ReceivedMessage)(ref received)).Bytes.Length >= 4 && ((ReceivedMessage)(ref received)).Bytes.Length <= 4100;
}
protected override void OnHandleMessage(ReceivedMessage received)
{
if (((ReceivedMessage)(ref received)).Sender.HasValue && ((ReceivedMessage)(ref received)).Bytes != null && ((ReceivedMessage)(ref received)).Bytes.Length >= 4 && ((ReceivedMessage)(ref received)).Bytes.Length <= 4100)
{
HeadChatData headChatData = ((ReceivedMessage)(ref received)).ReadData<HeadChatData>();
RealTimeHeadChatMod.Receive(((ReceivedMessage)(ref received)).Sender.Value, headChatData.Text);
}
}
}
public sealed class HeadChatModule : Module
{
public override string Name => "Real-Time Head Chat";
public override string Author => "Local Utility";
public override Version Version => new Version(1, 5, 0);
public override ConsoleColor Color => (ConsoleColor)11;
protected override void OnModuleRegistered()
{
ModuleMessageManager.RegisterHandler<HeadChatMessage>();
}
}
[HarmonyPatch(typeof(ModuleMessageManager), "PreRelayMessage")]
internal static class ModulePreRelayHostDeliveryPatch
{
private static void Prefix(ReceivedMessage received)
{
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
if (!NetworkInfo.IsHost || !((ReceivedMessage)(ref received)).Sender.HasValue || ((ReceivedMessage)(ref received)).Bytes == null || ((ReceivedMessage)(ref received)).Bytes.Length < 12)
{
return;
}
long? handlerTagByType = ModuleMessageManager.GetHandlerTagByType(typeof(HeadChatMessage));
if (handlerTagByType.HasValue && ReadBigEndianInt64(((ReceivedMessage)(ref received)).Bytes) == handlerTagByType.Value)
{
int num = ((ReceivedMessage)(ref received)).Bytes.Length - 8;
if (num >= 4 && num <= 4100)
{
byte[] array = new byte[num];
Buffer.BlockCopy((System.Array)((ReceivedMessage)(ref received)).Bytes, 8, (System.Array)array, 0, num);
ReceivedMessage val = default(ReceivedMessage);
((ReceivedMessage)(ref val)).Sender = ((ReceivedMessage)(ref received)).Sender;
((ReceivedMessage)(ref val)).Bytes = array;
((ReceivedMessage)(ref val)).IsServerHandled = true;
HeadChatData headChatData = ((ReceivedMessage)(ref val)).ReadData<HeadChatData>();
RealTimeHeadChatMod.Receive(((ReceivedMessage)(ref received)).Sender.Value, headChatData.Text);
}
}
}
private static long ReadBigEndianInt64(byte[] bytes)
{
ulong num = 0uL;
for (int i = 0; i < 8; i++)
{
num = (num << 8) | bytes[i];
}
return (long)num;
}
}
}