Decompiled source of RoomChestTracker v0.1.1

plugins/StonewardsRoomChestTracker.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StonewardsRoomChestTracker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StonewardsRoomChestTracker")]
[assembly: AssemblyTitle("StonewardsRoomChestTracker")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 StonewardsRoomChestTracker
{
	[BepInPlugin("com.ellenashley.stonewards.roomchesttracker", "Stonewards Room & Chest Tracker", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.ellenashley.stonewards.roomchesttracker";

		public const string PluginName = "Stonewards Room & Chest Tracker";

		public const string PluginVersion = "0.1.0";

		internal static ManualLogSource Log;

		private ConfigEntry<KeyboardShortcut> toggleKey;

		private ConfigEntry<bool> startEnabled;

		private ConfigEntry<float> updateInterval;

		private ConfigEntry<float> panelWidth;

		private ConfigEntry<float> panelHeight;

		private ConfigEntry<float> positionX;

		private ConfigEntry<float> positionY;

		private ConfigEntry<float> scale;

		private ConfigEntry<float> alpha;

		private ConfigEntry<bool> showFoundTotals;

		private readonly HashSet<string> discoveredRoomIndices = new HashSet<string>(StringComparer.Ordinal);

		private string runKey;

		private string activeSceneName;

		private int maxRooms = -1;

		private bool trackerEnabled = true;

		private float nextRefresh;

		private Canvas canvas;

		private RectTransform panel;

		private Text titleText;

		private Text roomsText;

		private Text chestsText;

		private Text detailText;

		private Harmony harmony;

		private static readonly BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly BindingFlags AllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			Instance = this;
			toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("UI", "ToggleKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Show/hide the tracker.");
			startEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "StartEnabled", true, "Show the tracker automatically when a cave map loads.");
			updateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "UpdateInterval", 0.25f, "How often the counts are refreshed.");
			panelWidth = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelWidth", 330f, "Tracker panel width in pixels.");
			panelHeight = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelHeight", 145f, "Tracker panel height in pixels.");
			positionX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PositionX", 25f, "Distance from the right edge of the screen.");
			positionY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PositionY", 25f, "Distance from the top edge of the screen.");
			scale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Scale", 1f, "Overall UI scale.");
			alpha = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "BackgroundAlpha", 0.82f, "Background opacity.");
			showFoundTotals = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowFoundTotals", true, "Show rooms found / total beneath the remaining count.");
			Log = ((BaseUnityPlugin)this).Logger;
			trackerEnabled = startEnabled.Value;
			BuildUi();
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
			harmony = new Harmony("com.ellenashley.stonewards.roomchesttracker");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Stonewards Room & Chest Tracker v0.1.0 loaded.");
		}

		private void OnDestroy()
		{
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Instance = null;
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				trackerEnabled = !trackerEnabled;
			}
			UpdateUiVisibility();
			if (trackerEnabled && IsCaveMap() && Time.unscaledTime >= nextRefresh)
			{
				nextRefresh = Time.unscaledTime + Mathf.Max(0.05f, updateInterval.Value);
				RefreshState();
			}
		}

		private void OnActiveSceneChanged(Scene previous, Scene next)
		{
			activeSceneName = ((Scene)(ref next)).name;
			ResetRunState();
			trackerEnabled = startEnabled.Value;
			UpdateUiVisibility();
		}

		internal void RecordPatternChange()
		{
			if (IsCaveMap())
			{
				object diggingManager = GetDiggingManager();
				if (diggingManager != null)
				{
					RecordCurrentPattern(diggingManager);
				}
			}
		}

		private void RefreshState()
		{
			try
			{
				object diggingManager = GetDiggingManager();
				if (diggingManager == null)
				{
					ShowCounts("Waiting for cave...", "", "");
					return;
				}
				UpdateRunKey(diggingManager);
				UpdateMaxRooms(diggingManager);
				RecordCurrentPattern(diggingManager);
				int num = CountUnopenedChests();
				int num2 = ((maxRooms < 0) ? (-1) : Mathf.Max(0, maxRooms - discoveredRoomIndices.Count));
				string roomLine = ((num2 < 0) ? "Rooms left: ?" : $"Rooms left: {num2}");
				string chestLine = $"Chests left: {num}";
				string detail = ((showFoundTotals.Value && maxRooms >= 0) ? $"Rooms found: {discoveredRoomIndices.Count} / {maxRooms}" : "Unique generated rooms visited this run");
				ShowCounts(roomLine, chestLine, detail);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Tracker refresh failed: {arg}");
				ShowCounts("Rooms left: ?", "Chests left: ?", "See BepInEx log for details.");
			}
		}

		private void UpdateRunKey(object manager)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string name = ((Scene)(ref activeScene)).name;
			object memberValue = GetMemberValue(manager, manager.GetType(), "worldSeed");
			string b = name + "|" + ((memberValue == null) ? "?" : Convert.ToString(memberValue));
			if (runKey == null)
			{
				runKey = b;
			}
			else if (!string.Equals(runKey, b, StringComparison.Ordinal))
			{
				runKey = b;
				ResetRoomDiscoveryOnly();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("New Stonewards run detected: " + runKey));
			}
		}

		private void UpdateMaxRooms(object manager)
		{
			object obj = GetMemberValue(manager, manager.GetType(), "caveLevelGenerator");
			if (obj == null)
			{
				PropertyInfo property = manager.GetType().GetProperty("CaveLevelGenerator", AllInstance);
				if (property != null)
				{
					obj = property.GetValue(manager, null);
				}
			}
			if (obj == null)
			{
				return;
			}
			object memberValue = GetMemberValue(obj, obj.GetType(), "caveLevelGenerationSO");
			if (memberValue != null)
			{
				object memberValue2 = GetMemberValue(memberValue, memberValue.GetType(), "MaxRooms");
				if (memberValue2 == null)
				{
					memberValue2 = GetMemberValue(memberValue, memberValue.GetType(), "maxRooms");
				}
				if (memberValue2 != null && int.TryParse(Convert.ToString(memberValue2), out var result))
				{
					maxRooms = result;
				}
			}
		}

		private void RecordCurrentPattern(object manager)
		{
			object memberValue = GetMemberValue(manager, manager.GetType(), "syncDiggingPatternIndex");
			if (memberValue != null)
			{
				string text = Convert.ToString(memberValue);
				if (!string.IsNullOrEmpty(text))
				{
					discoveredRoomIndices.Add(text);
				}
			}
		}

		private int CountUnopenedChests()
		{
			return 0 + CountUnopenedByType("Chest") + CountUnopenedByType("TreasureChest");
		}

		private int CountUnopenedByType(string typeName)
		{
			Type type = FindGameType(typeName);
			if (type == null || !typeof(Component).IsAssignableFrom(type))
			{
				return 0;
			}
			Object[] array = Object.FindObjectsOfType(type);
			int num = 0;
			Object[] array2 = array;
			foreach (Object obj in array2)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.gameObject == (Object)null) && val.gameObject.activeInHierarchy)
				{
					object memberValue = GetMemberValue(val, ((object)val).GetType(), "isOpened");
					if (memberValue is bool && !(bool)memberValue)
					{
						num++;
					}
				}
			}
			return num;
		}

		private static object GetMemberValue(object target, Type type, string name)
		{
			if (target == null)
			{
				return null;
			}
			FieldInfo field = type.GetField(name, AllInstance | AllStatic);
			if (field != null)
			{
				return field.GetValue(target);
			}
			PropertyInfo property = type.GetProperty(name, AllInstance | AllStatic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return property.GetValue(target, null);
				}
				catch
				{
					return null;
				}
			}
			return null;
		}

		private static Type FindGameType(string typeName)
		{
			Type type = Type.GetType(typeName + ", Assembly-CSharp");
			if (type != null)
			{
				return type;
			}
			return typeof(Plugin).Assembly.GetType(typeName) ?? AppDomain.CurrentDomain.GetAssemblies()[0].GetType(typeName);
		}

		private static object GetDiggingManager()
		{
			Type type = FindGameType("DiggingManager");
			if (type == null)
			{
				return null;
			}
			FieldInfo field = type.GetField("Instance", AllStatic);
			if (field == null)
			{
				return null;
			}
			return field.GetValue(null);
		}

		private void ResetRunState()
		{
			runKey = null;
			maxRooms = -1;
			discoveredRoomIndices.Clear();
		}

		private void ResetRoomDiscoveryOnly()
		{
			discoveredRoomIndices.Clear();
		}

		private bool IsCaveMap()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string name = ((Scene)(ref activeScene)).name;
			if (!string.IsNullOrEmpty(name))
			{
				return name.StartsWith("MAP_", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private void BuildUi()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_004a: 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_006c: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("StonewardsRoomChestTrackerCanvas");
			canvas = val.AddComponent<Canvas>();
			canvas.renderMode = (RenderMode)0;
			canvas.sortingOrder = 5000;
			CanvasScaler obj = val.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			val.AddComponent<GraphicRaycaster>();
			Object.DontDestroyOnLoad((Object)(object)val);
			GameObject val2 = new GameObject("TrackerPanel");
			val2.transform.SetParent(((Component)canvas).transform, false);
			panel = val2.AddComponent<RectTransform>();
			((Graphic)val2.AddComponent<Image>()).color = new Color(0.02f, 0.03f, 0.05f, alpha.Value);
			panel.anchorMin = new Vector2(1f, 1f);
			panel.anchorMax = new Vector2(1f, 1f);
			panel.pivot = new Vector2(1f, 1f);
			MakeText(val2.transform, "Title", "WORLD STATUS", 22, (FontStyle)1, out titleText);
			MakeText(val2.transform, "Rooms", "Rooms left: ?", 30, (FontStyle)1, out roomsText);
			MakeText(val2.transform, "Chests", "Chests left: ?", 30, (FontStyle)1, out chestsText);
			MakeText(val2.transform, "Detail", "", 17, (FontStyle)0, out detailText);
			ApplyUiLayout();
		}

		private void MakeText(Transform parent, string name, string value, int fontSize, FontStyle style, out Text text)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			text = val.AddComponent<Text>();
			text.text = value;
			text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			text.fontSize = fontSize;
			text.fontStyle = style;
			((Graphic)text).color = Color.white;
			text.alignment = (TextAnchor)3;
			text.horizontalOverflow = (HorizontalWrapMode)1;
			text.verticalOverflow = (VerticalWrapMode)1;
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
		}

		private void ApplyUiLayout()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			if (!((Object)(object)panel == (Object)null))
			{
				panel.sizeDelta = new Vector2(panelWidth.Value, panelHeight.Value);
				panel.anchoredPosition = new Vector2(0f - positionX.Value, 0f - positionY.Value);
				((Transform)panel).localScale = Vector3.one * Mathf.Max(0.25f, scale.Value);
				LayoutText(titleText, 8f, -8f, 20f, 34f);
				LayoutText(roomsText, 8f, -43f, 20f, 40f);
				LayoutText(chestsText, 8f, -82f, 20f, 40f);
				LayoutText(detailText, 8f, -122f, 20f, 24f);
			}
		}

		private static void LayoutText(Text text, float x, float y, float width, float height)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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)
			if (!((Object)(object)text == (Object)null))
			{
				RectTransform rectTransform = ((Graphic)text).rectTransform;
				rectTransform.anchorMin = new Vector2(0f, 1f);
				rectTransform.anchorMax = new Vector2(1f, 1f);
				rectTransform.offsetMin = new Vector2(x, y - height);
				rectTransform.offsetMax = new Vector2(0f - x, y);
			}
		}

		private void ShowCounts(string roomLine, string chestLine, string detail)
		{
			if ((Object)(object)roomsText != (Object)null)
			{
				roomsText.text = roomLine;
			}
			if ((Object)(object)chestsText != (Object)null)
			{
				chestsText.text = chestLine;
			}
			if ((Object)(object)detailText != (Object)null)
			{
				detailText.text = detail;
			}
		}

		private void UpdateUiVisibility()
		{
			bool active = trackerEnabled && IsCaveMap();
			if ((Object)(object)canvas != (Object)null)
			{
				((Component)canvas).gameObject.SetActive(active);
			}
		}
	}
	[HarmonyPatch(typeof(DiggingManager), "OnPatternChange")]
	internal static class DiggingManagerOnPatternChangePatch
	{
		private static void Postfix()
		{
			try
			{
				Plugin.Instance?.RecordPatternChange();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("OnPatternChange capture failed: " + ex.Message));
				}
			}
		}
	}
}