Decompiled source of BossTimer v0.1.4

BepInEx/plugins/HowToFish.BossTimer/HowToFish.BossTimer.dll

Decompiled 3 hours ago
using System;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using FishNet;
using FishNet.Managing.Timing;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: AssemblyDescription("Shows the exact synchronized time remaining before the active boss leaves.")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyCompany("evansvl")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HowToFish.BossTimer;

[BepInPlugin("community.howtofish.bosstimer", "Boss Timer", "0.1.4")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "community.howtofish.bosstimer";

	public const string PluginName = "Boss Timer";

	public const string PluginVersion = "0.1.4";

	private const float LegacyTopOffset = 125f;

	private const float DefaultTopOffset = 175f;

	private ConfigEntry<bool> _enabled;

	private ConfigEntry<bool> _showLabel;

	private ConfigEntry<bool> _showTenths;

	private ConfigEntry<float> _scale;

	private ConfigEntry<float> _opacity;

	private ConfigEntry<float> _topOffset;

	private GameObject _root;

	private RectTransform _panel;

	private CanvasGroup _canvasGroup;

	private TextMeshProUGUI _text;

	private bool _nativeStyleApplied;

	private BossUI _nativeBossUi;

	private CanvasGroup _nativeCountdownGroup;

	private void Awake()
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Expected O, but got Unknown
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Expected O, but got Unknown
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, "Show the boss departure countdown while a boss is active.");
		_showLabel = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowLabel", true, "Show 'BOSS LEAVES IN' before the remaining time.");
		_showTenths = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowTenths", true, "Show tenths of a second.");
		_scale = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Scale", 1f, new ConfigDescription("HUD scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1.8f), new object[0]));
		_opacity = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Opacity", 1f, new ConfigDescription("HUD opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1f), new object[0]));
		_topOffset = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "TopOffset", 175f, new ConfigDescription("Distance from the top of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 350f), new object[0]));
		if (Mathf.Approximately(_topOffset.Value, 125f))
		{
			_topOffset.Value = 175f;
		}
		CreateHud();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded. Countdown reads BossManager's synchronized leave tick and never changes gameplay state.");
	}

	private void Update()
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		if (!_enabled.Value || !IsGameplayHudVisible() || !TryGetRemainingSeconds(out var seconds))
		{
			SetVisible(visible: false);
			return;
		}
		ApplyNativeStyleWhenAvailable();
		ApplyLiveSettings();
		((TMP_Text)_text).text = (_showLabel.Value ? "BOSS LEAVES IN  " : string.Empty) + FormatRemaining(seconds);
		((Graphic)_text).color = CountdownColor(seconds, _opacity.Value);
		float nativeCountdownAlpha = GetNativeCountdownAlpha();
		_canvasGroup.alpha = ((nativeCountdownAlpha > 0.001f) ? Mathf.MoveTowards(_canvasGroup.alpha, 0f, Time.unscaledDeltaTime * 2f) : 1f);
		SetVisible(_canvasGroup.alpha > 0.001f);
	}

	private static bool IsGameplayHudVisible()
	{
		if (MainMenuManager.IsInMenu || PauseManager.IsPaused)
		{
			return false;
		}
		try
		{
			return PlayerUI.BossCanvasActive;
		}
		catch
		{
			return false;
		}
	}

	private static bool TryGetRemainingSeconds(out double seconds)
	{
		seconds = 0.0;
		if (MainMenuManager.IsInMenu)
		{
			return false;
		}
		Creature boss = BossManager.Boss;
		if ((Object)(object)boss == (Object)null || boss.IsDead)
		{
			return false;
		}
		TimeManager timeManager = InstanceFinder.TimeManager;
		if ((Object)(object)timeManager == (Object)null || timeManager.TickRate == 0)
		{
			return false;
		}
		uint bossTotalTimeInTicks = BossManager.BossTotalTimeInTicks;
		if (bossTotalTimeInTicks == 0)
		{
			return false;
		}
		uint num = BossManager.BossLeavesTick - timeManager.Tick;
		if (num > bossTotalTimeInTicks)
		{
			return false;
		}
		seconds = (double)num / (double)(int)timeManager.TickRate;
		return true;
	}

	private string FormatRemaining(double seconds)
	{
		if (_showTenths.Value)
		{
			double num = Math.Ceiling(Math.Max(0.0, seconds) * 10.0) / 10.0;
			int num2 = (int)(num / 60.0);
			double num3 = num - (double)num2 * 60.0;
			return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00.0}", num2, num3);
		}
		int num4 = (int)Math.Ceiling(Math.Max(0.0, seconds));
		return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00}", num4 / 60, num4 % 60);
	}

	private void CreateHud()
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Expected O, but got Unknown
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: 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_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Expected O, but got Unknown
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dd: 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_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		_root = new GameObject("BossTimerHUD", new Type[3]
		{
			typeof(RectTransform),
			typeof(Canvas),
			typeof(CanvasScaler)
		});
		Object.DontDestroyOnLoad((Object)(object)_root);
		Canvas component = _root.GetComponent<Canvas>();
		component.renderMode = (RenderMode)0;
		component.sortingOrder = 32761;
		CanvasScaler component2 = _root.GetComponent<CanvasScaler>();
		component2.uiScaleMode = (ScaleMode)1;
		component2.referenceResolution = new Vector2(1920f, 1080f);
		component2.screenMatchMode = (ScreenMatchMode)0;
		component2.matchWidthOrHeight = 0.5f;
		GameObject val = new GameObject("Anchor", new Type[2]
		{
			typeof(RectTransform),
			typeof(CanvasGroup)
		});
		val.transform.SetParent(_root.transform, false);
		_panel = val.GetComponent<RectTransform>();
		_canvasGroup = val.GetComponent<CanvasGroup>();
		RectTransform panel = _panel;
		Vector2 anchorMin = (_panel.anchorMax = new Vector2(0.5f, 1f));
		panel.anchorMin = anchorMin;
		_panel.pivot = new Vector2(0.5f, 1f);
		_panel.sizeDelta = new Vector2(700f, 64f);
		GameObject val3 = new GameObject("Countdown", new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(TextMeshProUGUI)
		});
		val3.transform.SetParent((Transform)(object)_panel, false);
		_text = val3.GetComponent<TextMeshProUGUI>();
		RectTransform rectTransform = ((TMP_Text)_text).rectTransform;
		rectTransform.anchorMin = Vector2.zero;
		rectTransform.anchorMax = Vector2.one;
		rectTransform.offsetMin = Vector2.zero;
		rectTransform.offsetMax = Vector2.zero;
		((TMP_Text)_text).fontSize = 31f;
		((TMP_Text)_text).alignment = (TextAlignmentOptions)258;
		((TMP_Text)_text).enableWordWrapping = false;
		((Graphic)_text).raycastTarget = false;
		((TMP_Text)_text).overflowMode = (TextOverflowModes)0;
		((TMP_Text)_text).outlineColor = new Color32((byte)20, (byte)18, (byte)18, byte.MaxValue);
		((TMP_Text)_text).outlineWidth = 0.18f;
		_root.SetActive(false);
	}

	private float GetNativeCountdownAlpha()
	{
		if ((Object)(object)_nativeBossUi == (Object)null || (Object)(object)_nativeCountdownGroup == (Object)null)
		{
			FindNativeCountdownGroup();
		}
		if ((Object)(object)_nativeCountdownGroup == (Object)null || !((Component)_nativeCountdownGroup).gameObject.activeInHierarchy)
		{
			return 0f;
		}
		return Mathf.Clamp01(_nativeCountdownGroup.alpha);
	}

	private void FindNativeCountdownGroup()
	{
		_nativeBossUi = null;
		_nativeCountdownGroup = null;
		FieldInfo field = typeof(BossUI).GetField("_countdownGroup", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null)
		{
			return;
		}
		BossUI[] array = Resources.FindObjectsOfTypeAll<BossUI>();
		BossUI[] array2 = array;
		foreach (BossUI val in array2)
		{
			if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
			{
				object? value = field.GetValue(val);
				CanvasGroup val2 = (CanvasGroup)((value is CanvasGroup) ? value : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					_nativeBossUi = val;
					_nativeCountdownGroup = val2;
					break;
				}
			}
		}
	}

	private void ApplyNativeStyleWhenAvailable()
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		if (_nativeStyleApplied)
		{
			return;
		}
		TextMeshProUGUI val = FindNativeCountdownText();
		if ((Object)(object)val == (Object)null)
		{
			val = FindVersionText();
		}
		if (!((Object)(object)val == (Object)null) && !((Object)(object)((TMP_Text)val).font == (Object)null))
		{
			((TMP_Text)_text).font = ((TMP_Text)val).font;
			if ((Object)(object)((TMP_Text)val).fontSharedMaterial != (Object)null)
			{
				((TMP_Text)_text).fontSharedMaterial = ((TMP_Text)val).fontSharedMaterial;
			}
			((TMP_Text)_text).characterSpacing = ((TMP_Text)val).characterSpacing;
			((TMP_Text)_text).outlineColor = new Color32((byte)20, (byte)18, (byte)18, byte.MaxValue);
			((TMP_Text)_text).outlineWidth = 0.18f;
			_nativeStyleApplied = true;
		}
	}

	private static TextMeshProUGUI FindNativeCountdownText()
	{
		BossUI[] array = Resources.FindObjectsOfTypeAll<BossUI>();
		FieldInfo field = typeof(BossUI).GetField("_timeCountdownText", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null)
		{
			return null;
		}
		BossUI[] array2 = array;
		foreach (BossUI val in array2)
		{
			if (!((Object)(object)val == (Object)null))
			{
				object? value = field.GetValue(val);
				TextMeshProUGUI val2 = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
		}
		return null;
	}

	private static TextMeshProUGUI FindVersionText()
	{
		TextMeshProUGUI[] array = Resources.FindObjectsOfTypeAll<TextMeshProUGUI>();
		TextMeshProUGUI[] array2 = array;
		foreach (TextMeshProUGUI val in array2)
		{
			if ((Object)(object)val != (Object)null && ((TMP_Text)val).text != null && ((TMP_Text)val).text.IndexOf("Version", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return val;
			}
		}
		return null;
	}

	private void ApplyLiveSettings()
	{
		//IL_0006: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		((Transform)_panel).localScale = Vector3.one * _scale.Value;
		_panel.anchoredPosition = new Vector2(0f, 0f - _topOffset.Value);
	}

	private static Color CountdownColor(double seconds, float opacity)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		if (seconds <= 5.0)
		{
			return new Color(1f, 0.24f, 0.13f, opacity);
		}
		if (seconds <= 10.0)
		{
			return new Color(1f, 0.53f, 0.08f, opacity);
		}
		return new Color(1f, 1f, 1f, opacity);
	}

	private void SetVisible(bool visible)
	{
		if ((Object)(object)_root != (Object)null && _root.activeSelf != visible)
		{
			_root.SetActive(visible);
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
	}
}