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+a871af15f412559c5cca9e35ff105dc56c0d333e")]
[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 Control", "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 Control";
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 Control 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 Control 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;
}
}
}