using System;
using System.Collections;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using Mirror;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace StonewardsRoomChestTracker;
[BepInPlugin("com.ellenashley.stonewards.roomchesttracker", "Stonewards Room & Chest Tracker", "0.2.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.2.0";
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 bool trackerEnabled;
private float nextRefresh;
private bool clientHandlerRegistered;
private bool haveServerState;
private int syncedRoomsLeft = -1;
private int syncedRoomsFound = -1;
private int syncedRoomsTotal = -1;
private int syncedChestsLeft = -1;
private Canvas canvas;
private RectTransform panel;
private Image background;
private Text titleText;
private Text roomsText;
private Text chestsText;
private Text detailText;
private float lastW;
private float lastH;
private float lastX;
private float lastY;
private float lastScale;
private float lastAlpha;
private bool lastShowTotals;
private static readonly BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private static readonly BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
internal static Plugin Instance { get; private set; }
private void Awake()
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("UI", "ToggleKey", new KeyboardShortcut((KeyCode)288, (KeyCode[])(object)new KeyCode[0]), "Show/hide the tracker.");
startEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "StartEnabled", true, "Show the tracker automatically on cave maps.");
updateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "UpdateInterval", 0.25f, "State refresh interval in seconds.");
panelWidth = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelWidth", 220f, "Tracker width in pixels.");
panelHeight = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelHeight", 82f, "Tracker height in pixels.");
positionX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PositionX", 25f, "Distance from the LEFT edge of the screen.");
positionY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PositionY", 90f, "Distance from the TOP edge of the screen.");
scale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Scale", 0.8f, "Overall tracker scale.");
alpha = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "BackgroundAlpha", 0.72f, "Tracker background opacity.");
showFoundTotals = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowFoundTotals", false, "Show rooms found / total.");
trackerEnabled = startEnabled.Value;
BuildUi();
SceneManager.activeSceneChanged += OnActiveSceneChanged;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Stonewards Room & Chest Tracker v0.2.0 loaded.");
}
private void OnDestroy()
{
SceneManager.activeSceneChanged -= OnActiveSceneChanged;
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;
}
ApplyUiIfChanged();
UpdateUiVisibility();
if (trackerEnabled && IsCaveMap())
{
RegisterClientHandlerIfNeeded();
if (Time.unscaledTime >= nextRefresh)
{
nextRefresh = Time.unscaledTime + Mathf.Max(0.05f, updateInterval.Value);
Refresh();
}
}
}
private void OnActiveSceneChanged(Scene oldScene, Scene newScene)
{
trackerEnabled = startEnabled.Value;
haveServerState = false;
syncedRoomsLeft = (syncedRoomsFound = (syncedRoomsTotal = (syncedChestsLeft = -1)));
clientHandlerRegistered = false;
UpdateUiVisibility();
}
private void RegisterClientHandlerIfNeeded()
{
if (NetworkClient.active && !clientHandlerRegistered)
{
NetworkClient.RegisterHandler<TrackerStateMessage>((Action<TrackerStateMessage>)OnTrackerState, true);
clientHandlerRegistered = true;
}
}
private void OnTrackerState(TrackerStateMessage msg)
{
syncedRoomsLeft = msg.roomsLeft;
syncedRoomsFound = msg.roomsFound;
syncedRoomsTotal = msg.roomsTotal;
syncedChestsLeft = msg.chestsLeft;
haveServerState = true;
}
private void Refresh()
{
try
{
if (NetworkServer.active)
{
int maxRooms = GetMaxRooms();
int generatedPatternCount = GetGeneratedPatternCount();
int roomsLeft = ((maxRooms >= 0 && generatedPatternCount >= 0) ? Mathf.Max(0, maxRooms - generatedPatternCount) : (-1));
int roomsFound = ((maxRooms >= 0 && generatedPatternCount >= 0) ? generatedPatternCount : (-1));
int chestsLeft = CountUnopenedChests();
syncedRoomsLeft = roomsLeft;
syncedRoomsFound = roomsFound;
syncedRoomsTotal = maxRooms;
syncedChestsLeft = chestsLeft;
haveServerState = true;
NetworkServer.SendToAll<TrackerStateMessage>(new TrackerStateMessage
{
roomsLeft = roomsLeft,
roomsFound = roomsFound,
roomsTotal = maxRooms,
chestsLeft = chestsLeft
}, 0, false);
}
if (haveServerState)
{
string room = ((syncedRoomsLeft < 0) ? "Rooms left: ?" : ("Rooms left: " + syncedRoomsLeft));
string chest = ((syncedChestsLeft < 0) ? "Chests left: ?" : ("Chests left: " + syncedChestsLeft));
string detail = ((showFoundTotals.Value && syncedRoomsTotal >= 0) ? ("Found: " + Mathf.Max(0, syncedRoomsFound) + " / " + syncedRoomsTotal) : "");
ShowCounts(room, chest, detail);
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Tracker refresh failed: " + ex));
}
}
private int GetMaxRooms()
{
object diggingManager = GetDiggingManager();
if (diggingManager == null)
{
return -1;
}
object member = GetMember(diggingManager, "caveLevelGenerator");
if (member == null)
{
member = GetMember(diggingManager, "CaveLevelGenerator");
}
if (member == null)
{
return -1;
}
object member2 = GetMember(member, "caveLevelGenerationSO");
if (member2 == null)
{
return -1;
}
object value = GetMember(member2, "MaxRooms") ?? GetMember(member2, "maxRooms");
return TryInt(value);
}
private int GetGeneratedPatternCount()
{
object diggingManager = GetDiggingManager();
if (diggingManager == null)
{
return -1;
}
return GetCollectionCount(GetMember(diggingManager, "generatedPatterns"));
}
private int CountUnopenedChests()
{
int num = 0;
num += CountType("Chest");
return num + CountType("TreasureChest");
}
private int CountType(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 val in array2)
{
Component val2 = (Component)(object)((val is Component) ? val : null);
if (!((Object)(object)val2 == (Object)null) && Object.op_Implicit((Object)(object)val2.gameObject) && val2.gameObject.activeInHierarchy)
{
object member = GetMember(val2, "NetworkisOpened");
if (!(member is bool))
{
member = GetMember(val2, "isOpened");
}
if (member is bool && !(bool)member)
{
num++;
}
}
}
return num;
}
private static int GetCollectionCount(object value)
{
if (value == null)
{
return -1;
}
if (value is ICollection collection)
{
return collection.Count;
}
PropertyInfo property = value.GetType().GetProperty("Count", Inst);
if (property != null && property.PropertyType == typeof(int))
{
try
{
return (int)property.GetValue(value, null);
}
catch
{
}
}
if (!(value is IEnumerable enumerable))
{
return -1;
}
int num = 0;
foreach (object item in enumerable)
{
_ = item;
num++;
}
return num;
}
private static int TryInt(object value)
{
if (value == null)
{
return -1;
}
try
{
return Convert.ToInt32(value);
}
catch
{
return -1;
}
}
private static object GetMember(object target, string name)
{
if (target == null)
{
return null;
}
Type type = target.GetType();
FieldInfo field = type.GetField(name, Inst | Stat);
if (field != null)
{
try
{
return field.GetValue(target);
}
catch
{
}
}
PropertyInfo property = type.GetProperty(name, Inst | Stat);
if (property != null && property.GetIndexParameters().Length == 0)
{
try
{
return property.GetValue(target, null);
}
catch
{
}
}
return null;
}
private static Type FindGameType(string name)
{
Type type = Type.GetType(name + ", Assembly-CSharp");
if (type != null)
{
return type;
}
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
type = assembly.GetType(name);
if (type != null)
{
return type;
}
}
catch
{
}
}
return null;
}
private static object GetDiggingManager()
{
Type type = FindGameType("DiggingManager");
if (type == null)
{
return null;
}
FieldInfo fieldInfo = type.GetField("Instance", Stat) ?? type.GetField("instance", Stat);
if (!(fieldInfo == null))
{
return fieldInfo.GetValue(null);
}
return null;
}
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_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Expected O, but got Unknown
GameObject val = new GameObject("StonewardsRoomChestTrackerCanvas");
canvas = val.AddComponent<Canvas>();
canvas.renderMode = (RenderMode)0;
canvas.sortingOrder = 5000;
CanvasScaler val2 = val.AddComponent<CanvasScaler>();
val2.uiScaleMode = (ScaleMode)1;
val2.referenceResolution = new Vector2(1920f, 1080f);
val.AddComponent<GraphicRaycaster>();
Object.DontDestroyOnLoad((Object)(object)val);
GameObject val3 = new GameObject("TrackerPanel");
val3.transform.SetParent(((Component)canvas).transform, false);
panel = val3.AddComponent<RectTransform>();
background = val3.AddComponent<Image>();
ApplyUiBackground();
MakeText(val3.transform, "Title", "WORLD STATUS", 15, (FontStyle)1, out titleText);
MakeText(val3.transform, "Rooms", "Rooms left: ?", 20, (FontStyle)1, out roomsText);
MakeText(val3.transform, "Chests", "Chests left: ?", 20, (FontStyle)1, out chestsText);
MakeText(val3.transform, "Detail", "", 12, (FontStyle)0, out detailText);
ApplyUiLayout();
}
private void MakeText(Transform parent, string name, string value, int size, 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_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: 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 = size;
text.fontStyle = style;
((Graphic)text).color = Color.white;
text.alignment = (TextAnchor)3;
text.horizontalOverflow = (HorizontalWrapMode)1;
text.verticalOverflow = (VerticalWrapMode)1;
RectTransform rectTransform = ((Graphic)text).rectTransform;
rectTransform.anchorMin = new Vector2(0f, 1f);
rectTransform.anchorMax = new Vector2(1f, 1f);
rectTransform.pivot = new Vector2(0.5f, 1f);
}
private void ApplyUiIfChanged()
{
if (!((Object)(object)panel == (Object)null) && (lastW != panelWidth.Value || lastH != panelHeight.Value || lastX != positionX.Value || lastY != positionY.Value || lastScale != scale.Value || lastAlpha != alpha.Value || lastShowTotals != showFoundTotals.Value))
{
ApplyUiLayout();
ApplyUiBackground();
}
}
private void ApplyUiLayout()
{
//IL_0010: 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_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
panel.anchorMin = new Vector2(0f, 1f);
panel.anchorMax = new Vector2(0f, 1f);
panel.pivot = new Vector2(0f, 1f);
panel.sizeDelta = new Vector2(Mathf.Max(120f, panelWidth.Value), Mathf.Max(60f, panelHeight.Value));
panel.anchoredPosition = new Vector2(Mathf.Max(0f, positionX.Value), 0f - Mathf.Max(0f, positionY.Value));
((Transform)panel).localScale = Vector3.one * Mathf.Clamp(scale.Value, 0.25f, 3f);
LayoutText(titleText, 8f, -6f, 18f);
LayoutText(roomsText, 8f, -28f, 24f);
LayoutText(chestsText, 8f, -51f, 24f);
LayoutText(detailText, 8f, -72f, 16f);
lastW = panelWidth.Value;
lastH = panelHeight.Value;
lastX = positionX.Value;
lastY = positionY.Value;
lastScale = scale.Value;
lastShowTotals = showFoundTotals.Value;
}
private static void LayoutText(Text text, float x, float y, float height)
{
//IL_0016: 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)
if (!((Object)(object)text == (Object)null))
{
RectTransform rectTransform = ((Graphic)text).rectTransform;
rectTransform.offsetMin = new Vector2(x, y - height);
rectTransform.offsetMax = new Vector2(0f - x, y);
}
}
private void ApplyUiBackground()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)background == (Object)null))
{
((Graphic)background).color = new Color(0.02f, 0.03f, 0.05f, Mathf.Clamp01(alpha.Value));
lastAlpha = alpha.Value;
}
}
private void ShowCounts(string room, string chest, string detail)
{
roomsText.text = room;
chestsText.text = chest;
detailText.text = (showFoundTotals.Value ? detail : "");
}
private void UpdateUiVisibility()
{
if ((Object)(object)canvas != (Object)null)
{
((Component)canvas).gameObject.SetActive(trackerEnabled && IsCaveMap());
}
}
}
public struct TrackerStateMessage : NetworkMessage
{
public int roomsLeft;
public int roomsFound;
public int roomsTotal;
public int chestsLeft;
}