Decompiled source of StockAlert v1.0.1

StockAlert.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Eremite;
using Eremite.Model;
using Eremite.Services;
using Eremite.View.HUD;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using StockAlert.Config;
using StockAlert.Core.Models;
using StockAlert.Game;
using StockAlert.Game.Discovery;
using StockAlert.Infrastructure.Bootstrap;
using StockAlert.Infrastructure.Plugin;
using StockAlert.UI.HUD;
using StockAlert.UI.Panels;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("StockAlert")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+12e76e031af0b444b32feeca2fc4d911e073def9")]
[assembly: AssemblyProduct("StockAlert")]
[assembly: AssemblyTitle("StockAlert")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 StockAlert
{
	public static class StockAlertInfo
	{
		public const string Id = "com.stockalert.ats";

		public const string Name = "StockAlert";

		public const string Version = "1.0.0";
	}
}
namespace StockAlert.UI.Panels
{
	public static class UI
	{
		private sealed class SettingsPanelBehaviour : MonoBehaviour
		{
			private readonly Dictionary<string, string> _thresholdInputs = new Dictionary<string, string>();

			private Rect _windowRect = new Rect(40f, 40f, 480f, 640f);

			private Vector2 _scrollPosition;

			public bool Visible { get; private set; }

			public void Toggle()
			{
				Visible = !Visible;
			}

			private void OnGUI()
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				if (Visible)
				{
					_windowRect = GUILayout.Window(((Object)this).GetInstanceID(), _windowRect, new WindowFunction(DrawWindow), "Stock Alert Settings", Array.Empty<GUILayoutOption>());
				}
			}

			private void DrawWindow(int windowId)
			{
				//IL_0011: 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_0074: 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_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Unknown result type (might be due to invalid IL or missing references)
				GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
				KeyboardShortcut toggleSettingsKey = ConfigManager.ToggleSettingsKey;
				GUILayout.Label("Toggle key: " + ((object)(KeyboardShortcut)(ref toggleSettingsKey)).ToString(), Array.Empty<GUILayoutOption>());
				GUILayout.Label("Thresholds mirror the game's production limits.", Array.Empty<GUILayoutOption>());
				GUILayout.Space(8f);
				if (Discovery.Goods.Count == 0)
				{
					GUILayout.Label("No goods discovered yet.", Array.Empty<GUILayoutOption>());
				}
				_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
				foreach (GoodInfo item in Discovery.Goods.OrderBy((GoodInfo g) => g.DisplayName))
				{
					DrawGoodRow(item);
				}
				GUILayout.EndScrollView();
				GUILayout.Space(8f);
				if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
				{
					Visible = false;
				}
				GUILayout.EndVertical();
				GUI.DragWindow(new Rect(0f, 0f, 10000f, 24f));
			}

			private void DrawGoodRow(GoodInfo good)
			{
				if (!_thresholdInputs.TryGetValue(good.Id, out var value))
				{
					value = good.Threshold.ToString();
					_thresholdInputs[good.Id] = value;
				}
				GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
				GUILayout.Label(good.DisplayName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) });
				GUILayout.Label("Stock: " + good.CurrentAmount, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
				GUILayout.Label("Limit", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) });
				GUILayout.Label(value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
				GUILayout.EndHorizontal();
			}

			public void RefreshInputs()
			{
				_thresholdInputs.Clear();
				foreach (GoodInfo good in Discovery.Goods)
				{
					_thresholdInputs[good.Id] = good.Threshold.ToString();
				}
			}
		}

		private static GameObject _uiRoot;

		private static SettingsPanelBehaviour _panel;

		public static void Initialize()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			if (!((Object)(object)_uiRoot != (Object)null))
			{
				Plugin.Log("UI.Initialize()");
				_uiRoot = new GameObject("StockAlertUI");
				Object.DontDestroyOnLoad((Object)(object)_uiRoot);
				_panel = _uiRoot.AddComponent<SettingsPanelBehaviour>();
			}
		}

		public static void Refresh()
		{
			_panel?.RefreshInputs();
		}

		public static void Toggle()
		{
			if (!((Object)(object)_panel == (Object)null))
			{
				_panel.Toggle();
				Plugin.Log("UI toggled: " + _panel.Visible);
			}
		}
	}
}
namespace StockAlert.UI.HUD
{
	public static class HUD
	{
		private sealed class AlertHudBehaviour : MonoBehaviour
		{
			private const float IconSize = 22.5f;

