Decompiled source of BellLock v0.1.1

plugins/StonewardsBellControl.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.UIElements;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("StonewardsBellControl")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+98b6bcea1ab12d0bb38114ebfd44361a09dcd72a")]
[assembly: AssemblyProduct("StonewardsBellControl")]
[assembly: AssemblyTitle("StonewardsBellControl")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 StonewardsBellControl
{
	internal enum PanelCorner
	{
		TopRight,
		TopLeft,
		BottomRight,
		BottomLeft
	}
	internal sealed class BellControlPanel : IDisposable
	{
		private const float Width = 248f;

		private const float Height = 42f;

		private const float EdgePadding = 6f;

		private const float GoldSafeTopPixels = 200f;

		private static readonly Color PanelFrame = new Color(0.72f, 0.64f, 0.48f, 1f);

		private static readonly Color PanelBackground = new Color(0.07450981f, 0.09019608f, 0.1254902f, 0.97f);

		private static readonly Color PanelHeader = new Color(0.29803923f, 0.18039216f, 16f / 85f, 1f);

		private static readonly Color Divider = new Color(0.71f, 0.63f, 0.48f, 0.38f);

		private static readonly Color Heading = new Color(0.98f, 0.79f, 0.51f, 1f);

		private static readonly Color TextColor = new Color(0.98f, 0.9f, 0.76f, 1f);

		private static readonly Color Green = new Color(0.47f, 0.87f, 0.56f, 1f);

		private static readonly Color Red = new Color(1f, 0.52f, 0.5f, 1f);

		private static readonly Color ButtonFill = new Color(11f / 51f, 0.23137255f, 23f / 85f, 1f);

		private static readonly Color ButtonActive = new Color(0.30980393f, 19f / 85f, 0.23921569f, 1f);

		private static readonly Color ButtonOutline = new Color(0.69803923f, 0.6f, 19f / 51f, 1f);

		private readonly Action _onToggle;

		private readonly GameObject _canvasObject;

		private readonly Canvas _canvas;

		private readonly GraphicRaycaster _raycaster;

		private readonly RectTransform _panelRoot;

		private readonly List<Text> _labels = new List<Text>();

		private readonly Text _statusText;

		private readonly Text _actionText;

		private readonly Text _shortcutText;

		private readonly Image _statusBar;

		private readonly Image _buttonFill;

		private readonly Image _buttonFillCenter;

		private float _nextFontCheck;

		private bool _gameFontApplied;

		private bool _visible;

		public bool IsAlive
		{
			get
			{
				if ((Object)(object)_canvasObject != (Object)null)
				{
					return (Object)(object)_panelRoot != (Object)null;
				}
				return false;
			}
		}

		public BellControlPanel(Action onToggle)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Expected O, but got Unknown
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			_onToggle = onToggle ?? throw new ArgumentNullException("onToggle");
			_canvasObject = new GameObject("Stonewards Bell Control Canvas", new Type[1] { typeof(RectTransform) });
			Object.DontDestroyOnLoad((Object)(object)_canvasObject);
			_canvas = _canvasObject.AddComponent<Canvas>();
			((Behaviour)_canvas).enabled = false;
			_visible = false;
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 110;
			CanvasScaler obj = _canvasObject.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			obj.screenMatchMode = (ScreenMatchMode)0;
			obj.matchWidthOrHeight = 0.5f;
			_raycaster = _canvasObject.AddComponent<GraphicRaycaster>();
			((Behaviour)_raycaster).enabled = false;
			Font val = FindGameFont();
			_gameFontApplied = (Object)(object)val != (Object)null;
			if ((Object)(object)val == (Object)null)
			{
				val = GetFallbackFont();
			}
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("Neither a Stonewards font nor a fallback UI font was found.");
			}
			_panelRoot = CreateRect("Bell Control Window", _canvasObject.transform, 0f, 0f, 248f, 42f);
			AddSolid("Background Top", (Transform)(object)_panelRoot, 7f, 0f, 234f, 42f, PanelBackground);
			AddSolid("Background Center", (Transform)(object)_panelRoot, 0f, 7f, 248f, 28f, PanelBackground);
			AddSolid("Burgundy Title Top", (Transform)(object)_panelRoot, 7f, 2f, 51f, 38f, PanelHeader);
			AddSolid("Burgundy Title Center", (Transform)(object)_panelRoot, 2f, 7f, 56f, 28f, PanelHeader);
			DrawWindowFrame((Transform)(object)_panelRoot, 248f, 42f);
			AddText("Title", (Transform)(object)_panelRoot, val, "BELL", 17, bold: true, Heading, (TextAnchor)3, 12f, 5f, 42f, 31f);
			AddSolid("Divider", (Transform)(object)_panelRoot, 59f, 10f, 1f, 22f, Divider);
			_statusBar = AddSolid("Status Accent", (Transform)(object)_panelRoot, 65f, 13f, 3f, 17f, Red);
			_statusText = AddText("Status", (Transform)(object)_panelRoot, val, "BLOCKED", 16, bold: true, Red, (TextAnchor)4, 71f, 5f, 69f, 31f);
			RectTransform val2 = CreateRect("Guest Permission Button", (Transform)(object)_panelRoot, 147f, 7f, 92f, 28f);
			_buttonFill = AddSolid("Button Top", (Transform)(object)val2, 3f, 0f, 86f, 28f, ButtonFill);
			_buttonFillCenter = AddSolid("Button Center", (Transform)(object)val2, 0f, 3f, 92f, 22f, ButtonFill);
			DrawButtonFrame((Transform)(object)val2, 92f, 28f);
			((Graphic)_buttonFill).raycastTarget = true;
			Button obj2 = ((Component)val2).gameObject.AddComponent<Button>();
			((Selectable)obj2).targetGraphic = (Graphic)(object)_buttonFill;
			((Selectable)obj2).transition = (Transition)0;
			((UnityEvent)obj2.onClick).AddListener(new UnityAction(OnButtonClicked));
			_actionText = AddText("Action", (Transform)(object)val2, val, "ALLOW", 16, bold: true, TextColor, (TextAnchor)4, 5f, 0f, 57f, 28f);
			_shortcutText = AddText("Shortcut", (Transform)(object)val2, val, "F8", 12, bold: true, Heading, (TextAnchor)4, 64f, 0f, 22f, 28f);
		}

		public void SetAllowed(bool allowed)
		{
			//IL_0033: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (IsAlive)
			{
				_statusText.text = (allowed ? "ALLOWED" : "BLOCKED");
				((Graphic)_statusText).color = (allowed ? Green : Red);
				((Graphic)_statusBar).color = (allowed ? Green : Red);
				_actionText.text = (allowed ? "BLOCK" : "ALLOW");
				_shortcutText.text = "F8";
				((Graphic)_buttonFill).color = (allowed ? ButtonActive : ButtonFill);
				((Graphic)_buttonFillCenter).color = (allowed ? ButtonActive : ButtonFill);
			}
		}

		public void SetVisible(bool visible)
		{
			if (IsAlive)
			{
				if (_visible != visible)
				{
					_visible = visible;
					((Behaviour)_canvas).enabled = visible;
				}
				if (!visible)
				{
					((Behaviour)_raycaster).enabled = false;
				}
			}
		}

		public void UpdateInteraction()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			if (!IsAlive)
			{
				return;
			}
			((Behaviour)_raycaster).enabled = _visible && Cursor.visible && (int)Cursor.lockState == 0;
			if (_gameFontApplied || !(Time.unscaledTime >= _nextFontCheck))
			{
				return;
			}
			_nextFontCheck = Time.unscaledTime + 4f;
			Font val = FindGameFont();
			if (!((Object)(object)val != (Object)null))
			{
				return;
			}
			for (int i = 0; i < _labels.Count; i++)
			{
				if ((Object)(object)_labels[i] != (Object)null)
				{
					_labels[i].font = val;
					_labels[i].fontStyle = (FontStyle)0;
				}
			}
			_gameFontApplied = true;
		}

		public void UpdateLayout(PanelCorner corner, int horizontalOffset, int verticalOffset, float requestedScale, bool avoidGoldCounter, Rect? emoteBadge, int emoteGapPixels, int emoteHorizontalAdjustPixels)
		{
			//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)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			if (IsAlive)
			{
				float num = Mathf.Max(0.01f, Mathf.Sqrt(Mathf.Max(0.01f, (float)Screen.width / 1920f) * Mathf.Max(0.01f, (float)Screen.height / 1080f)));
				float num2 = (float)Screen.width / num;
				float num3 = (float)Screen.height / num;
				float num4 = Mathf.Clamp(requestedScale, 0.65f, 1.6f);
				num4 = Mathf.Min(new float[3]
				{
					num4,
					Mathf.Max(0.1f, (num2 - 12f) / 248f),
					Mathf.Max(0.1f, (num3 - 12f) / 42f)
				});
				((Transform)_panelRoot).localScale = Vector3.one * num4;
				bool flag = corner == PanelCorner.TopRight || corner == PanelCorner.BottomRight;
				bool flag2 = corner == PanelCorner.TopRight || corner == PanelCorner.TopLeft;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(flag ? 1f : 0f, flag2 ? 1f : 0f);
				_panelRoot.anchorMin = val;
				_panelRoot.anchorMax = val;
				_panelRoot.pivot = val;
				float num5 = Mathf.Max(6f, num2 - 248f * num4 - 6f);
				float num6 = Mathf.Max(6f, num3 - 42f * num4 - 6f);
				if (corner == PanelCorner.BottomLeft && emoteBadge.HasValue)
				{
					Rect value = emoteBadge.Value;
					float num7 = ((Rect)(ref value)).xMin + (float)emoteHorizontalAdjustPixels;
					float num8 = (float)Screen.height - ((Rect)(ref value)).yMin + (float)Mathf.Max(0, emoteGapPixels);
					float num9 = Mathf.Clamp(num7 / num, 6f, num5);
					float num10 = Mathf.Clamp(num8 / num, 6f, num6);
					_panelRoot.anchoredPosition = new Vector2(num9, num10);
				}
				else
				{
					float num11 = Mathf.Clamp((float)Mathf.Max(0, horizontalOffset), 6f, num5);
					float num12 = ((flag2 && flag && avoidGoldCounter) ? 200f : (6f * num));
					float num13 = Mathf.Clamp(Mathf.Max((float)Mathf.Max(0, verticalOffset), num12) / num, 6f, num6);
					_panelRoot.anchoredPosition = new Vector2(flag ? (0f - num11) : num11, flag2 ? (0f - num13) : num13);
				}
			}
		}

		private static void DrawWindowFrame(Transform parent, float w, float h)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			AddSolid("Frame Top", parent, 8f, 0f, w - 16f, 2f, PanelFrame);
			AddSolid("Top Left Step 1", parent, 5f, 2f, 3f, 2f, PanelFrame);
			AddSolid("Top Left Step 2", parent, 2f, 5f, 3f, 2f, PanelFrame);
			AddSolid("Left Border", parent, 0f, 7f, 2f, h - 14f, PanelFrame);
			AddSolid("Top Right Step 1", parent, w - 8f, 2f, 3f, 2f, PanelFrame);
			AddSolid("Top Right Step 2", parent, w - 5f, 5f, 3f, 2f, PanelFrame);
			AddSolid("Right Border", parent, w - 2f, 7f, 2f, h - 14f, PanelFrame);
			AddSolid("Bottom Border", parent, 8f, h - 2f, w - 16f, 2f, PanelFrame);
			AddSolid("Bottom Left Step 1", parent, 5f, h - 4f, 3f, 2f, PanelFrame);
			AddSolid("Bottom Left Step 2", parent, 2f, h - 7f, 3f, 2f, PanelFrame);
			AddSolid("Bottom Right Step 1", parent, w - 8f, h - 4f, 3f, 2f, PanelFrame);
			AddSolid("Bottom Right Step 2", parent, w - 5f, h - 7f, 3f, 2f, PanelFrame);
		}

		private static void DrawButtonFrame(Transform parent, float w, float h)
		{
			//IL_001c: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			AddSolid("Button Top Frame", parent, 4f, 0f, w - 8f, 2f, ButtonOutline);
			AddSolid("Button Bottom Frame", parent, 4f, h - 2f, w - 8f, 2f, ButtonOutline);
			AddSolid("Button Left Frame", parent, 0f, 4f, 2f, h - 8f, ButtonOutline);
			AddSolid("Button Right Frame", parent, w - 2f, 4f, 2f, h - 8f, ButtonOutline);
			AddSolid("Button TL", parent, 2f, 2f, 2f, 2f, ButtonOutline);
			AddSolid("Button TR", parent, w - 4f, 2f, 2f, 2f, ButtonOutline);
			AddSolid("Button BL", parent, 2f, h - 4f, 2f, 2f, ButtonOutline);
			AddSolid("Button BR", parent, w - 4f, h - 4f, 2f, 2f, ButtonOutline);
		}

		private static RectTransform CreateRect(string name, Transform parent, float x, float y, float w, float h)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(0f, 1f);
			component.pivot = new Vector2(0f, 1f);
			component.anchoredPosition = new Vector2(x, 0f - y);
			component.sizeDelta = new Vector2(w, h);
			return component;
		}

		private static Image AddSolid(string name, Transform parent, float x, float y, float w, float h, Color color)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)CreateRect(name, parent, x, y, w, h)).gameObject.AddComponent<Image>();
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private Text AddText(string name, Transform parent, Font font, string value, int size, bool bold, Color color, TextAnchor align, float x, float y, float w, float h)
		{
			//IL_0053: 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)
			Text val = ((Component)CreateRect(name, parent, x, y, w, h)).gameObject.AddComponent<Text>();
			val.text = value;
			val.font = font;
			val.fontSize = size;
			val.fontStyle = (FontStyle)((bold && !((Object)font).name.StartsWith("Jersey10", StringComparison.OrdinalIgnoreCase)) ? 1 : 0);
			((Graphic)val).color = color;
			val.alignment = align;
			val.horizontalOverflow = (HorizontalWrapMode)1;
			val.verticalOverflow = (VerticalWrapMode)0;
			((Graphic)val).raycastTarget = false;
			_labels.Add(val);
			return val;
		}

		private static Font FindGameFont()
		{
			Font result = null;
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			foreach (Font val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (((Object)val).name == "Jersey10-Regular")
					{
						return val;
					}
					if (((Object)val).name == "Jersey10-Regular2")
					{
						result = val;
					}
				}
			}
			return result;
		}

		private static Font GetFallbackFont()
		{
			try
			{
				Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf");
				if ((Object)(object)builtinResource != (Object)null)
				{
					return builtinResource;
				}
			}
			catch (Exception)
			{
			}
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			foreach (Font val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private void OnButtonClicked()
		{
			_onToggle();
		}

		public void Dispose()
		{
			if ((Object)(object)_canvasObject != (Object)null)
			{
				Object.Destroy((Object)(object)_canvasObject);
			}
		}
	}
	[BepInPlugin("dev.stonewards.bellcontrol", "Stonewards Bell Lock", "0.1.1")]
	public sealed class BellControlPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Bell), "InvokeUserCode_CmdTryRing__FirstPersonController")]
		private static class GuestBellRingPatch
		{
			[HarmonyPrefix]
			private static bool Prefix()
			{
				if (NetworkServer.active)
				{
					return GuestsMayRing;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Bell), "InvokeUserCode_CmdStartAnimation__FirstPersonController")]
		private static class GuestBellAnimationPatch
		{
			[HarmonyPrefix]
			private static bool Prefix()
			{
				if (NetworkServer.active)
				{
					return GuestsMayRing;
				}
				return true;
			}
		}

		public const string PluginGuid = "dev.stonewards.bellcontrol";

		public const string PluginName = "Stonewards Bell Lock";

		public const string PluginVersion = "0.1.1";

		private ConfigEntry<PanelCorner> _anchor;

		private ConfigEntry<int> _horizontalOffset;

		private ConfigEntry<int> _verticalOffset;

		private ConfigEntry<float> _scale;

		private ConfigEntry<bool> _avoidGoldCounter;

		private ConfigEntry<int> _layoutRevision;

		private ConfigEntry<bool> _followEmote;

		private ConfigEntry<int> _emoteGap;

		private ConfigEntry<int> _emoteHorizontalAdjust;

		private EmoteHudAnchor _emoteHud;

		private Harmony _harmony;

		private BellControlPanel _panel;

		private bool _wasHosting;

		private bool _uiUnavailable;

		private bool _anchorFallbackLocked;

		private bool _anchorSearchStarted;

		private float _anchorDeadline;

		private const float AnchorResolutionTimeout = 8f;

		internal static bool GuestsMayRing { get; private set; }

		private static bool IsHost
		{
			get
			{
				if (NetworkServer.active)
				{
					return NetworkClient.active;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			_anchor = ((BaseUnityPlugin)this).Config.Bind<PanelCorner>("Appearance", "Anchor", PanelCorner.BottomLeft, "Corner used to position the Bell Control panel.");
			_horizontalOffset = ((BaseUnityPlugin)this).Config.Bind<int>("Appearance", "HorizontalOffset", 26, "Distance inward from the selected left/right screen edge, in reference-resolution units.");
			_verticalOffset = ((BaseUnityPlugin)this).Config.Bind<int>("Appearance", "VerticalOffset", 80, "Distance inward from the selected top/bottom screen edge, in actual screen pixels.");
			_avoidGoldCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "AvoidGoldCounter", true, "Keep the Bell Control panel below the native gold counter when anchored TopRight.");
			_followEmote = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "FollowEmote", true, "Follow the in-game Emote shortcut. Panel stays hidden until positioned, avoiding startup jumps.");
			_emoteGap = ((BaseUnityPlugin)this).Config.Bind<int>("Appearance", "EmoteGap", 12, "Gap in physical pixels between Emote and the Bell Control panel.");
			_emoteHorizontalAdjust = ((BaseUnityPlugin)this).Config.Bind<int>("Appearance", "EmoteHorizontalAdjust", 0, "Horizontal adjustment in physical pixels relative to the Emote shortcut.");
			_emoteHud = new EmoteHudAnchor(((BaseUnityPlugin)this).Logger);
			_scale = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Scale", 1f, new ConfigDescription("Panel scale (0.65 - 1.60).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.65f, 1.6f), Array.Empty<object>()));
			_layoutRevision = ((BaseUnityPlugin)this).Config.Bind<int>("Appearance", "LayoutRevision", 0, "Internal layout migration version. Leave unchanged.");
			MigrateLayoutIfNeeded();
			GuestsMayRing = false;
			_harmony = new Harmony("dev.stonewards.bellcontrol");
			_harmony.PatchAll(typeof(BellControlPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Bell Lock loaded. Host: F8 toggles guest access to the bell.");
		}

		private void MigrateLayoutIfNeeded()
		{
			if (_layoutRevision.Value >= 4)
			{
				return;
			}
			if (_layoutRevision.Value < 2)
			{
				if (_anchor.Value == PanelCorner.TopRight && _horizontalOffset.Value == 14 && (_verticalOffset.Value == 80 || _verticalOffset.Value == 132 || _verticalOffset.Value == 200))
				{
					_anchor.Value = PanelCorner.BottomLeft;
					_horizontalOffset.Value = 26;
					_verticalOffset.Value = 80;
				}
				else if (_anchor.Value == PanelCorner.BottomLeft && _horizontalOffset.Value == 14 && _verticalOffset.Value == 146)
				{
					_horizontalOffset.Value = 26;
					_verticalOffset.Value = 80;
				}
			}
			if (_anchor.Value == PanelCorner.BottomLeft && _emoteGap.Value == 12 && _emoteHorizontalAdjust.Value == 0)
			{
				bool num = _horizontalOffset.Value == 14 && _verticalOffset.Value == 92;
				bool flag = _horizontalOffset.Value == 26 && _verticalOffset.Value == 80;
				if (num || flag)
				{
					_followEmote.Value = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Restored automatic Emote positioning with no visible startup jump.");
				}
			}
			_layoutRevision.Value = 4;
			((BaseUnityPlugin)this).Config.Save();
		}

		private void Update()
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Invalid comparison between Unknown and I4
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Invalid comparison between Unknown and I4
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			bool isHost = IsHost;
			if (isHost != _wasHosting)
			{
				GuestsMayRing = false;
				_wasHosting = isHost;
				_uiUnavailable = false;
				_anchorFallbackLocked = false;
				_anchorSearchStarted = false;
				_anchorDeadline = 0f;
				if (_emoteHud != null)
				{
					_emoteHud.Reset();
				}
				DisposePanel();
			}
			if (!isHost)
			{
				return;
			}
			if (Keyboard.current != null && ((ButtonControl)Keyboard.current.f8Key).wasPressedThisFrame)
			{
				ToggleGuests();
			}
			if (_panel == null || !_panel.IsAlive)
			{
				if (_uiUnavailable)
				{
					return;
				}
				try
				{
					_panel = new BellControlPanel(ToggleGuests);
					_panel.SetAllowed(GuestsMayRing);
				}
				catch (Exception ex)
				{
					_uiUnavailable = true;
					DisposePanel();
					((BaseUnityPlugin)this).Logger.LogError((object)("Failed to create Bell Lock Canvas; F8 still works: " + ex));
					return;
				}
			}
			bool num = _followEmote.Value && _anchor.Value == PanelCorner.BottomLeft;
			Rect? emoteBadge = null;
			bool flag = true;
			if (num && !_anchorFallbackLocked)
			{
				InputManager instance = InputManager.Instance;
				if ((!((Object)(object)instance != (Object)null) || ((int)instance.CurrentInputState != 2 && (int)instance.CurrentInputState != 4)) && !_anchorSearchStarted)
				{
					flag = false;
				}
				else
				{
					if (!_anchorSearchStarted)
					{
						_anchorSearchStarted = true;
						_anchorDeadline = Time.unscaledTime + 8f;
					}
					_emoteHud.RefreshIfNeeded();
					if (_emoteHud.TryGetBadgeBounds(out var bounds))
					{
						emoteBadge = bounds;
					}
					else if (_emoteHud.WasLocated)
					{
						flag = false;
					}
					else if (Time.unscaledTime < _anchorDeadline)
					{
						flag = false;
					}
					else
					{
						_anchorFallbackLocked = true;
						((BaseUnityPlugin)this).Logger.LogWarning((object)"Emote HUD unavailable; using fixed layout for this session.");
					}
				}
			}
			if (flag)
			{
				_panel.UpdateLayout(_anchor.Value, _horizontalOffset.Value, _verticalOffset.Value, _scale.Value, _avoidGoldCounter.Value, emoteBadge, _emoteGap.Value, _emoteHorizontalAdjust.Value);
			}
			_panel.SetVisible(flag);
			_panel.UpdateInteraction();
		}

		private void ToggleGuests()
		{
			if (IsHost)
			{
				GuestsMayRing = !GuestsMayRing;
				if (_panel != null && _panel.IsAlive)
				{
					_panel.SetAllowed(GuestsMayRing);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)(GuestsMayRing ? "Guest access to the bell ENABLED." : "Guest access to the bell DISABLED."));
			}
		}

		private void DisposePanel()
		{
			if (_panel != null)
			{
				_panel.Dispose();
				_panel = null;
			}
		}

		private void OnDestroy()
		{
			DisposePanel();
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class EmoteHudAnchor
	{
		private const float InitialRetrySeconds = 0.35f;

		private const float LaterRetrySeconds = 1f;

		private const int MaxProbesPerHostSession = 16;

		private const int MaxVisitedElements = 4000;

		private readonly ManualLogSource _log;

		private TextElement _emoteLabel;

		private VisualElement _emoteBadge;

		private int _probeCount;

		private float _nextProbe;

		private bool _loggedFallback;

		private bool _everLocated;

		public bool WasLocated => _everLocated;

		public EmoteHudAnchor(ManualLogSource log)
		{
			_log = log;
		}

		public void Reset()
		{
			_emoteLabel = null;
			_emoteBadge = null;
			_probeCount = 0;
			_nextProbe = 0f;
			_loggedFallback = false;
			_everLocated = false;
		}

		public void RefreshIfNeeded()
		{
			if (_emoteLabel == null || ((VisualElement)_emoteLabel).panel == null)
			{
				_emoteLabel = null;
				_emoteBadge = null;
				if (_probeCount < 16 && !(Time.unscaledTime < _nextProbe))
				{
					_probeCount++;
					_nextProbe = Time.unscaledTime + ((_probeCount <= 6) ? 0.35f : 1f);
					Discover();
				}
			}
		}

		public bool TryGetBadgeBounds(out Rect bounds)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Rect);
			if (_emoteLabel == null || ((VisualElement)_emoteLabel).panel == null)
			{
				return false;
			}
			try
			{
				if (!IsVisible((VisualElement)(object)_emoteLabel))
				{
					return false;
				}
				if (!TryGetScreenRect((VisualElement)((_emoteBadge != null && _emoteBadge.panel == ((VisualElement)_emoteLabel).panel) ? ((object)_emoteBadge) : ((object)_emoteLabel)), out bounds))
				{
					return false;
				}
				return ((Rect)(ref bounds)).width > 1f && ((Rect)(ref bounds)).height > 1f;
			}
			catch (Exception)
			{
				_emoteLabel = null;
				_emoteBadge = null;
				return false;
			}
		}

		private void Discover()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			try
			{
				UIDocument[] array = Resources.FindObjectsOfTypeAll<UIDocument>();
				TextElement val = null;
				VisualElement val2 = null;
				float num = float.NegativeInfinity;
				int num2 = 4000;
				UIDocument[] array2 = array;
				foreach (UIDocument val3 in array2)
				{
					if (num2 <= 0)
					{
						break;
					}
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					Scene scene = ((Component)val3).gameObject.scene;
					if (!((Scene)(ref scene)).IsValid() || !((Component)val3).gameObject.activeInHierarchy)
					{
						continue;
					}
					VisualElement rootVisualElement = val3.rootVisualElement;
					if (rootVisualElement == null || rootVisualElement.panel == null)
					{
						continue;
					}
					Stack<VisualElement> stack = new Stack<VisualElement>();
					stack.Push(rootVisualElement);
					while (stack.Count != 0 && num2-- > 0)
					{
						VisualElement val4 = stack.Pop();
						TextElement val5 = (TextElement)(object)((val4 is TextElement) ? val4 : null);
						if (val5 != null && IsEmoteLabel(val5) && IsVisible((VisualElement)(object)val5) && TryGetScreenRect((VisualElement)(object)val5, out var rect) && ((Rect)(ref rect)).xMin >= 0f && ((Rect)(ref rect)).xMin < (float)Screen.width * 0.36f && ((Rect)(ref rect)).yMin > (float)Screen.height * 0.62f)
						{
							float num3 = 1f - ((Rect)(ref rect)).xMin / (float)Screen.width + ((Rect)(ref rect)).yMin / (float)Screen.height;
							if (HasEmoteNameNearby((VisualElement)(object)val5))
							{
								num3 += 2f;
							}
							if (num3 > num)
							{
								num = num3;
								val = val5;
								val2 = FindShortcutBadge(val5, rect);
							}
						}
						for (int num4 = val4.childCount - 1; num4 >= 0; num4--)
						{
							stack.Push(val4.ElementAt(num4));
						}
					}
				}
				if (val != null)
				{
					_emoteLabel = val;
					_emoteBadge = (VisualElement)(((object)val2) ?? ((object)val));
					_everLocated = true;
					_log.LogInfo((object)"Located Emote HUD. Bell Control will follow its live position.");
				}
				else if (!_loggedFallback)
				{
					_loggedFallback = true;
					_log.LogInfo((object)"Emote HUD is not ready yet; retrying before displaying Bell Control.");
				}
			}
			catch (Exception ex)
			{
				if (!_loggedFallback)
				{
					_loggedFallback = true;
					_log.LogWarning((object)("Emote HUD discovery attempt failed: " + ex.Message));
				}
			}
		}

		private static bool IsEmoteLabel(TextElement label)
		{
			string text = label.text;
			if (string.IsNullOrWhiteSpace(text) || text.Length > 64)
			{
				return false;
			}
			text = text.Trim();
			if (!text.Equals("Emote", StringComparison.OrdinalIgnoreCase) && !text.EndsWith(" Emote", StringComparison.OrdinalIgnoreCase))
			{
				if (HasEmoteNameNearby((VisualElement)(object)label))
				{
					return text.IndexOf("Emote", StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return false;
			}
			return true;
		}

		private static bool HasEmoteNameNearby(VisualElement element)
		{
			int num = 0;
			VisualElement val = element;
			while (val != null && num++ < 4)
			{
				if (HasEmoteKeyword(val.name))
				{
					return true;
				}
				foreach (string @class in val.GetClasses())
				{
					if (HasEmoteKeyword(@class))
					{
						return true;
					}
				}
				val = val.parent;
			}
			return false;
		}

		private static bool HasEmoteKeyword(string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				return value.IndexOf("emote", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static VisualElement FindShortcutBadge(TextElement label, Rect labelRect)
		{
			VisualElement result = (VisualElement)(object)label;
			int num = 0;
			VisualElement parent = ((VisualElement)label).parent;
			Rect rect;
			while (parent != null && num++ < 5 && TryGetScreenRect(parent, out rect) && !(((Rect)(ref rect)).width > 225f) && !(((Rect)(ref rect)).height > 85f) && !(((Rect)(ref rect)).width < ((Rect)(ref labelRect)).width) && !(((Rect)(ref rect)).height < ((Rect)(ref labelRect)).height) && !(((Rect)(ref rect)).xMin > ((Rect)(ref labelRect)).xMin) && !(((Rect)(ref rect)).yMin > ((Rect)(ref labelRect)).yMin))
			{
				result = parent;
				parent = parent.parent;
			}
			return result;
		}

		private static bool IsVisible(VisualElement element)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			for (VisualElement val = element; val != null; val = val.parent)
			{
				IResolvedStyle resolvedStyle = val.resolvedStyle;
				if ((int)resolvedStyle.display == 1 || (int)resolvedStyle.visibility == 1 || resolvedStyle.opacity <= 0.01f)
				{
					return false;
				}
			}
			return element.panel != null;
		}

		private static bool TryGetScreenRect(VisualElement element, out Rect rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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)
			rect = default(Rect);
			if (element == null || element.panel == null)
			{
				return false;
			}
			Rect worldBound = element.panel.visualTree.worldBound;
			Rect worldBound2 = element.worldBound;
			if (((Rect)(ref worldBound)).width <= 0f || ((Rect)(ref worldBound)).height <= 0f || ((Rect)(ref worldBound2)).width <= 0f || ((Rect)(ref worldBound2)).height <= 0f)
			{
				return false;
			}
			float num = (float)Screen.width / ((Rect)(ref worldBound)).width;
			float num2 = (float)Screen.height / ((Rect)(ref worldBound)).height;
			rect = new Rect((((Rect)(ref worldBound2)).xMin - ((Rect)(ref worldBound)).xMin) * num, (((Rect)(ref worldBound2)).yMin - ((Rect)(ref worldBound)).yMin) * num2, ((Rect)(ref worldBound2)).width * num, ((Rect)(ref worldBound2)).height * num2);
			if (((Rect)(ref rect)).xMin >= -1f && ((Rect)(ref rect)).yMin >= -1f && ((Rect)(ref rect)).xMax <= (float)Screen.width + 1f)
			{
				return ((Rect)(ref rect)).yMax <= (float)Screen.height + 1f;
			}
			return false;
		}
	}
}