using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GMHearDisplay.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("GMHearDisplay")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GMHearDisplay")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("be13cb0f-c894-4d78-b9c2-2c9bb103c566")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace GMHearDisplay
{
[BepInPlugin("dev.closeraven783.gmheardisplay", "GM Hear Display", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class GMHearDisplayMain : BaseUnityPlugin
{
internal static class PluginInfo
{
public const string GUID = "dev.closeraven783.gmheardisplay";
public const string NAME = "GM Hear Display";
public const string VERSION = "1.0.1";
}
internal static ManualLogSource Log;
private readonly Harmony _harmony = new Harmony("dev.closeraven783.gmheardisplay");
private static GMHearDisplayMain Instance;
public static ConfigEntry<bool> ShowVoiceUI { get; private set; }
public static ConfigEntry<float> MaxDistance { get; private set; }
public static ConfigEntry<float> BarelyHearThreshold { get; private set; }
public static ConfigEntry<string> TextColor { get; private set; }
private void Awake()
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
Log = ((BaseUnityPlugin)this).Logger;
SetupBaseConfig();
SetupLethalConfig();
Log.LogInfo((object)"Plugin [dev.closeraven783.gmheardisplay (GM Hear Display) v1.0.1] is loaded!");
GameObject val = new GameObject("GMHearDisplay");
Object.DontDestroyOnLoad((Object)(object)val);
((Object)val).hideFlags = (HideFlags)61;
val.AddComponent<VoiceDisplayRunner>();
}
private void SetupBaseConfig()
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Expected O, but got Unknown
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Expected O, but got Unknown
ShowVoiceUI = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show Voice UI", true, "Enable or disable the voice UI display");
MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Distances", "Max Distance", 45f, new ConfigDescription("Maximum distance players can (probably) hear you normally", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
BarelyHearThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Distances", "Barely Hear Threshold", 47f, new ConfigDescription("Distance threshold for barely hearing", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>()));
TextColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "Text Color", "#FFFFFF", "Hex color of the UI text (e.g., #FF0000 for red)");
}
private void SetupLethalConfig()
{
try
{
Type type = Type.GetType("LethalConfig.LethalConfigManager, LethalConfig");
if (type != null)
{
Log.LogInfo((object)"LethalConfig found, adding config entries...");
AddLethalConfigEntries(type);
}
}
catch (Exception ex)
{
Log.LogWarning((object)("Could not initialize LethalConfig: " + ex.Message));
}
}
private void AddLethalConfigEntries(Type lethalConfigManager)
{
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Expected O, but got Unknown
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Expected O, but got Unknown
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Expected O, but got Unknown
//IL_00f6: Expected O, but got Unknown
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Expected O, but got Unknown
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Expected O, but got Unknown
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Expected O, but got Unknown
//IL_0127: Expected O, but got Unknown
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Expected O, but got Unknown
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Expected O, but got Unknown
try
{
Assembly assembly = null;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly2 in assemblies)
{
if (assembly2.GetName().Name == "LethalConfig")
{
assembly = assembly2;
break;
}
}
if (assembly == null)
{
Log.LogError((object)"Could not find LethalConfig assembly!");
return;
}
Type type = assembly.GetType("LethalConfig.ConfigItems.BaseConfigItem");
if (type == null)
{
Log.LogError((object)"Could not find BaseConfigItem type!");
return;
}
MethodInfo method = lethalConfigManager.GetMethod("AddConfigItem", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { type }, null);
BoolCheckBoxConfigItem val = new BoolCheckBoxConfigItem(ShowVoiceUI, false);
ConfigEntry<float> maxDistance = MaxDistance;
FloatSliderOptions val2 = new FloatSliderOptions
{
RequiresRestart = false
};
((BaseRangeOptions<float>)val2).Min = 0f;
((BaseRangeOptions<float>)val2).Max = 100f;
FloatSliderConfigItem val3 = new FloatSliderConfigItem(maxDistance, val2);
ConfigEntry<float> barelyHearThreshold = BarelyHearThreshold;
FloatSliderOptions val4 = new FloatSliderOptions
{
RequiresRestart = false
};
((BaseRangeOptions<float>)val4).Min = 100f;
((BaseRangeOptions<float>)val4).Max = 120f;
FloatSliderConfigItem val5 = new FloatSliderConfigItem(barelyHearThreshold, val4);
HexColorInputFieldConfigItem val6 = new HexColorInputFieldConfigItem(TextColor, false);
Log.LogInfo((object)"LethalConfig entires added");
}
catch (Exception arg)
{
Log.LogError((object)$"Failed to add LethalConfig entries: {arg}");
}
}
}
}
namespace GMHearDisplay.Patches
{
public class VoiceDisplayUI
{
private static readonly Dictionary<PlayerControllerB, AudioSource> playerVoiceAudios = new Dictionary<PlayerControllerB, AudioSource>();
private static PlayerControllerB localPlayer;
private static AudioSource localVoiceAudio;
private static float nextSearchTime = 0f;
private static Texture2D voiceIconTexture;
private static Color ParseHexColor(string hex)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
Color result = default(Color);
if (ColorUtility.TryParseHtmlString(hex, ref result))
{
return result;
}
return Color.white;
}
public static void LoadVoiceIcon()
{
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Expected O, but got Unknown
try
{
string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string text = Path.Combine(directoryName, "Assets", "voice_icon.png");
if (!File.Exists(text))
{
text = Path.Combine(directoryName, "voice_icon.png");
if (!File.Exists(text))
{
GMHearDisplayMain.Log.LogError((object)("Voice icon not found at either:\n" + Path.Combine(directoryName, "Assets", "voice_icon.png") + "\nor\n" + text));
return;
}
GMHearDisplayMain.Log.LogInfo((object)("Voice icon found at root: " + text));
}
byte[] array = File.ReadAllBytes(text);
voiceIconTexture = new Texture2D(3, 2);
if (ImageConversion.LoadImage(voiceIconTexture, array))
{
((Texture)voiceIconTexture).filterMode = (FilterMode)0;
}
else
{
GMHearDisplayMain.Log.LogError((object)"Failed to load image data into texture");
}
}
catch (Exception arg)
{
GMHearDisplayMain.Log.LogError((object)$"Failed to load voice icon: {arg}");
}
}
private static void UpdateVoiceInfo()
{
if (Time.time < nextSearchTime)
{
return;
}
nextSearchTime = Time.time + 1.5f;
playerVoiceAudios.Clear();
PlayerControllerB[] array = Object.FindObjectsOfType<PlayerControllerB>();
localPlayer = StartOfRound.Instance?.localPlayerController;
PlayerControllerB[] array2 = array;
foreach (PlayerControllerB val in array2)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
AudioSource[] componentsInChildren = ((Component)val).GetComponentsInChildren<AudioSource>();
AudioSource[] array3 = componentsInChildren;
foreach (AudioSource val2 in array3)
{
if ((Object)(object)val2 != (Object)null && val2.spatialBlend > 0.5f)
{
playerVoiceAudios[val] = val2;
if ((Object)(object)val == (Object)(object)localPlayer)
{
localVoiceAudio = val2;
}
break;
}
}
}
}
public static void OnGUI()
{
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Expected O, but got Unknown
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: 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_0081: Expected O, but got Unknown
//IL_0095: 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_00a6: 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_01a0: Unknown result type (might be due to invalid IL or missing references)
//IL_0440: Unknown result type (might be due to invalid IL or missing references)
//IL_041e: Unknown result type (might be due to invalid IL or missing references)
//IL_0429: Unknown result type (might be due to invalid IL or missing references)
//IL_042e: Unknown result type (might be due to invalid IL or missing references)
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
//IL_027c: Unknown result type (might be due to invalid IL or missing references)
//IL_0281: Unknown result type (might be due to invalid IL or missing references)
//IL_028c: Expected O, but got Unknown
//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
//IL_0240: Unknown result type (might be due to invalid IL or missing references)
//IL_036b: Unknown result type (might be due to invalid IL or missing references)
//IL_0372: Expected O, but got Unknown
//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
//IL_032f: Unknown result type (might be due to invalid IL or missing references)
if (!GMHearDisplayMain.ShowVoiceUI.Value)
{
return;
}
UpdateVoiceInfo();
if ((Object)(object)localPlayer != (Object)null && localPlayer.isPlayerDead)
{
return;
}
if ((Object)(object)localPlayer == (Object)null || (Object)(object)localVoiceAudio == (Object)null)
{
GUIStyle style = new GUIStyle(GUI.skin.label)
{
fontSize = 16
};
DrawTextWithStroke(new Rect(20f, 20f, 250f, 30f), "waiting for voice :3", style, Color.white, Color.black, 1f);
return;
}
GUIStyle style2 = new GUIStyle(GUI.skin.label)
{
fontSize = 20
};
Rect rect = default(Rect);
((Rect)(ref rect))..ctor(20f, 500f, 300f, 50f);
DrawTextWithStroke(rect, "Who can hear you:", style2, ParseHexColor(GMHearDisplayMain.TextColor.Value), Color.black, 1.5f);
float value = GMHearDisplayMain.MaxDistance.Value;
float value2 = GMHearDisplayMain.BarelyHearThreshold.Value;
Vector3 position = ((Component)localPlayer).transform.position;
float num = 550f;
bool flag = false;
bool flag2 = false;
Rect val = default(Rect);
Rect rect2 = default(Rect);
Rect val2 = default(Rect);
Rect rect3 = default(Rect);
foreach (KeyValuePair<PlayerControllerB, AudioSource> playerVoiceAudio in playerVoiceAudios)
{
PlayerControllerB key = playerVoiceAudio.Key;
if ((Object)(object)key == (Object)null || (Object)(object)key == (Object)(object)localPlayer)
{
continue;
}
float num2 = Vector3.Distance(((Component)key).transform.position, position);
bool flag3 = key.voicePlayerState != null && key.voicePlayerState.IsSpeaking;
if (num2 <= value)
{
flag = true;
float num3 = Mathf.Clamp01(num2 / value);
Color textColor = Color.Lerp(Color.green, Color.red, num3 * num3);
float num4 = 0f;
if (flag3 && (Object)(object)voiceIconTexture != (Object)null)
{
float num5 = 24f;
float num6 = 20f;
float num7 = num + 5f;
((Rect)(ref val))..ctor(num6, num7, num5, num5);
GUI.DrawTexture(val, (Texture)(object)voiceIconTexture);
num4 = num5 + 5f;
}
string text = $"{key.playerUsername} ({num2:F1}m)";
GUIStyle style3 = new GUIStyle(GUI.skin.label)
{
fontSize = 18
};
((Rect)(ref rect2))..ctor(20f + num4, num, 300f - num4, 35f);
DrawTextWithStroke(rect2, text, style3, textColor, Color.black, 1f, eightWayStroke: true);
num += 35f;
}
else if (num2 <= value2)
{
flag2 = true;
float num8 = 0f;
if (flag3 && (Object)(object)voiceIconTexture != (Object)null)
{
float num9 = 24f;
float num10 = 20f;
float num11 = num + 7f;
((Rect)(ref val2))..ctor(num10, num11, num9, num9);
GUI.DrawTexture(val2, (Texture)(object)voiceIconTexture);
num8 = num9 + 5f;
}
string text2 = $"{key.playerUsername} might be barely hearing you ({num2:F1}m)";
GUIStyle val3 = new GUIStyle(GUI.skin.label);
val3.fontSize = 18;
((Rect)(ref rect3))..ctor(20f + num8, num + 2f, 300f - num8, 30f);
DrawTextWithStroke(rect3, text2, val3, Color.yellow, new Color(0.2f, 0.2f, 0f));
num += 35f;
}
}
if (!flag && !flag2)
{
DrawTextWithStroke(new Rect(20f, num + 3f, 300f, 25f), "No one", style2, Color.gray, Color.black, 1f);
}
GUI.color = Color.white;
}
private static void DrawTextWithStroke(Rect rect, string text, GUIStyle style, Color textColor, Color strokeColor, float strokeThickness = 2f, bool eightWayStroke = false)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: 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_0128: 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_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_0173: 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)
Color color = GUI.color;
GUI.color = strokeColor;
if (eightWayStroke)
{
Rect val = default(Rect);
for (float num = 0f - strokeThickness; num <= strokeThickness; num += strokeThickness)
{
for (float num2 = 0f - strokeThickness; num2 <= strokeThickness; num2 += strokeThickness)
{
if (num != 0f || num2 != 0f)
{
((Rect)(ref val))..ctor(((Rect)(ref rect)).x + num, ((Rect)(ref rect)).y + num2, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height);
GUI.Label(val, text, style);
}
}
}
}
else
{
Rect val2 = default(Rect);
((Rect)(ref val2))..ctor(((Rect)(ref rect)).x - strokeThickness, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height);
GUI.Label(val2, text, style);
((Rect)(ref val2))..ctor(((Rect)(ref rect)).x + strokeThickness, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height);
GUI.Label(val2, text, style);
((Rect)(ref val2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y - strokeThickness, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height);
GUI.Label(val2, text, style);
((Rect)(ref val2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + strokeThickness, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height);
GUI.Label(val2, text, style);
}
GUI.color = textColor;
GUI.Label(rect, text, style);
GUI.color = color;
}
}
public class VoiceDisplayRunner : MonoBehaviour
{
private void Start()
{
VoiceDisplayUI.LoadVoiceIcon();
}
private void OnGUI()
{
VoiceDisplayUI.OnGUI();
}
}
}