			private const float RowHeight = 24f;

			private const float BoxMargin = 16f;

			private const float BoxPaddingX = 20f;

			private const int MaxRowsPerColumn = 15;

			private const float ColumnGap = 12f;

			private GUIStyle _lineStyle;

			private GUIStyle _boxStyle;

			private readonly HashSet<string> _activeAlertIds = new HashSet<string>();

			private readonly Dictionary<string, long> _alertOrder = new Dictionary<string, long>();

			private long _nextAlertOrder;

			private void OnGUI()
			{
				//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
				List<GoodInfo> list = Discovery.Goods.Where((GoodInfo g) => g.IsBelowThreshold).ToList();
				if (list.Count == 0)
				{
					_activeAlertIds.Clear();
					_alertOrder.Clear();
					return;
				}
				RefreshAlertOrdering(list);
				list = (from g in list
					orderby _alertOrder.TryGetValue(g.Id, out var value) ? value : 0 descending, g.DisplayName
					select g).ToList();
				EnsureStyles();
				int num = Mathf.Min(15, list.Count);
				int num2 = ((list.Count > 15) ? (list.Count - 15) : 0);
				List<GoodInfo> goods = list.Take(num).ToList();
				List<GoodInfo> goods2 = ((num2 > 0) ? list.Skip(15).Take(15).ToList() : new List<GoodInfo>());
				float columnWidth = GetColumnWidth(goods);
				float num3 = ((num2 > 0) ? GetColumnWidth(goods2) : 0f);
				float num4 = columnWidth + ((num2 > 0) ? (12f + num3) : 0f);
				float num5 = Mathf.Min(num4 + 20f, (float)Screen.width - 32f);
				int num6 = Mathf.Max(num, num2);
				float num7 = (float)num6 * 24f;
				int num8 = _boxStyle.padding.top + _boxStyle.padding.bottom;
				float num9 = num7 + (float)num8;
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)Screen.width - num5 - 16f, (float)Screen.height - num9 - 16f, num5, num9);
				GUI.color = new Color(1f, 0.9f, 0.9f, 0.96f);
				GUILayout.BeginArea(val, _boxStyle);
				GUI.color = Color.white;
				if (num2 > 0)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					DrawColumn(goods, columnWidth);
					GUILayout.Space(12f);
					DrawColumn(goods2, num3);
					GUILayout.EndHorizontal();
				}
				else
				{
					DrawColumn(goods, columnWidth);
				}
				GUILayout.EndArea();
			}

			private void EnsureStyles()
			{
				//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_0027: Expected O, but got Unknown
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: 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_0050: Expected O, but got Unknown
				//IL_0056: Expected O, but got Unknown
				if (_lineStyle == null)
				{
					_lineStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = 16
					};
				}
				if (_boxStyle == null)
				{
					_boxStyle = new GUIStyle(GUI.skin.box)
					{
						padding = new RectOffset(10, 10, 8, 8)
					};
				}
			}

			private void DrawColumn(IReadOnlyList<GoodInfo> goods, float width)
			{
				GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) });
				foreach (GoodInfo good in goods)
				{
					DrawAlertLine(good);
				}
				GUILayout.EndVertical();
			}

			private void RefreshAlertOrdering(IReadOnlyList<GoodInfo> belowThreshold)
			{
				HashSet<string> currentIds = new HashSet<string>(belowThreshold.Select((GoodInfo g) => g.Id));
				foreach (GoodInfo item in belowThreshold)
				{
					if (!_activeAlertIds.Contains(item.Id))
					{
						_activeAlertIds.Add(item.Id);
						_alertOrder[item.Id] = ++_nextAlertOrder;
					}
				}
				List<string> list = _activeAlertIds.Where((string id) => !currentIds.Contains(id)).ToList();
				foreach (string item2 in list)
				{
					_activeAlertIds.Remove(item2);
					_alertOrder.Remove(item2);
				}
			}

			private float GetColumnWidth(IReadOnlyList<GoodInfo> goods)
			{
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Expected O, but got Unknown
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				float num = 0f;
				foreach (GoodInfo good in goods)
				{
					string text = $"{good.DisplayName}: {good.CurrentAmount}/{good.Threshold}";
					float num2 = _lineStyle.CalcSize(new GUIContent(text)).x + 22.5f + 8f;
					if (num2 > num)
					{
						num = num2;
					}
				}
				return Mathf.Max(180f, num);
			}

			private void DrawAlertLine(GoodInfo good)
			{
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_011b: Unknown result type (might be due to invalid IL or missing references)
				GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) });
				if ((Object)(object)good.Icon != (Object)null && (Object)(object)good.Icon.texture != (Object)null)
				{
					Rect rect = GUILayoutUtility.GetRect(22.5f, 22.5f, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(22.5f),
						GUILayout.Height(22.5f)
					});
					Rect textureRect = good.Icon.textureRect;
					float num = ((Rect)(ref textureRect)).x / (float)((Texture)good.Icon.texture).width;
					textureRect = good.Icon.textureRect;
					float num2 = ((Rect)(ref textureRect)).y / (float)((Texture)good.Icon.texture).height;
					textureRect = good.Icon.textureRect;
					float num3 = ((Rect)(ref textureRect)).width / (float)((Texture)good.Icon.texture).width;
					textureRect = good.Icon.textureRect;
					Rect val = default(Rect);
					((Rect)(ref val))..ctor(num, num2, num3, ((Rect)(ref textureRect)).height / (float)((Texture)good.Icon.texture).height);
					GUI.DrawTextureWithTexCoords(rect, (Texture)(object)good.Icon.texture, val);
				}
				else
				{
					GUILayout.Label(string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(22.5f),
						GUILayout.Height(22.5f)
					});
				}
				GUILayout.Label($"{good.DisplayName}: {good.CurrentAmount}/{good.Threshold}", _lineStyle, Array.Empty<GUILayoutOption>());
				GUILayout.EndHorizontal();
			}
		}

		private static GameObject _hudRoot;

		public static void Initialize()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			if (!((Object)(object)_hudRoot != (Object)null))
			{
				Plugin.Log("HUD.Initialize()");
				_hudRoot = new GameObject("StockAlertHUD");
				Object.DontDestroyOnLoad((Object)(object)_hudRoot);
				_hudRoot.AddComponent<AlertHudBehaviour>();
			}
		}
	}
}
namespace StockAlert.Infrastructure.Utils
{
	public static class Utils
	{
		public static Texture2D SpriteToTexture(Sprite sprite)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if ((Object)(object)sprite == (Object)null)
			{
				return null;
			}
			Rect textureRect = sprite.textureRect;
			Texture2D val = new Texture2D((int)((Rect)(ref textureRect)).width, (int)((Rect)(ref textureRect)).height);
			Color[] pixels = sprite.texture.GetPixels((int)((Rect)(ref textureRect)).x, (int)((Rect)(ref textureRect)).y, (int)((Rect)(ref textureRect)).width, (int)((Rect)(ref textureRect)).height);
			val.SetPixels(pixels);
			val.Apply();
			return val;
		}
	}
}
namespace StockAlert.Infrastructure.Plugin
{
	[BepInPlugin("StockAlert", "Stock Alert", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static Plugin Instance;

		private Harmony _harmony;

		private bool _gameReadyInitialized;

		private void Awake()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			Instance = this;
			Log("StockAlert loaded");
			ConfigManager.Load();
			_harmony = new Harmony("StockAlert.Harmony");
			_harmony.PatchAll();
			Log("Harmony patches applied");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		public static void Log(string msg)
		{
			Logger.CreateLogSource("Stock Alert").LogInfo((object)msg);
		}

		public void OnGameReady()
		{
			if (_gameReadyInitialized)
			{
				Log("OnGameReady() skipped - already initialized");
				return;
			}
			_gameReadyInitialized = true;
			Log("OnGameReady() fired - initializing mod");
			Discovery.Initialize();
			HUD.Initialize();
			StockAlert.UI.Panels.UI.Initialize();
			StockAlert.UI.Panels.UI.Refresh();
			StockAlertRuntime.Initialize();
		}
	}
}
namespace StockAlert.Infrastructure.Hooks
{
	[HarmonyPatch(typeof(GoodsHUD), "SetUp")]
	internal static class GoodsHudReadyPatch
	{
		private static void Postfix(GoodsHUD __instance)
		{
			StockAlert.Infrastructure.Plugin.Plugin.Log("GoodsHUD.SetUp detected");
			StockAlert.Infrastructure.Plugin.Plugin.Instance?.OnGameReady();
		}
	}
	[HarmonyPatch(typeof(WorkshopsService), "SetGlobalLimitFor")]
	internal static class WorkshopsServiceSetGlobalLimitForPatch
	{
		private static void Postfix(string goodModel, int limit)
		{
			StockAlert.Infrastructure.Plugin.Plugin.Log($"Production limit updated: {goodModel} -> {limit}");
			ProductionLimitSync.SyncThresholds();
		}
	}
	[HarmonyPatch(typeof(WorkshopsService), "LoadLimits")]
	internal static class WorkshopsServiceLoadLimitsPatch
	{
		private static void Postfix()
		{
			StockAlert.Infrastructure.Plugin.Plugin.Log("Workshop global limits loaded");
			ProductionLimitSync.SyncThresholds();
		}
	}
	internal static class ProductionLimitSync
	{
		public static void SyncThresholds()
		{
			if (ConfigManager.RefreshGoodsFromProductionLimits(Discovery.Goods))
			{
				StockAlert.UI.Panels.UI.Refresh();
			}
		}
	}
}
namespace StockAlert.Infrastructure.Bootstrap
{
	internal sealed class StockAlertRuntime : MonoBehaviour
	{
		private static StockAlertRuntime _instance;

