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;
}
}
}