Decompiled source of FoodMenuServingCounts v1.0.0

BepInEx/plugins/FoodMenuServingCounts.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using TMPro;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("FoodMenuServingCounts")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FoodMenuServingCounts")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c26237b0-d31e-4d87-9e33-d235560dffdc")]
[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")]
namespace FoodMenuServingCounts;

[BepInPlugin("FoodMenuServingCounts", "Food Menu Serving Counts", "1.0.0")]
public class FoodMenuServingCounts : BaseUnityPlugin
{
	public const string PluginGuid = "FoodMenuServingCounts";

	public const string PluginName = "Food Menu Serving Counts";

	public const string PluginVersion = "1.0.0";

	private void Awake()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		new Harmony("FoodMenuServingCounts").PatchAll(typeof(FoodMenuServingCounts).Assembly);
	}
}
internal static class ServingTableCountsCache
{
	private static bool _dirty = true;

	private static float _nextRecalcTime = 0f;

	private static readonly Dictionary<ushort, int> _counts = new Dictionary<ushort, int>(128);

	internal static IReadOnlyDictionary<ushort, int> GetCounts()
	{
		Ensure();
		return _counts;
	}

	internal static void MarkDirty()
	{
		_dirty = true;
	}

	private static void Ensure()
	{
		if (_dirty || !(Time.unscaledTime < _nextRecalcTime))
		{
			Recalc();
		}
	}

	private static void Recalc()
	{
		_dirty = false;
		_nextRecalcTime = Time.unscaledTime + 0.25f;
		_counts.Clear();
		ServingTableSlot[] array = Object.FindObjectsOfType<ServingTableSlot>(true);
		foreach (ServingTableSlot val in array)
		{
			if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).IsSpawned)
			{
				continue;
			}
			ServingTable componentInParent = ((Component)val).GetComponentInParent<ServingTable>(true);
			if (!((Object)(object)componentInParent != (Object)null) || !IsBedroomServingTable(componentInParent))
			{
				ushort value = val.itemDataId.Value;
				if (value != 0)
				{
					_counts.TryGetValue(value, out var value2);
					_counts[value] = value2 + 1;
				}
			}
		}
	}

	private static bool IsBedroomServingTable(ServingTable st)
	{
		try
		{
			if (st.IsBedroomTable)
			{
				return true;
			}
		}
		catch
		{
		}
		if (ContainsBedroom(((Object)(object)((Component)st).gameObject != (Object)null) ? ((Object)((Component)st).gameObject).name : null))
		{
			return true;
		}
		Transform val = ((Component)st).transform;
		while ((Object)(object)val != (Object)null)
		{
			if (ContainsBedroom(((Object)val).name))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static bool ContainsBedroom(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return false;
		}
		return name.IndexOf("Bedroom", StringComparison.OrdinalIgnoreCase) >= 0;
	}
}
internal static class ServingCountUI
{
	private sealed class ElementState
	{
		public ushort ItemDataId;

		public string BaseName;
	}

	private static readonly Dictionary<RecipeMenuElement, ElementState> _states = new Dictionary<RecipeMenuElement, ElementState>(64);

	private static readonly List<RecipeMenuElement> _remove = new List<RecipeMenuElement>(16);

	private static float _nextUiUpdateTime = 0f;

	private static FieldInfo _fiName;

	internal static void RegisterOrUpdate(RecipeMenuElement element, ushort itemDataId)
	{
		if ((Object)(object)element == (Object)null)
		{
			return;
		}
		TextMeshProUGUI nameText = GetNameText(element);
		if (!((Object)(object)nameText == (Object)null))
		{
			((TMP_Text)nameText).richText = true;
			if (!_states.TryGetValue(element, out var value))
			{
				value = new ElementState();
				_states[element] = value;
			}
			value.ItemDataId = itemDataId;
			value.BaseName = StripInjectedCount(((TMP_Text)nameText).text);
			UpdateElement(element, value, ServingTableCountsCache.GetCounts());
		}
	}