		private float _nextRefreshTime;

		public static void Initialize()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			if ((Object)(object)_instance != (Object)null)
			{
				((Behaviour)_instance).enabled = true;
				return;
			}
			GameObject val = new GameObject("StockAlertRuntime");
			Object.DontDestroyOnLoad((Object)(object)val);
			_instance = val.AddComponent<StockAlertRuntime>();
		}

		private void Awake()
		{
			StockAlert.Infrastructure.Plugin.Plugin.Log("StockAlertRuntime ready");
		}

		private void Update()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut toggleSettingsKey = ConfigManager.ToggleSettingsKey;
			if (((KeyboardShortcut)(ref toggleSettingsKey)).IsDown())
			{
				StockAlert.UI.Panels.UI.Toggle();
			}
			if (Time.unscaledTime >= _nextRefreshTime)
			{
				_nextRefreshTime = Time.unscaledTime + 1f;
				Discovery.UpdateStock();
			}
		}
	}
}
namespace StockAlert.Game
{
	public static class GameAPI
	{
		private static PropertyInfo _piMbSettings;

		private static PropertyInfo _piStorageService;

		private static PropertyInfo _piWorkshopsService;

		private static MethodInfo _miGetAmount;

		private static MethodInfo _miGetGlobalLimit;

		private static PropertyInfo _piWorkshopLimits;

