using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.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 LocalHudControl { [BepInPlugin("local.howtofish.hudcontrol", "HUD Control", "1.0.0")] [DefaultExecutionOrder(30000)] public sealed class HudControl : BaseUnityPlugin { public const string Id = "local.howtofish.hudcontrol"; public static HudControl Instance; private static readonly FieldInfo UiInstance = AccessTools.Field(typeof(PlayerUI), "_instance"); private static readonly string[] GroupNames = new string[3] { "_mainCanvas", "_fxCanvas", "_bossCanvas" }; private readonly List groups = new List(); private ConfigEntry scaleSetting; private ConfigEntry opacitySetting; private ConfigEntry hideSetting; private Harmony harmony; private PlayerUI boundUi; private bool open; private bool dirty; private float nextScan; private float saveAt; private Rect window = new Rect(32f, 80f, 400f, 300f); private CursorLockMode oldLock; private bool oldVisible; private GUIStyle heading; private GUIStyle label; private GUIStyle button; private GUIStyle checkbox; public static bool IsOpen { get { if ((Object)(object)Instance != (Object)null) { return Instance.open; } return false; } } private void Awake() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown Instance = this; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; scaleSetting = ((BaseUnityPlugin)this).Config.Bind("HUD", "Scale", 1f, new ConfigDescription("Base HUD scale: 0.5 (50%) to 1 (default).", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 1f), Array.Empty())); opacitySetting = ((BaseUnityPlugin)this).Config.Bind("HUD", "Opacity", 1f, new ConfigDescription("Base HUD opacity: 0 (transparent) to 1 (opaque).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); hideSetting = ((BaseUnityPlugin)this).Config.Bind("HUD", "HideHUD", false, "Hide the base HUD. The F1 settings panel remains accessible."); scaleSetting.Value = HudValues.Clamp(scaleSetting.Value, 0.5f, 1f, 1f); opacitySetting.Value = HudValues.Clamp(opacitySetting.Value, 0f, 1f, 1f); harmony = new Harmony("local.howtofish.hudcontrol"); harmony.PatchAll(typeof(HudControl).Assembly); ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"HUD Control loaded. F1 opens scale, visibility and opacity settings."); } private void Update() { Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current.f1Key).wasPressedThisFrame) { SetOpen(!open); } if (Time.unscaledTime >= nextScan) { nextScan = Time.unscaledTime + 0.5f; object? value = UiInstance.GetValue(null); PlayerUI val = (PlayerUI)((value is PlayerUI) ? value : null); if ((Object)(object)val != (Object)(object)boundUi || ((Object)(object)val != (Object)null && groups.Count == 0) || ((Object)(object)val == (Object)null && groups.Count > 0)) { Bind(val); } } } private void Bind(PlayerUI ui) { foreach (HudGroup group in groups) { group.Restore(); } groups.Clear(); boundUi = ui; if ((Object)(object)ui == (Object)null) { return; } List list = new List(); string[] groupNames = GroupNames; foreach (string text in groupNames) { object? value = AccessTools.Field(typeof(PlayerUI), text).GetValue(ui); CanvasGroup val = (CanvasGroup)((value is CanvasGroup) ? value : null); if ((Object)(object)val != (Object)null && !list.Contains(val)) { list.Add(val); } } foreach (CanvasGroup item in list) { bool flag = false; foreach (CanvasGroup item2 in list) { if ((Object)(object)item2 != (Object)(object)item && ((Component)item).transform.IsChildOf(((Component)item2).transform)) { flag = true; } } if (!flag) { groups.Add(new HudGroup(item)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controlling base HUD group: " + ((Object)item).name)); } } } private void LateUpdate() { foreach (HudGroup group in groups) { group.Apply(scaleSetting.Value, hideSetting.Value ? 0f : opacitySetting.Value); } if (open) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } if (dirty && Time.unscaledTime >= saveAt) { Save(); } } public void CaptureGameAlpha(MethodBase method) { string text = ((method.Name == "ToggleMainCanvas") ? "_mainCanvas" : ((method.Name == "ToggleFXCanvas") ? "_fxCanvas" : "_bossCanvas")); object? value = UiInstance.GetValue(null); PlayerUI val = (PlayerUI)((value is PlayerUI) ? value : null); if ((Object)(object)val == (Object)null) { return; } object? value2 = AccessTools.Field(typeof(PlayerUI), text).GetValue(val); CanvasGroup val2 = (CanvasGroup)((value2 is CanvasGroup) ? value2 : null); foreach (HudGroup group in groups) { if ((Object)(object)group.Group == (Object)(object)val2) { group.CaptureAlpha(); } } } public void CaptureCanvasScale(CanvasScaler scaler, float value) { foreach (HudGroup group in groups) { group.CaptureCanvasScale(scaler, value); } } public void SetOpen(bool value) { //IL_005e: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 if (open == value) { return; } if (value) { oldLock = Cursor.lockState; oldVisible = Cursor.visible; open = true; try { PlayerCamera.ToggleMouse(true); } catch { } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } else { open = false; try { PlayerCamera.ToggleMouse((int)oldLock != 1); } catch { Cursor.lockState = oldLock; Cursor.visible = oldVisible; } Save(); } } private void Changed() { dirty = true; saveAt = Time.unscaledTime + 0.4f; } private void Save() { if (dirty) { ((BaseUnityPlugin)this).Config.Save(); dirty = false; } } private void OnGUI() { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0040: 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_0052: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //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_008c: Expected O, but got Unknown if (open) { if (heading == null) { heading = new GUIStyle(GUI.skin.label) { fontSize = 21, fontStyle = (FontStyle)1 }; label = new GUIStyle(GUI.skin.label) { fontSize = 15 }; button = new GUIStyle(GUI.skin.button) { fontSize = 14 }; checkbox = new GUIStyle(GUI.skin.toggle) { fontSize = 16 }; } GUI.depth = -10000; ((Rect)(ref window)).width = Mathf.Min(400, Screen.width); ((Rect)(ref window)).height = Mathf.Min(300, Screen.height); ((Rect)(ref window)).x = Mathf.Clamp(((Rect)(ref window)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref window)).width)); ((Rect)(ref window)).y = Mathf.Clamp(((Rect)(ref window)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref window)).height)); window = GUI.Window(4740420, window, new WindowFunction(DrawWindow), ""); } } private void DrawWindow(int id) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(18f, 12f, 290f, 32f), "HUD settings", heading); if (GUI.Button(new Rect(((Rect)(ref window)).width - 45f, 12f, 28f, 27f), "X", button)) { SetOpen(value: false); } GUI.Label(new Rect(18f, 51f, ((Rect)(ref window)).width - 36f, 24f), "Scale " + Mathf.RoundToInt(scaleSetting.Value * 100f) + "%", label); float num = Mathf.Round(GUI.HorizontalSlider(new Rect(20f, 81f, ((Rect)(ref window)).width - 40f, 22f), scaleSetting.Value, 0.5f, 1f) * 100f) / 100f; GUI.Label(new Rect(18f, 101f, ((Rect)(ref window)).width - 36f, 23f), "50% 100% (default)", label); bool flag = GUI.Toggle(new Rect(20f, 133f, ((Rect)(ref window)).width - 40f, 26f), hideSetting.Value, " Hide HUD", checkbox); GUI.Label(new Rect(18f, 171f, ((Rect)(ref window)).width - 36f, 24f), "Opacity " + Mathf.RoundToInt(opacitySetting.Value * 100f) + "%", label); float num2 = Mathf.Round(GUI.HorizontalSlider(new Rect(20f, 202f, ((Rect)(ref window)).width - 40f, 22f), opacitySetting.Value, 0f, 1f) * 100f) / 100f; if (num != scaleSetting.Value || num2 != opacitySetting.Value || flag != hideSetting.Value) { scaleSetting.Value = num; opacitySetting.Value = num2; hideSetting.Value = flag; Changed(); } if (GUI.Button(new Rect(18f, 246f, 128f, 30f), "Reset defaults", button)) { ConfigEntry obj = scaleSetting; float value = (opacitySetting.Value = 1f); obj.Value = value; hideSetting.Value = false; Changed(); } GUI.Label(new Rect(163f, 249f, ((Rect)(ref window)).width - 170f, 25f), (groups.Count == 0) ? "Load a game to preview" : "F1 / Esc to close", label); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref window)).width - 52f, 45f)); } private void OnDestroy() { SetOpen(value: false); Save(); foreach (HudGroup group in groups) { group.Restore(); } if (harmony != null) { harmony.UnpatchSelf(); } Instance = null; } } public sealed class HudGroup { private readonly RectTransform rect; private readonly Canvas rootCanvas; private readonly Vector3 originalScale; private readonly Vector2 originalSize; private readonly bool originalRaycasts; private readonly bool originalInteractable; private readonly MultiplierState alpha; private readonly MultiplierState canvasScale; public CanvasGroup Group { get; private set; } public HudGroup(CanvasGroup group) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003b: Unknown result type (might be due to invalid IL or missing references) Group = group; ref RectTransform reference = ref rect; Transform transform = ((Component)group).transform; reference = (RectTransform)(object)((transform is RectTransform) ? transform : null); originalScale = ((Transform)rect).localScale; originalSize = rect.sizeDelta; originalRaycasts = group.blocksRaycasts; originalInteractable = group.interactable; alpha = new MultiplierState(group.alpha); Canvas component = ((Component)group).GetComponent(); if ((Object)(object)component != (Object)null && component.isRootCanvas) { rootCanvas = component; canvasScale = new MultiplierState(component.scaleFactor); } } public void CaptureAlpha() { if ((Object)(object)Group != (Object)null) { alpha.SetBaseline(Group.alpha); } } public void CaptureCanvasScale(CanvasScaler scaler, float value) { if ((Object)(object)rootCanvas != (Object)null && (Object)(object)((Component)scaler).gameObject == (Object)(object)((Component)rootCanvas).gameObject) { canvasScale.SetBaseline(value); } } public void Apply(float scale, float opacity) { //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Group == (Object)null) { return; } alpha.Observe(Group.alpha); Group.alpha = alpha.Apply(opacity); Group.blocksRaycasts = opacity > 0f && originalRaycasts; Group.interactable = opacity > 0f && originalInteractable; if ((Object)(object)rootCanvas != (Object)null) { canvasScale.Observe(rootCanvas.scaleFactor); rootCanvas.scaleFactor = canvasScale.Apply(scale); } else if ((Object)(object)rect != (Object)null) { Transform parent = ((Transform)rect).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val != (Object)null) { Rect val2 = val.rect; Vector2 val3 = Vector2.Scale(((Rect)(ref val2)).size, rect.anchorMax - rect.anchorMin); rect.sizeDelta = new Vector2(HudValues.CompensatedSize(val3.x, originalSize.x, scale), HudValues.CompensatedSize(val3.y, originalSize.y, scale)); } ((Transform)rect).localScale = originalScale * scale; } } public void Restore() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Group == (Object)null)) { Group.alpha = alpha.Baseline; Group.blocksRaycasts = originalRaycasts; Group.interactable = originalInteractable; if ((Object)(object)rootCanvas != (Object)null) { rootCanvas.scaleFactor = canvasScale.Baseline; } else if ((Object)(object)rect != (Object)null) { ((Transform)rect).localScale = originalScale; rect.sizeDelta = originalSize; } } } } [HarmonyPatch(typeof(Player), "get_BlockInputs")] internal static class BlockPlayerInput { private static void Postfix(Player __instance, ref bool __result) { if (HudControl.IsOpen && (Object)(object)__instance == (Object)(object)Player.LocalPlayer) { __result = true; } } } [HarmonyPatch] internal static class BlockCameraInput { private static IEnumerable TargetMethods() { string[] array = new string[4] { "MouseClick", "MouseInput", "ControllerRotation", "ApplyAimAssist" }; foreach (string text in array) { yield return AccessTools.Method(typeof(PlayerCamera), text, (Type[])null, (Type[])null); } } private static bool Prefix() { return !HudControl.IsOpen; } } [HarmonyPatch(typeof(PauseManager), "PauseInput")] internal static class CloseOnEscape { private static bool Prefix(CallbackContext context) { if (!HudControl.IsOpen) { return true; } if (((CallbackContext)(ref context)).performed) { HudControl.Instance.SetOpen(value: false); } return false; } } [HarmonyPatch] internal static class RespectGameVisibility { private static IEnumerable TargetMethods() { string[] array = new string[3] { "ToggleMainCanvas", "ToggleFXCanvas", "ToggleBossCanvas" }; foreach (string text in array) { yield return AccessTools.Method(typeof(PlayerUI), text, (Type[])null, (Type[])null); } } private static void Postfix(MethodBase __originalMethod) { if ((Object)(object)HudControl.Instance != (Object)null) { HudControl.Instance.CaptureGameAlpha(__originalMethod); } } } [HarmonyPatch(typeof(CanvasScaler), "SetScaleFactor")] internal static class RespectResolutionScale { private static void Postfix(CanvasScaler __instance, float __0) { if ((Object)(object)HudControl.Instance != (Object)null) { HudControl.Instance.CaptureCanvasScale(__instance, __0); } } } public static class HudValues { public static float Clamp(float value, float minimum, float maximum, float fallback) { if (float.IsNaN(value) || float.IsInfinity(value)) { return fallback; } return Math.Max(minimum, Math.Min(maximum, value)); } public static float CompensatedSize(float parentSpan, float originalDelta, float scale) { return (parentSpan + originalDelta) / scale - parentSpan; } } public sealed class MultiplierState { private float applied; private bool hasApplied; public float Baseline { get; private set; } public MultiplierState(float initial) { Baseline = initial; } public void Observe(float current) { if (!hasApplied || Math.Abs(current - applied) > 1E-05f) { Baseline = current; } } public void SetBaseline(float value) { Baseline = value; } public float Apply(float multiplier) { hasApplied = true; return applied = Baseline * multiplier; } } }