using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.Localization.Tables; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("OldMarket.RottenUtil")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("OldMarket.RottenUtil")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d1173fe2-e39d-489d-b10d-f2b8bebd55b6")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [HarmonyPatch(typeof(UIManager), "OpenJournal")] public static class UIManager_OpenJournal_RottenStockPatch { private static void Postfix(UIManager __instance) { try { IEnumerable source = from x in Object.FindObjectsOfType() where (Object)(object)x != (Object)null && x.itemSO is ProductSO select x; var enumerable = from <>h__TransparentIdentifier0 in source.Select(delegate(Item item) { ItemSO itemSO3 = item.itemSO; return new { item = item, product = (ProductSO)(object)((itemSO3 is ProductSO) ? itemSO3 : null) }; }) group item by item.itemSO.id into g let itemSO = GameManager.Instance.GetItemById(g.Key) orderby itemSO.GetLocalizedName() select new { ItemId = g.Key, StackCount = g.Count(delegate(Item it) { ItemSO itemSO2 = it.itemSO; ProductSO val4 = (ProductSO)(object)((itemSO2 is ProductSO) ? itemSO2 : null); return (Object)(object)val4 == (Object)null || val4.maxDays <= 0 || !RottenUtil.IsExpired(val4, it.dayCounter.Value); }), TotalAmount = g.Sum(delegate(Item it) { ItemSO itemSO = it.itemSO; ProductSO val3 = (ProductSO)(object)((itemSO is ProductSO) ? itemSO : null); return (!((Object)(object)val3 != (Object)null) || val3.maxDays <= 0 || !RottenUtil.IsExpired(val3, it.dayCounter.Value)) ? it.amount.Value : 0; }) }; Transform listInventory = __instance.listInventory; for (int num = listInventory.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)listInventory.GetChild(num)).gameObject); } foreach (var item in enumerable) { if (item.StackCount <= 0 && item.TotalAmount <= 0) { continue; } ItemSO itemById = GameManager.Instance.GetItemById(item.ItemId); string localizedName = itemById.GetLocalizedName(); GameObject val = Object.Instantiate(__instance.prefabInventoryTile, listInventory); MonoBehaviour[] components = val.GetComponents(); MonoBehaviour[] array = components; foreach (MonoBehaviour val2 in array) { Type type = ((object)val2).GetType(); if (type.Name.Contains("InventoryTile")) { MethodInfo method = type.GetMethod("SetData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method == null)) { method.Invoke(val2, new object[3] { localizedName, item.StackCount, item.TotalAmount }); break; } } } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[JournalStock] OpenJournal postfix failed: {arg}"); } } } } [HarmonyPatch(typeof(UIManager), "Update")] public static class UIManager_Update_RottenOutlineHotkey_Patch { private static void Postfix() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) try { if (Keyboard.current != null) { Key value = RottenItemManager.OutlineToggleKey.Value; KeyControl val = Keyboard.current[value]; if (val != null && ((ButtonControl)val).wasPressedThisFrame) { RottenOutlineController.ToggleOutlineForExpiringItems(); } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenOutline] UIManager.Update hotkey patch failed: {arg}"); } } } } public static class RottenOutlineController { private static bool _enabled = false; private static readonly List _createdOutlines = new List(); public static bool IsEnabled => _enabled; public static int LastExpiringCount { get; private set; } public static int LastRottenCount { get; private set; } public static void ToggleOutlineForExpiringItems() { _enabled = !_enabled; if (_enabled) { EnableOutline(); } else { DisableOutline(); } } private static void EnableOutline() { //IL_0064: 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) try { _createdOutlines.RemoveAll((Outline o) => (Object)(object)o == (Object)null); List worldItemsExpiringTomorrow = RottenScan.GetWorldItemsExpiringTomorrow(); List worldRottenItems = RottenScan.GetWorldRottenItems(); LastExpiringCount = worldItemsExpiringTomorrow.Count; LastRottenCount = worldRottenItems.Count; foreach (Item item in worldItemsExpiringTomorrow) { AddOutlineIfPossible(item, Color.yellow); } foreach (Item item2 in worldRottenItems) { AddOutlineIfPossible(item2, Color.red); } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenOutline] EnableOutline failed: {arg}"); } } } private static void AddOutlineIfPossible(Item item, Color color) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).GetComponent() != (Object)null) { return; } try { Outline val = ((Component)item).gameObject.AddComponent(); val.OutlineMode = (Mode)0; val.OutlineWidth = 4f; val.OutlineColor = color; _createdOutlines.Add(val); } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenOutline] Failed to add outline to {((Object)item).name}: {arg}"); } } } private static void DisableOutline() { try { foreach (Outline createdOutline in _createdOutlines) { if ((Object)(object)createdOutline == (Object)null) { continue; } try { Object.Destroy((Object)(object)createdOutline); } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenOutline] Failed to destroy outline: {arg}"); } } } _createdOutlines.Clear(); LastExpiringCount = 0; LastRottenCount = 0; } catch (Exception arg2) { ManualLogSource log2 = RottenItemManager.Log; if (log2 != null) { log2.LogError((object)$"[RottenOutline] DisableOutline failed: {arg2}"); } } } } [HarmonyPatch(typeof(ItemSlot), "UpdateSlot")] public static class ItemSlot_UpdateSlot_RottenPatch { private static readonly Dictionary DefaultTmpColor = new Dictionary(); private static readonly Dictionary DefaultTextColor = new Dictionary(); private static readonly Dictionary DefaultImageColor = new Dictionary(); private static void Postfix(ItemSlot __instance, InventorySlot inventorySlot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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_0159: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) try { TextMeshProUGUI val = null; TextMeshProUGUI[] componentsInChildren = ((Component)__instance).GetComponentsInChildren(true); foreach (TextMeshProUGUI val2 in componentsInChildren) { if (int.TryParse(((TMP_Text)val2).text, out var _)) { val = val2; break; } } Text val3 = null; if ((Object)(object)val == (Object)null) { Text[] componentsInChildren2 = ((Component)__instance).GetComponentsInChildren(true); foreach (Text val4 in componentsInChildren2) { if (int.TryParse(val4.text, out var _)) { val3 = val4; break; } } } Image componentInChildren = ((Component)__instance).GetComponentInChildren(); if ((Object)(object)val != (Object)null && !DefaultTmpColor.ContainsKey(val)) { DefaultTmpColor[val] = ((Graphic)val).color; } if ((Object)(object)val3 != (Object)null && !DefaultTextColor.ContainsKey(val3)) { DefaultTextColor[val3] = ((Graphic)val3).color; } if ((Object)(object)componentInChildren != (Object)null && !DefaultImageColor.ContainsKey(componentInChildren)) { DefaultImageColor[componentInChildren] = ((Graphic)componentInChildren).color; } bool flag = false; if (inventorySlot.itemId != -1 && inventorySlot.amount > 0) { ItemSO itemById = GameManager.Instance.GetItemById(inventorySlot.itemId); ProductSO val5 = (ProductSO)(object)((itemById is ProductSO) ? itemById : null); if ((Object)(object)val5 != (Object)null && val5.maxDays > 0 && RottenUtil.IsExpired(val5, inventorySlot.dayCounter)) { flag = true; } } if (flag) { if ((Object)(object)val != (Object)null) { Color color = ((Graphic)val).color; ((Graphic)val).color = new Color(0.7f, 0.7f, 0.7f, color.a); } else if ((Object)(object)val3 != (Object)null) { Color color2 = ((Graphic)val3).color; ((Graphic)val3).color = new Color(0.7f, 0.7f, 0.7f, color2.a); } if ((Object)(object)componentInChildren != (Object)null) { Color color3 = ((Graphic)componentInChildren).color; ((Graphic)componentInChildren).color = new Color(0.7f, 0.7f, 0.7f, color3.a); } } else { if ((Object)(object)val != (Object)null && DefaultTmpColor.TryGetValue(val, out var value)) { ((Graphic)val).color = value; } if ((Object)(object)val3 != (Object)null && DefaultTextColor.TryGetValue(val3, out var value2)) { ((Graphic)val3).color = value2; } if ((Object)(object)componentInChildren != (Object)null && DefaultImageColor.TryGetValue(componentInChildren, out var value3)) { ((Graphic)componentInChildren).color = value3; } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"ItemSlot_UpdateSlot_RottenPatch failed: {arg}"); } } } } [HarmonyPatch(typeof(UIManager), "OpenSleepWarningPanel")] public static class UIManager_OpenSleepWarningPanel_RottenPatch { private static void Postfix(UIManager __instance) { try { if (RottenScan.HasItemsExpiringTomorrow()) { TextMeshProUGUI componentInChildren = __instance.panelSleepWarning.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null)) { string text = BuildLocalizedWarningMessage(); ((TMP_Text)componentInChildren).text = ((TMP_Text)componentInChildren).text + "\n" + text; } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[SleepWarning] Postfix failed: {arg}"); } } } private static string BuildLocalizedWarningMessage() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) string text = "DAYS LEFT"; string text2 = "en"; try { text = LocalizationSettings.StringDatabase.GetLocalizedString(TableReference.op_Implicit("Translations"), TableEntryReference.op_Implicit("days_left"), (Locale)null, (FallbackBehavior)0, Array.Empty()); Locale selectedLocale = LocalizationSettings.SelectedLocale; if ((Object)(object)selectedLocale != (Object)null) { LocaleIdentifier identifier = selectedLocale.Identifier; text2 = ((LocaleIdentifier)(ref identifier)).Code; } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[SleepWarning] Localization failed: {arg}"); } text = "DAYS LEFT"; text2 = "en"; } Key val = (Key)((RottenItemManager.OutlineToggleKey == null) ? 101 : ((int)RottenItemManager.OutlineToggleKey.Value)); string text3 = ((object)(Key)(ref val)).ToString(); string text4 = ((!text2.StartsWith("ja")) ? ("There are items with 1 " + text + " (" + text3 + " to highlight)") : ("1" + text + "の商品があります(" + text3 + "で表示)")); return "" + text4 + ""; } } public static class RottenScan { public static List GetWorldItemsExpiringTomorrow() { List list = new List(); try { Item[] array = Object.FindObjectsOfType(); Item[] array2 = array; foreach (Item val in array2) { if (!((Object)(object)val == (Object)null) && val.amount.Value > 0 && !val.IsInFreezer()) { ItemSO itemSO = val.itemSO; ProductSO val2 = (ProductSO)(object)((itemSO is ProductSO) ? itemSO : null); if (!((Object)(object)val2 == (Object)null) && val2.maxDays > 0 && !RottenUtil.IsExpired(val2, val.dayCounter.Value) && RottenUtil.IsExpireTomorrow(val2, val.dayCounter.Value)) { list.Add(val); } } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenScan] GetWorldItemsExpiringTomorrow failed: {arg}"); } } return list; } public static List GetWorldRottenItems() { List list = new List(); try { Item[] array = Object.FindObjectsOfType(); Item[] array2 = array; foreach (Item val in array2) { if (!((Object)(object)val == (Object)null) && val.amount.Value > 0) { ItemSO itemSO = val.itemSO; ProductSO val2 = (ProductSO)(object)((itemSO is ProductSO) ? itemSO : null); if (!((Object)(object)val2 == (Object)null) && val2.maxDays > 0 && RottenUtil.IsExpired(val2, val.dayCounter.Value)) { list.Add(val); } } } } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"[RottenScan] GetWorldRottenItems failed: {arg}"); } } return list; } public static bool HasItemsExpiringTomorrow() { List worldItemsExpiringTomorrow = GetWorldItemsExpiringTomorrow(); return worldItemsExpiringTomorrow != null && worldItemsExpiringTomorrow.Count > 0; } } [HarmonyPatch(typeof(PlayerInventory), "GetItemCount")] public static class PlayerInventory_GetItemCount_RottenPatch { private static void Postfix(PlayerInventory __instance, ItemSO itemSO, ref int __result) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) try { ProductSO val = (ProductSO)(object)((itemSO is ProductSO) ? itemSO : null); if ((Object)(object)val == (Object)null || val.maxDays <= 0) { return; } int num = 0; NetworkList slots = __instance.slots; for (int i = 0; i < slots.Count; i++) { InventorySlot val2 = slots[i]; if (val2.itemId == itemSO.id && val2.amount > 0) { ItemSO itemById = GameManager.Instance.GetItemById(val2.itemId); ProductSO val3 = (ProductSO)(object)((itemById is ProductSO) ? itemById : null); if (!((Object)(object)val3 == (Object)null) && !RottenUtil.IsExpired(val3, val2.dayCounter)) { num += val2.amount; } } } __result = num; } catch (Exception arg) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)$"PlayerInventory_GetItemCount_RottenPatch failed: {arg}"); } } } } [BepInPlugin("com.example.oldmarket.expiryitemmanager", "Old Market Expiry Item Manager", "1.0.2")] public class RottenItemManager : BaseUnityPlugin { public const string PluginGuid = "com.example.oldmarket.expiryitemmanager"; public const string PluginName = "Old Market Expiry Item Manager"; public const string PluginVersion = "1.0.2"; internal static ManualLogSource Log; internal static ConfigEntry OutlineToggleKey; internal static TextMeshProUGUI OutlineStatusText; private void Awake() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; OutlineToggleKey = ((BaseUnityPlugin)this).Config.Bind("Hotkeys", "OutlineToggleKey", (Key)101, "Key to toggle outline for expiring/rotten items."); Harmony val = new Harmony("com.example.oldmarket.expiryitemmanager"); val.PatchAll(); } } [HarmonyPatch(typeof(PlayerInteraction))] internal static class RottenOutlineUI_Patches { private static bool _lastEnabled; private static int _lastExpiring; private static int _lastRotten; [HarmonyPostfix] [HarmonyPatch("Start")] private static void PlayerInteraction_Start_Postfix(PlayerInteraction __instance) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)RottenItemManager.OutlineStatusText != (Object)null) && !((Object)(object)__instance.interactionUI == (Object)null)) { Canvas componentInParent = __instance.interactionUI.GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { GameObject val = new GameObject("OutlineStatusText"); val.transform.SetParent(((Component)componentInParent).transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(0f, 1f); val2.pivot = new Vector2(0f, 1f); val2.anchoredPosition = new Vector2(10f, -10f); val2.sizeDelta = new Vector2(800f, 24f); TextMeshProUGUI val3 = val.AddComponent(); ((TMP_Text)val3).fontSize = 18f; ((TMP_Text)val3).alignment = (TextAlignmentOptions)257; ((Graphic)val3).color = Color.white; ((TMP_Text)val3).enableWordWrapping = false; ((TMP_Text)val3).text = string.Empty; RottenItemManager.OutlineStatusText = val3; _lastEnabled = false; _lastExpiring = 0; _lastRotten = 0; } } } catch (Exception ex) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)("[ExpiryItemManager] Failed to create OutlineStatusText: " + ex)); } } } [HarmonyPostfix] [HarmonyPatch("Update")] private static void PlayerInteraction_Update_Postfix(PlayerInteraction __instance) { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI outlineStatusText = RottenItemManager.OutlineStatusText; if ((Object)(object)outlineStatusText == (Object)null) { return; } bool isEnabled; int lastExpiringCount; int lastRottenCount; try { isEnabled = RottenOutlineController.IsEnabled; lastExpiringCount = RottenOutlineController.LastExpiringCount; lastRottenCount = RottenOutlineController.LastRottenCount; } catch (Exception ex) { ManualLogSource log = RottenItemManager.Log; if (log != null) { log.LogError((object)("[ExpiryItemManager] Failed to read RottenOutlineController: " + ex)); } if (((Component)outlineStatusText).gameObject.activeSelf) { ((Component)outlineStatusText).gameObject.SetActive(false); } return; } if (!isEnabled) { if (((Component)outlineStatusText).gameObject.activeSelf) { ((Component)outlineStatusText).gameObject.SetActive(false); } _lastEnabled = false; _lastExpiring = lastExpiringCount; _lastRotten = lastRottenCount; } else if (_lastEnabled != isEnabled || _lastExpiring != lastExpiringCount || _lastRotten != lastRottenCount) { if (!((Component)outlineStatusText).gameObject.activeSelf) { ((Component)outlineStatusText).gameObject.SetActive(true); } Key val = (Key)((RottenItemManager.OutlineToggleKey == null) ? 101 : ((int)RottenItemManager.OutlineToggleKey.Value)); string arg = ((object)(Key)(ref val)).ToString(); ((TMP_Text)outlineStatusText).text = $"Outline({arg}): 1-day={lastExpiringCount}, rotten={lastRottenCount}"; _lastEnabled = isEnabled; _lastExpiring = lastExpiringCount; _lastRotten = lastRottenCount; } } } public static class RottenUtil { public static bool IsPerishable(ProductSO product) { return (Object)(object)product != (Object)null && product.maxDays > 0; } public static bool IsExpired(ProductSO product, int dayCounter) { return IsPerishable(product) && dayCounter >= product.maxDays; } public static bool IsExpireTomorrow(ProductSO product, int dayCounter) { if (!IsPerishable(product)) { return false; } int num = product.maxDays - dayCounter; return num == 1; } }