		public static Settings GetSettings()
		{
			if (_piMbSettings == null)
			{
				_piMbSettings = typeof(MB).GetProperty("Settings", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			object? obj = _piMbSettings?.GetValue(null, null);
			return (Settings)((obj is Settings) ? obj : null);
		}

		public static int GetStoredAmount(GoodModel model, string fallbackId)
		{
			if ((Object)(object)model == (Object)null && string.IsNullOrWhiteSpace(fallbackId))
			{
				return 0;
			}
			try
			{
				if (_piStorageService == null)
				{
					_piStorageService = typeof(GameMB).GetProperty("StorageService", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				object obj = _piStorageService?.GetValue(null, null);
				if (obj == null)
				{
					return 0;
				}
				if (_miGetAmount == null)
				{
					_miGetAmount = obj.GetType().GetMethod("GetAmount", new Type[1] { typeof(string) });
				}
				if (_miGetAmount == null)
				{
					return 0;
				}
				string text = (((Object)(object)model != (Object)null) ? ((SO)model).Name : fallbackId);
				return (int)_miGetAmount.Invoke(obj, new object[1] { text });
			}
			catch (Exception)
			{
				return 0;
			}
		}

		public static Dictionary<string, int> GetProductionLimitsSnapshot()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			try
			{
				object workshopsService = GetWorkshopsService();
				if (workshopsService == null)
				{
					return dictionary;
				}
				if (_piWorkshopLimits == null)
				{
					_piWorkshopLimits = workshopsService.GetType().GetProperty("Limits", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if (!(_piWorkshopLimits?.GetValue(workshopsService, null) is IDictionary dictionary2))
				{
					return dictionary;
				}
				foreach (DictionaryEntry item in dictionary2)
				{
					if (item.Key is string key && item.Value is int value)
					{
						dictionary[key] = value;
					}
				}
			}
			catch (Exception)
			{
			}
			return dictionary;
		}

		public static int GetProductionLimit(GoodModel model, string fallbackId)
		{
			if (string.IsNullOrWhiteSpace(fallbackId))
			{
				return 0;
			}
			try
			{
				object workshopsService = GetWorkshopsService();
				if (workshopsService == null)
				{
					return 0;
				}
				if (_miGetGlobalLimit == null)
				{
					_miGetGlobalLimit = workshopsService.GetType().GetMethod("GetGlobalLimitFor", new Type[1] { typeof(string) });
				}
				if (_miGetGlobalLimit == null)
				{
					return 0;
				}
				return (int)_miGetGlobalLimit.Invoke(workshopsService, new object[1] { fallbackId });
			}
			catch (Exception)
			{
				return 0;
			}
		}

		private static object GetWorkshopsService()
		{
			if (_piWorkshopsService == null)
			{
				_piWorkshopsService = typeof(GameMB).GetProperty("WorkshopsService", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			return _piWorkshopsService?.GetValue(null, null);
		}
	}
}
namespace StockAlert.Game.Hooks
{
	public class GameReadyHook : MonoBehaviour
	{
		private void Awake()
		{
			Plugin.Log("GameReadyHook created, persistent, ID=" + ((Object)this).GetInstanceID());
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			Plugin.Log("Scene loaded: " + ((Scene)(ref scene)).name);
			if (((Scene)(ref scene)).name != "Game" && ((Scene)(ref scene)).name != "Gameplay")
			{
				Plugin.Log("Not gameplay scene, ignoring: " + ((Scene)(ref scene)).name);
				return;
			}
			Plugin.Log("Gameplay scene detected, searching for GameMB...");
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			foreach (GameObject val in rootGameObjects)
			{
				GameMB componentInChildren = val.GetComponentInChildren<GameMB>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Plugin.Log("Found REAL GameMB in gameplay scene");
					((MonoBehaviour)this).StartCoroutine(WaitForGameServices(componentInChildren));
					return;
				}
			}
			Plugin.Log("Gameplay scene loaded but GameMB not found yet.");
		}

		private IEnumerator WaitForGameServices(GameMB game)
		{
			Plugin.Log("Waiting for GameServices...");
			FieldInfo fi = typeof(GameMB).GetField("GameServices", BindingFlags.Instance | BindingFlags.NonPublic);
			while (fi.GetValue(game) == null)
			{
				Plugin.Log("GameServices still null...");
				yield return (object)new WaitForSeconds(0.5f);
			}
			Plugin.Log("GameServices READY — firing callback!");
			Plugin.Instance.OnGameReady();
		}
	}
	internal class StockAlertGameReadyHook : MonoBehaviour
	{
		private void Awake()
		{
			Plugin.Log("StockAlertGameReadyHook created, persistent, ID=" + ((Object)this).GetInstanceID());
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			SceneManager.sceneLoaded += OnSceneLoaded;
			Plugin.Log("Subscribed to sceneLoaded event");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			Plugin.Log($"OnSceneLoaded called on ID={((Object)this).GetInstanceID()} for scene {((Scene)(ref scene)).name}");
			if (((Scene)(ref scene)).name != "Game" && ((Scene)(ref scene)).name != "Gameplay")
			{
				return;
			}
			Plugin.Log("Gameplay scene detected, searching for GameMB...");
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			foreach (GameObject val in rootGameObjects)
			{
				GameMB componentInChildren = val.GetComponentInChildren<GameMB>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Plugin.Log("Found REAL GameMB in gameplay scene");
					((MonoBehaviour)this).StartCoroutine(WaitForGameServices(componentInChildren));
					return;
				}
			}
			Plugin.Log("Gameplay scene loaded but GameMB not found yet.");
		}

		private IEnumerator WaitForGameServices(GameMB game)
		{
			Plugin.Log("Waiting for GameServices...");
			FieldInfo fi = typeof(GameMB).GetField("GameServices", BindingFlags.Instance | BindingFlags.NonPublic);
			while (fi.GetValue(game) == null)
			{
				Plugin.Log("GameServices still null...");
				yield return (object)new WaitForSeconds(0.5f);
			}
			Plugin.Log("GameServices READY — firing callback!");
			Plugin.Instance.OnGameReady();
		}
	}
}
namespace StockAlert.Game.Discovery
{
	public static class Discovery
	{
		public static List<GoodInfo> Goods { get; } = new List<GoodInfo>();


		public static void Initialize()
		{
			Plugin.Log("Discovery.Initialize()");
			Goods.Clear();
			Settings settings = GameAPI.GetSettings();
			if ((Object)(object)settings == (Object)null)
			{
				Plugin.Log("Discovery.Initialize(): Settings is null");
				return;
			}
			GoodModel[] goods = settings.Goods;
			if (goods == null || goods.Length == 0)
			{
				Plugin.Log("Discovery.Initialize(): No goods found in Settings");
				return;
			}
			GoodModel[] array = goods;
			foreach (GoodModel val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					GoodInfo goodInfo = new GoodInfo
					{
						Model = val,
						Id = ((SO)val).Name,
						ConfigKey = BuildConfigKey(((SO)val).Name),
						DisplayName = ResolveDisplayName(val),
						Icon = val.icon,
						CurrentAmount = 0
					};
					ConfigManager.EnsureGoodConfig(goodInfo);
					Goods.Add(goodInfo);
				}
			}
			Goods.Sort((GoodInfo left, GoodInfo right) => string.Compare(left.DisplayName, right.DisplayName, StringComparison.OrdinalIgnoreCase));
			ConfigManager.RefreshGoodsFromProductionLimits(Goods);
			UpdateStock();
			Plugin.Log($"Discovery.Initialize(): Loaded {Goods.Count} goods");
		}

		private static string ResolveDisplayName(GoodModel model)
		{
			string text = ((object)model.displayName)?.ToString();
			if (!string.IsNullOrWhiteSpace(text) && text.IndexOf("Missing key", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return text;
			}
			if (!string.IsNullOrWhiteSpace(((SO)model).Name))
			{
				return ((SO)model).Name;
			}
			return "Unknown Good";
		}

		private static string BuildConfigKey(string rawId)
		{
			if (string.IsNullOrWhiteSpace(rawId))
			{
				return "UnknownGood";
			}
			StringBuilder stringBuilder = new StringBuilder(rawId.Length);
			foreach (char c in rawId)
			{
				switch (c)
				{
				case '\t':
				case '\n':
				case '"':
				case '\'':
				case '=':
				case '[':
				case '\\':
				case ']':
					stringBuilder.Append('_');
					break;
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			return stringBuilder.ToString();
		}

		public static void UpdateStock()
		{
			foreach (GoodInfo good in Goods)
			{
				if (!((Object)(object)good.Model == (Object)null))
				{
					good.CurrentAmount = GameAPI.GetStoredAmount(good.Model, good.Id);
				}
			}
		}
	}
}
namespace StockAlert.Core.Models
{
	public class GoodInfo
	{
		public GoodModel Model;

		public string Id;

		public string ConfigKey;

		public string DisplayName;

		public Sprite Icon;

		public int CurrentAmount;

		public int Threshold;

		public bool Enabled;

		public bool IsBelowThreshold => Enabled && CurrentAmount < Threshold;
	}
}
namespace StockAlert.Config
{
	public static class ConfigManager
	{
		private static ConfigFile _config;

		private static ConfigEntry<KeyboardShortcut> _toggleSettingsKey;

		public static KeyboardShortcut ToggleSettingsKey
		{
			get
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				Load();
				return _toggleSettingsKey.Value;
			}
		}

		public static void Load()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (_config == null)
			{
				_config = new ConfigFile(Paths.ConfigPath + "\\StockAlert.cfg", true);
				_toggleSettingsKey = _config.Bind<KeyboardShortcut>("General", "ToggleSettingsKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Key used to show or hide the Stock Alert settings window.");
			}
		}

		public static void EnsureGoodConfig(GoodInfo g)
		{
			Load();
			ApplyProductionLimit(g);
		}

		public static bool RefreshGoodsFromProductionLimits(IEnumerable<GoodInfo> goods)
		{
			bool flag = false;
			foreach (GoodInfo good in goods)
			{
				flag |= ApplyProductionLimit(good);
			}
			return flag;
		}

		public static bool RefreshGoodFromProductionLimit(GoodInfo good)
		{
			return ApplyProductionLimit(good);
		}

		private static bool ApplyProductionLimit(GoodInfo g)
		{
			int threshold = g.Threshold;
			int productionLimit = GameAPI.GetProductionLimit(g.Model, g.Id);
			g.Threshold = Mathf.Max(0, productionLimit);
			g.Enabled = g.Threshold > 0;
			if (threshold != g.Threshold)
			{
				Plugin.Log($"Synced threshold for {g.Id} => {g.Threshold}");
				return true;
			}
			return false;
		}
	}
}