	internal static void UpdateAllIfNeeded()
	{
		if (Time.unscaledTime < _nextUiUpdateTime)
		{
			return;
		}
		_nextUiUpdateTime = Time.unscaledTime + 0.25f;
		IReadOnlyDictionary<ushort, int> counts = ServingTableCountsCache.GetCounts();
		foreach (KeyValuePair<RecipeMenuElement, ElementState> state in _states)
		{
			RecipeMenuElement key = state.Key;
			ElementState value = state.Value;
			if ((Object)(object)key == (Object)null)
			{
				_remove.Add(key);
			}
			else
			{
				UpdateElement(key, value, counts);
			}
		}
		for (int i = 0; i < _remove.Count; i++)
		{
			_states.Remove(_remove[i]);
		}
		_remove.Clear();
	}

	private static void UpdateElement(RecipeMenuElement element, ElementState st, IReadOnlyDictionary<ushort, int> counts)
	{
		if ((Object)(object)element == (Object)null || st == null)
		{
			return;
		}
		TextMeshProUGUI nameText = GetNameText(element);
		if (!((Object)(object)nameText == (Object)null))
		{
			((TMP_Text)nameText).richText = true;
			if (string.IsNullOrEmpty(st.BaseName))
			{
				st.BaseName = StripInjectedCount(((TMP_Text)nameText).text);
			}
			counts.TryGetValue(st.ItemDataId, out var value);
			string text = "×" + value;
			switch (value)
			{
			case 0:
				text = "<color=#FF4040>" + text + "</color>";
				break;
			case 1:
				text = "<color=#FFD200>" + text + "</color>";
				break;
			}
			((TMP_Text)nameText).text = st.BaseName + " " + text;
		}
	}

	private static string StripInjectedCount(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return s;
		}
		s = s.TrimEnd(Array.Empty<char>());
		if (s.EndsWith("</color>", StringComparison.OrdinalIgnoreCase))
		{
			int num = s.LastIndexOf("<color=", StringComparison.OrdinalIgnoreCase);
			if (num >= 0)
			{
				int num2 = s.LastIndexOf(' ', num);
				if (num2 >= 0)
				{
					return s.Substring(0, num2).TrimEnd(Array.Empty<char>());
				}
			}
		}
		int num3 = s.LastIndexOf('×');
		if (num3 >= 1 && s[num3 - 1] == ' ' && int.TryParse(s.Substring(num3 + 1), out var _))
		{
			return s.Substring(0, num3 - 1).TrimEnd(Array.Empty<char>());
		}
		return s;
	}

	private static TextMeshProUGUI GetNameText(RecipeMenuElement element)
	{
		if (_fiName == null)
		{
			_fiName = AccessTools.Field(typeof(RecipeMenuElement), "_name");
		}
		if (!(_fiName != null))
		{
			return null;
		}
		object? value = _fiName.GetValue(element);
		return (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
	}
}
[HarmonyPatch(typeof(RecipeMenuElement), "SetItem")]
internal static class Patch_RecipeMenuElement_SetItem
{
	private static void Postfix(RecipeMenuElement __instance, ItemData itemData)
	{
		if (!((Object)(object)__instance == (Object)null) && !((Object)(object)itemData == (Object)null))
		{
			ServingCountUI.RegisterOrUpdate(__instance, itemData.id);
		}
	}
}
[HarmonyPatch(typeof(FoodMenuUI), "Update")]
internal static class Patch_FoodMenuUI_Update
{
	private static void Postfix()
	{
		ServingCountUI.UpdateAllIfNeeded();
	}
}
[HarmonyPatch(typeof(ServingTableSlot), "OnItemDataIdChanged")]
internal static class Patch_ServingTableSlot_OnItemDataIdChanged
{
	private static void Postfix()
	{
		ServingTableCountsCache.MarkDirty();
	}
}