Decompiled source of Hearthwait v1.0.0

plugins/Hearthwait.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("blackhearthx")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Hover timers for everything on the homestead that takes time: mead, metal, honey, sap, cooking, crops, bushes, fires, eggs, growing animals, taming and birth.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+695c1f2f24a58dbb102cd97eb20c5c1aa44aa6b5")]
[assembly: AssemblyProduct("Hearthwait")]
[assembly: AssemblyTitle("Hearthwait")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 Hearthwait
{
	[BepInPlugin("com.blackhearthx.hearthwait", "Hearthwait", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class HearthwaitPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.blackhearthx.hearthwait";

		public const string PluginName = "Hearthwait";

		public const string PluginVersion = "1.0.0";

		internal static HearthwaitPlugin Instance { get; private set; }

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			Instance = this;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			ModLocalization.Register();
			HoverTimePatch.Apply(new Harmony("com.blackhearthx.hearthwait"));
			Logger.LogInfo((object)"Hearthwait 1.0.0 loaded");
		}
	}
	internal static class HoverBodyPatch
	{
		internal static void Postfix(Hud __instance, Player player)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)player == (Object)null || (Object)(object)__instance.m_hoverName == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)TextViewer.instance != (Object)null && TextViewer.instance.IsVisible())
				{
					return;
				}
			}
			catch
			{
			}
			GameObject hoverObject;
			try
			{
				hoverObject = ((Humanoid)player).GetHoverObject();
			}
			catch
			{
				return;
			}
			if ((Object)(object)hoverObject == (Object)null)
			{
				return;
			}
			try
			{
				string text = StationTimes.ForBodyHover(hoverObject);
				if (string.IsNullOrEmpty(text))
				{
					return;
				}
				string text2 = null;
				try
				{
					Hoverable componentInParent = hoverObject.GetComponentInParent<Hoverable>();
					if (componentInParent != null)
					{
						text2 = componentInParent.GetHoverText();
					}
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text2))
				{
					((TMP_Text)__instance.m_hoverName).text = text;
				}
				else if (HasSameClock(text2, text))
				{
					((TMP_Text)__instance.m_hoverName).text = text2;
				}
				else
				{
					((TMP_Text)__instance.m_hoverName).text = text2 + "\n" + text;
				}
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait body hover: " + ex.Message));
			}
		}

		private static bool HasSameClock(string baseText, string extra)
		{
			string text = StripColors(baseText);
			string text2 = StripColors(extra);
			if (text.IndexOf(text2, StringComparison.Ordinal) >= 0)
			{
				return true;
			}
			string[] array = text2.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text3 = array[i].Trim();
				if (text3.Length < 5)
				{
					continue;
				}
				string[] array2 = text3.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length >= 2)
				{
					string value = array2[0] + " " + array2[1];
					if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string StripColors(string s)
		{
			if (string.IsNullOrEmpty(s) || s.IndexOf('<') < 0)
			{
				return s;
			}
			char[] array = new char[s.Length];
			int length = 0;
			bool flag = false;
			foreach (char c in s)
			{
				switch (c)
				{
				case '<':
					flag = true;
					continue;
				case '>':
					flag = false;
					continue;
				}
				if (!flag)
				{
					array[length++] = c;
				}
			}
			return new string(array, 0, length);
		}
	}
	internal static class HoverStyle
	{
		internal const string Wait = "#E8B86D";

		internal const string Ready = "#9CCC8A";

		internal const string Warn = "#E08888";

		internal const string Pause = "#9AADC0";

		internal const string Soft = "#D2C4A8";

		internal static string Paint(string hex, string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "<color=" + hex + ">" + text + "</color>";
		}

		internal static string Waiting(string text)
		{
			return Paint("#E8B86D", text);
		}

		internal static string Done(string text)
		{
			return Paint("#9CCC8A", text);
		}

		internal static string Warning(string text)
		{
			return Paint("#E08888", text);
		}

		internal static string Paused(string text)
		{
			return Paint("#9AADC0", text);
		}
	}
	internal static class HoverTimePatch
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Expected O, but got Unknown
			Patch(harmony, typeof(Fermenter), "GetHoverText", "FermenterHover");
			Patch(harmony, typeof(Beehive), "GetHoverText", "BeehiveHover");
			Patch(harmony, typeof(SapCollector), "GetHoverText", "SapHover");
			Patch(harmony, typeof(CookingStation), "GetHoverText", "CookHover");
			Patch(harmony, typeof(Plant), "GetHoverText", "PlantHover");
			Patch(harmony, typeof(Pickable), "GetHoverText", "PickableHover");
			Patch(harmony, typeof(PickableItem), "GetHoverText", "PickableItemHover");
			Patch(harmony, typeof(Fireplace), "GetHoverText", "FireplaceHover");
			Patch(harmony, typeof(EggGrow), "GetHoverText", "EggHover");
			Patch(harmony, typeof(Tameable), "GetHoverText", "TameableHover");
			string[] array = new string[3] { "OnHoverAddOre", "OnHoverAddFuel", "OnHoverEmptyOre" };
			foreach (string method in array)
			{
				Patch(harmony, typeof(Smelter), method, "SmelterHover");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Hud), "UpdateCrosshair", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HoverBodyPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void Patch(Harmony harmony, Type type, string method, string postfix)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Logger.LogWarning((object)("Hearthwait: missing " + type.Name + "." + method));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HoverTimePatch), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void FermenterHover(Fermenter __instance, ref string __result)
		{
			Append(ref __result, StationTimes.FermenterText(__instance));
		}

		private static void BeehiveHover(Beehive __instance, ref string __result)
		{
			Append(ref __result, StationTimes.HiveText(__instance));
		}

		private static void SapHover(SapCollector __instance, ref string __result)
		{
			Append(ref __result, StationTimes.SapText(__instance));
		}

		private static void CookHover(CookingStation __instance, ref string __result)
		{
			Append(ref __result, StationTimes.CookText(__instance));
		}

		private static void PlantHover(Plant __instance, ref string __result)
		{
			Append(ref __result, StationTimes.PlantText(__instance));
		}

		private static void PickableHover(Pickable __instance, ref string __result)
		{
			Append(ref __result, StationTimes.PickableText(__instance));
		}

		private static void PickableItemHover(PickableItem __instance, ref string __result)
		{
			Append(ref __result, StationTimes.PickableItemText(__instance));
		}

		private static void FireplaceHover(Fireplace __instance, ref string __result)
		{
			Append(ref __result, StationTimes.FireplaceText(__instance));
		}

		private static void EggHover(EggGrow __instance, ref string __result)
		{
			Append(ref __result, StationTimes.EggText(__instance));
		}

		private static void TameableHover(Tameable __instance, ref string __result)
		{
			Append(ref __result, StationTimes.CreatureText(__instance));
		}

		private static void SmelterHover(Smelter __instance, ref string __result)
		{
			Append(ref __result, StationTimes.SmelterText(__instance));
		}

		private static void Append(ref string result, string extra)
		{
			if (!string.IsNullOrEmpty(extra))
			{
				if (string.IsNullOrEmpty(result))
				{
					result = extra;
				}
				else if (result.IndexOf(extra, StringComparison.Ordinal) < 0)
				{
					result = result + "\n" + extra;
				}
			}
		}
	}
	internal static class ModLocalization
	{
		internal static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			int num = 0;
			string[] array = new string[3] { "English", "Portuguese_Brazilian", "Portuguese_European" };
			foreach (string language in array)
			{
				if (TryLoad(localization, language))
				{
					num++;
				}
			}
			if (num == 0)
			{
				string text = "English";
				localization.AddTranslation(ref text, new Dictionary<string, string>
				{
					{ "hearthwait_ready_in", "Ready in {0}" },
					{ "hearthwait_ready_now", "Ready" },
					{ "hearthwait_next_in", "Next in {0}" },
					{ "hearthwait_full_in", "Full in {0}" },
					{ "hearthwait_full_ready", "Full — ready to collect" },
					{ "hearthwait_burn_in", "Burns in {0}" },
					{ "hearthwait_burnt", "Burnt" },
					{ "hearthwait_waiting_wind", "Waiting on the wind" },
					{ "hearthwait_plant_stalled", "Won't grow like this" },
					{ "hearthwait_ferment_paused", "Paused — needs cover" },
					{ "hearthwait_needs_fuel", "Needs fuel" },
					{ "hearthwait_empty", "Empty" },
					{ "hearthwait_picked", "Picked" },
					{ "hearthwait_grows_in", "Grows back in {0}" },
					{ "hearthwait_harvest_ready", "Ready — {0}" },
					{ "hearthwait_burns_for", "Burns for {0}" },
					{ "hearthwait_fire_out", "Fire is out" },
					{ "hearthwait_egg_stacked", "Split the stack to hatch" },
					{ "hearthwait_egg_cold", "Too cold to hatch" },
					{ "hearthwait_hatches_in", "Hatches in {0}" },
					{ "hearthwait_hatch_ready", "About to hatch" },
					{ "hearthwait_grows_up_in", "Grows up in {0}" },
					{ "hearthwait_grown_ready", "Ready to grow up" },
					{ "hearthwait_tames_in", "Tames in {0}" },
					{ "hearthwait_tame_ready", "Ready to tame" },
					{ "hearthwait_fed_for", "Fed for {0}" },
					{ "hearthwait_hungry", "Hungry" },
					{ "hearthwait_births_in", "Births in {0}" },
					{ "hearthwait_birth_ready", "About to give birth" },
					{ "hearthwait_love_points", "Love {0}/{1}" }
				});
				Logger.LogWarning((object)"Hearthwait: Translations folder missing — English inline fallback");
			}
		}

		private static bool TryLoad(CustomLocalization loc, string language)
		{
			try
			{
				string text = FindPath(language);
				if (string.IsNullOrEmpty(text) || !File.Exists(text))
				{
					return false;
				}
				loc.AddJsonFile(language, File.ReadAllText(text));
				return true;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Hearthwait: failed loading " + language + " — " + ex.Message));
				return false;
			}
		}

		private static string FindPath(string language)
		{
			string path = Path.Combine("Translations", language, "hearthwait.json");
			string text = Path.Combine(Path.GetDirectoryName(typeof(HearthwaitPlugin).Assembly.Location) ?? "", path);
			if (File.Exists(text))
			{
				return text;
			}
			string text2 = Path.Combine(Directory.GetCurrentDirectory(), path);
			if (!File.Exists(text2))
			{
				return null;
			}
			return text2;
		}

		internal static string L(string token, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + token) : ("$" + token));
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch
			{
				return text;
			}
		}
	}
	internal static class PluginConfig
	{
		internal static ConfigEntry<bool> EnableFermenter;

		internal static ConfigEntry<bool> EnableSmelter;

		internal static ConfigEntry<bool> EnableBeehive;

		internal static ConfigEntry<bool> EnableSap;

		internal static ConfigEntry<bool> EnableCooking;

		internal static ConfigEntry<bool> EnablePlant;

		internal static ConfigEntry<bool> EnablePickable;

		internal static ConfigEntry<bool> EnableFireplace;

		internal static ConfigEntry<bool> EnableEgg;

		internal static ConfigEntry<bool> EnableGrowup;

		internal static ConfigEntry<bool> EnableTame;

		internal static ConfigEntry<bool> EnablePregnant;

		internal static void Bind(ConfigFile cfg)
		{
			EnableFermenter = cfg.Bind<bool>("Hover timers", "Mead fermenter", true, "Remaining fermentation time.");
			EnableSmelter = cfg.Bind<bool>("Hover timers", "Smelters and mills", true, "Kiln, smelter, blast furnace, eitr, spinning wheel, windmill.");
			EnableBeehive = cfg.Bind<bool>("Hover timers", "Beehive", true, "Time until the next honey and until the hive is full.");
			EnableSap = cfg.Bind<bool>("Hover timers", "Sap collector", true, "Time until the next sap and until the collector is full.");
			EnableCooking = cfg.Bind<bool>("Hover timers", "Cooking and oven", true, "Time until food is done, and until it burns.");
			EnablePlant = cfg.Bind<bool>("Hover timers", "Crops and saplings", true, "Time until a planted crop or tree is grown.");
			EnablePickable = cfg.Bind<bool>("Hover timers", "Bushes and nature", true, "Ready label on bushes/berries/mushrooms; countdown when picked and growing back.");
			EnableFireplace = cfg.Bind<bool>("Hover timers", "Fires and torches", true, "How long the fire will keep burning.");
			EnableEgg = cfg.Bind<bool>("Hover timers", "Eggs hatching", true, "Time until a warm egg hatches.");
			EnableGrowup = cfg.Bind<bool>("Hover timers", "Young animals growing", true, "Time until a cub/chick/piglet grows up.");
			EnableTame = cfg.Bind<bool>("Hover timers", "Taming and feeding", true, "Time left to tame, and how long until hungry again.");
			EnablePregnant = cfg.Bind<bool>("Hover timers", "Pregnancy", true, "Time until offspring is born.");
		}
	}
	internal static class StationTimes
	{
		private static readonly MethodInfo CookGetSlot = AccessTools.Method(typeof(CookingStation), "GetSlot", (Type[])null, (Type[])null);

		private static readonly MethodInfo HiveSinceUpdate = AccessTools.Method(typeof(Beehive), "GetTimeSinceLastUpdate", (Type[])null, (Type[])null);

		private static readonly MethodInfo SapSinceUpdate = AccessTools.Method(typeof(SapCollector), "GetTimeSinceLastUpdate", (Type[])null, (Type[])null);

		private static readonly MethodInfo FermentElapsed = AccessTools.Method(typeof(Fermenter), "GetFermentationTime", (Type[])null, (Type[])null);

		internal static string ForBodyHover(GameObject hover)
		{
			if ((Object)(object)hover == (Object)null)
			{
				return null;
			}
			List<string> list = new List<string>();
			TryAdd(list, delegate
			{
				Smelter componentInParent = hover.GetComponentInParent<Smelter>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : SmelterText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Fermenter componentInParent = hover.GetComponentInParent<Fermenter>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : FermenterText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Beehive componentInParent = hover.GetComponentInParent<Beehive>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : HiveText(componentInParent);
			});
			TryAdd(list, delegate
			{
				SapCollector componentInParent = hover.GetComponentInParent<SapCollector>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : SapText(componentInParent);
			});
			TryAdd(list, delegate
			{
				CookingStation componentInParent = hover.GetComponentInParent<CookingStation>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : CookText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Fireplace componentInParent = hover.GetComponentInParent<Fireplace>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : FireplaceText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Plant componentInParent = hover.GetComponentInParent<Plant>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : PlantText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Pickable componentInParent = hover.GetComponentInParent<Pickable>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : PickableText(componentInParent);
			});
			TryAdd(list, delegate
			{
				EggGrow componentInParent = hover.GetComponentInParent<EggGrow>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : EggText(componentInParent);
			});
			TryAdd(list, delegate
			{
				Tameable componentInParent = hover.GetComponentInParent<Tameable>();
				return (!((Object)(object)componentInParent != (Object)null)) ? null : CreatureText(componentInParent);
			});
			if (list.Count != 0)
			{
				return string.Join("\n", list.ToArray());
			}
			return null;
		}

		private static void TryAdd(List<string> lines, Func<string> fn)
		{
			try
			{
				string text = fn();
				if (!string.IsNullOrEmpty(text) && !lines.Contains(text))
				{
					lines.Add(text);
				}
			}
			catch
			{
			}
		}

		internal static string FermenterText(Fermenter fer)
		{
			//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)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Invalid comparison between Unknown and I4
			if (!PluginConfig.EnableFermenter.Value || (Object)(object)fer == (Object)null || (Object)(object)fer.m_nview == (Object)null || !fer.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				Status status = fer.GetStatus();
				if ((int)status == 3)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				if ((int)status == 0)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_empty"));
				}
				if ((int)status != 1)
				{
					return null;
				}
				if (!fer.m_hasRoof || fer.m_exposed)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_ferment_paused"));
				}
				double fermentElapsedSeconds = GetFermentElapsedSeconds(fer);
				if (fermentElapsedSeconds < 0.0)
				{
					return null;
				}
				double num = (double)fer.m_fermentationDuration - fermentElapsedSeconds;
				if (num <= 0.5)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				return HoverStyle.Waiting(ModLocalization.L("hearthwait_ready_in", Format(num)));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait fermenter: " + ex.Message));
				return null;
			}
		}

		internal static string SmelterText(Smelter sm)
		{
			if (!PluginConfig.EnableSmelter.Value || (Object)(object)sm == (Object)null || (Object)(object)sm.m_nview == (Object)null || !sm.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				List<string> list = new List<string>();
				int num = 0;
				try
				{
					num = sm.GetProcessedQueueSize();
				}
				catch
				{
				}
				if (num > 0)
				{
					list.Add(HoverStyle.Done(ModLocalization.L("hearthwait_ready_now")));
				}
				int queueSize = sm.GetQueueSize();
				if (queueSize <= 0)
				{
					return (list.Count == 0) ? null : string.Join("\n", list.ToArray());
				}
				bool flag = (Object)(object)sm.m_fuelItem != (Object)null && sm.m_maxFuel > 0;
				if (flag && sm.GetFuel() < 1f)
				{
					list.Add(HoverStyle.Paused(ModLocalization.L("hearthwait_needs_fuel")));
					return string.Join("\n", list.ToArray());
				}
				float num2 = (((Object)(object)sm.m_windmill != (Object)null) ? sm.m_windmill.GetPowerOutput() : 1f);
				if (num2 <= 0.01f)
				{
					list.Add(HoverStyle.Paused(ModLocalization.L("hearthwait_waiting_wind")));
					return string.Join("\n", list.ToArray());
				}
				float num3 = sm.m_nview.GetZDO().GetFloat(ZDOVars.s_bakeTimer, 0f);
				double num4 = SecondsSinceZdoStart(sm.m_nview.GetZDO());
				float num5 = num3 + (float)num4 * num2;
				float num6 = Mathf.Max(0f, sm.m_secPerProduct - num5) / num2;
				float num7 = Mathf.Max(0f, (float)queueSize * sm.m_secPerProduct - num5) / num2;
				if (flag && sm.m_fuelPerProduct > 0)
				{
					float num8 = sm.GetFuel() * (sm.m_secPerProduct / (float)sm.m_fuelPerProduct) / num2;
					num7 = Mathf.Min(num7, num8);
					num6 = Mathf.Min(num6, num8);
				}
				if (num7 <= 0.5f)
				{
					list.Add(HoverStyle.Done(ModLocalization.L("hearthwait_ready_now")));
				}
				else
				{
					list.Add(HoverStyle.Waiting(ModLocalization.L("hearthwait_ready_in", Format(num7))));
					if (queueSize > 1 && num6 + 0.5f < num7)
					{
						list.Add(HoverStyle.Paint("#D2C4A8", ModLocalization.L("hearthwait_next_in", Format(num6))));
					}
				}
				return string.Join("\n", list.ToArray());
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait smelter: " + ex.Message));
				return null;
			}
		}

		internal static string HiveText(Beehive hive)
		{
			if (!PluginConfig.EnableBeehive.Value || (Object)(object)hive == (Object)null || (Object)(object)hive.m_nview == (Object)null || !hive.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				int honeyLevel = hive.GetHoneyLevel();
				if (honeyLevel >= hive.m_maxHoney)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_full_ready"));
				}
				float num = hive.m_nview.GetZDO().GetFloat("product", 0f) + InvokeFloat(HiveSinceUpdate, hive);
				float num2 = hive.m_secPerUnit - num;
				int num3 = hive.m_maxHoney - honeyLevel;
				float num4 = (float)(num3 - 1) * hive.m_secPerUnit + Mathf.Max(0f, num2);
				List<string> list = new List<string>();
				if (honeyLevel > 0)
				{
					list.Add(HoverStyle.Done(ModLocalization.L("hearthwait_ready_now")));
				}
				if (num2 > 0.5f)
				{
					list.Add(HoverStyle.Waiting(ModLocalization.L("hearthwait_next_in", Format(num2))));
				}
				if (num3 > 1 && num4 > num2 + 0.5f)
				{
					list.Add(HoverStyle.Paint("#D2C4A8", ModLocalization.L("hearthwait_full_in", Format(num4))));
				}
				return (list.Count == 0) ? null : string.Join("\n", list.ToArray());
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait hive: " + ex.Message));
				return null;
			}
		}

		internal static string SapText(SapCollector sap)
		{
			if (!PluginConfig.EnableSap.Value || (Object)(object)sap == (Object)null || (Object)(object)sap.m_nview == (Object)null || !sap.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				int level = sap.GetLevel();
				if (level >= sap.m_maxLevel)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_full_ready"));
				}
				float num = sap.m_nview.GetZDO().GetFloat("product", 0f) + InvokeFloat(SapSinceUpdate, sap);
				float num2 = sap.m_secPerUnit - num;
				int num3 = sap.m_maxLevel - level;
				float num4 = (float)(num3 - 1) * sap.m_secPerUnit + Mathf.Max(0f, num2);
				List<string> list = new List<string>();
				if (level > 0)
				{
					list.Add(HoverStyle.Done(ModLocalization.L("hearthwait_ready_now")));
				}
				if (num2 > 0.5f)
				{
					list.Add(HoverStyle.Waiting(ModLocalization.L("hearthwait_next_in", Format(num2))));
				}
				if (num3 > 1 && num4 > num2 + 0.5f)
				{
					list.Add(HoverStyle.Paint("#D2C4A8", ModLocalization.L("hearthwait_full_in", Format(num4))));
				}
				return (list.Count == 0) ? null : string.Join("\n", list.ToArray());
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait sap: " + ex.Message));
				return null;
			}
		}

		internal static string CookText(CookingStation station)
		{
			if (!PluginConfig.EnableCooking.Value || (Object)(object)station == (Object)null || station.m_slots == null)
			{
				return null;
			}
			try
			{
				List<string> list = new List<string>();
				for (int i = 0; i < station.m_slots.Length; i++)
				{
					if (!TryReadCookSlot(station, i, out var item, out var cooked))
					{
						continue;
					}
					ItemConversion itemConversion = station.GetItemConversion(item);
					if (itemConversion == null)
					{
						continue;
					}
					string text = Localization.instance.Localize(itemConversion.m_to.GetHoverName());
					float num = itemConversion.m_cookTime - cooked;
					if (num > 0.25f)
					{
						list.Add(HoverStyle.Waiting(text + " — " + ModLocalization.L("hearthwait_ready_in", Format(num))));
						continue;
					}
					float num2 = itemConversion.m_cookTime * 2f - cooked;
					if (num2 > 0.25f)
					{
						list.Add(HoverStyle.Done(text + " — " + ModLocalization.L("hearthwait_ready_now")) + "  " + HoverStyle.Warning(ModLocalization.L("hearthwait_burn_in", Format(num2))));
					}
					else
					{
						list.Add(HoverStyle.Warning(ModLocalization.L("hearthwait_burnt")));
					}
				}
				return (list.Count == 0) ? null : string.Join("\n", list.ToArray());
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait cook: " + ex.Message));
				return null;
			}
		}

		internal static string PlantText(Plant plant)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.EnablePlant.Value || (Object)(object)plant == (Object)null || (Object)(object)plant.m_nview == (Object)null || !plant.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				if ((int)plant.GetStatus() != 0)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_plant_stalled"));
				}
				float growTime = plant.GetGrowTime();
				double num = plant.TimeSincePlanted();
				double num2 = (double)growTime - num;
				if (num2 <= 0.5)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				return HoverStyle.Waiting(ModLocalization.L("hearthwait_ready_in", Format(num2)));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait plant: " + ex.Message));
				return null;
			}
		}

		internal static string PickableText(Pickable pick)
		{
			if (!PluginConfig.EnablePickable.Value || (Object)(object)pick == (Object)null)
			{
				return null;
			}
			try
			{
				if ((((Object)pick).name ?? "").IndexOf("surt", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return null;
				}
				if (!pick.m_picked)
				{
					string pickableItemLabel = GetPickableItemLabel(pick);
					if (!string.IsNullOrEmpty(pickableItemLabel))
					{
						return HoverStyle.Done(ModLocalization.L("hearthwait_harvest_ready", pickableItemLabel));
					}
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				if (pick.m_respawnTimeMinutes <= 0f || (Object)(object)pick.m_nview == (Object)null || !pick.m_nview.IsValid())
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_picked"));
				}
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_picked"));
				}
				double num = (double)pick.m_respawnTimeMinutes * 60.0;
				long num2 = pick.m_nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L);
				double num3 = num2 switch
				{
					0L => num, 
					1L => 0.0, 
					_ => num - (ZNet.instance.GetTime() - new DateTime(num2)).TotalSeconds, 
				};
				if (num3 <= 0.5)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				return HoverStyle.Waiting(ModLocalization.L("hearthwait_grows_in", Format(num3)));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait pickable: " + ex.Message));
				return null;
			}
		}

		internal static string PickableItemText(PickableItem item)
		{
			if (!PluginConfig.EnablePickable.Value || (Object)(object)item == (Object)null)
			{
				return null;
			}
			try
			{
				if (item.m_picked)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_picked"));
				}
				if ((Object)(object)item.m_itemPrefab == (Object)null)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_ready_now"));
				}
				string text = Localization.instance.Localize(item.m_itemPrefab.m_itemData.m_shared.m_name);
				return HoverStyle.Done(ModLocalization.L("hearthwait_harvest_ready", text));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait pickable item: " + ex.Message));
				return null;
			}
		}

		internal static string FireplaceText(Fireplace fire)
		{
			if (!PluginConfig.EnableFireplace.Value || (Object)(object)fire == (Object)null || (Object)(object)fire.m_nview == (Object)null || !fire.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				if (fire.m_infiniteFuel)
				{
					return null;
				}
				float num = Mathf.Max(0f, fire.m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f));
				if (num <= 0f)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_needs_fuel"));
				}
				if (!fire.IsBurning())
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_fire_out"));
				}
				if (fire.m_secPerFuel <= 0f)
				{
					return null;
				}
				float num2 = num * fire.m_secPerFuel;
				return HoverStyle.Waiting(ModLocalization.L("hearthwait_burns_for", Format(num2)));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait fireplace: " + ex.Message));
				return null;
			}
		}

		internal static string EggText(EggGrow egg)
		{
			if (!PluginConfig.EnableEgg.Value || (Object)(object)egg == (Object)null || (Object)(object)egg.m_nview == (Object)null || !egg.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				ItemDrop component = ((Component)egg).GetComponent<ItemDrop>();
				if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_stack > 1)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_egg_stacked"));
				}
				float num = egg.m_nview.GetZDO().GetFloat(ZDOVars.s_growStart, 0f);
				if (num <= 0f || (Object)(object)ZNet.instance == (Object)null)
				{
					return HoverStyle.Paused(ModLocalization.L("hearthwait_egg_cold"));
				}
				double num2 = (double)egg.m_growTime - (ZNet.instance.GetTimeSeconds() - (double)num);
				if (num2 <= 0.5)
				{
					return HoverStyle.Done(ModLocalization.L("hearthwait_hatch_ready"));
				}
				return HoverStyle.Waiting(ModLocalization.L("hearthwait_hatches_in", Format(num2)));
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait egg: " + ex.Message));
				return null;
			}
		}

		internal static string CreatureText(Tameable tame)
		{
			if ((Object)(object)tame == (Object)null || (Object)(object)tame.m_nview == (Object)null || !tame.m_nview.IsValid())
			{
				return null;
			}
			try
			{
				List<string> list = new List<string>();
				ZDO zDO = tame.m_nview.GetZDO();
				if (PluginConfig.EnableGrowup.Value)
				{
					Growup component = ((Component)tame).GetComponent<Growup>();
					if ((Object)(object)component != (Object)null)
					{
						string text = GrowupLine(component);
						if (!string.IsNullOrEmpty(text))
						{
							list.Add(text);
						}
					}
				}
				if (PluginConfig.EnablePregnant.Value)
				{
					Procreation component2 = ((Component)tame).GetComponent<Procreation>();
					if ((Object)(object)component2 != (Object)null)
					{
						string text2 = PregnantLine(component2, zDO);
						if (!string.IsNullOrEmpty(text2))
						{
							list.Add(text2);
						}
					}
				}
				if (PluginConfig.EnableTame.Value)
				{
					if (tame.IsTamed())
					{
						string text3 = FedLine(tame, zDO);
						if (!string.IsNullOrEmpty(text3))
						{
							list.Add(text3);
						}
					}
					else
					{
						float num = zDO.GetFloat(ZDOVars.s_tameTimeLeft, tame.m_tamingTime);
						if (num > 0.5f)
						{
							list.Add(HoverStyle.Waiting(ModLocalization.L("hearthwait_tames_in", Format(num))));
						}
						else
						{
							list.Add(HoverStyle.Done(ModLocalization.L("hearthwait_tame_ready")));
						}
					}
				}
				return (list.Count == 0) ? null : string.Join("\n", list.ToArray());
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Hearthwait creature: " + ex.Message));
				return null;
			}
		}

		private static string GrowupLine(Growup grow)
		{
			if (grow.m_growTime <= 0f || (Object)(object)ZNet.instance == (Object)null)
			{
				return null;
			}
			BaseAI component = ((Component)grow).GetComponent<BaseAI>();
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			double totalSeconds = component.GetTimeSinceSpawned().TotalSeconds;
			double num = (double)grow.m_growTime - totalSeconds;
			if (num <= 0.5)
			{
				return HoverStyle.Done(ModLocalization.L("hearthwait_grown_ready"));
			}
			return HoverStyle.Waiting(ModLocalization.L("hearthwait_grows_up_in", Format(num)));
		}

		private static string PregnantLine(Procreation proc, ZDO zdo)
		{
			long num = zdo.GetLong(ZDOVars.s_pregnant, 0L);
			if (num <= 0 || (Object)(object)ZNet.instance == (Object)null)
			{
				int num2 = zdo.GetInt(ZDOVars.s_lovePoints, 0);
				if (num2 > 0 && proc.m_requiredLovePoints > 0)
				{
					return HoverStyle.Paint("#D2C4A8", ModLocalization.L("hearthwait_love_points", num2, proc.m_requiredLovePoints));
				}
				return null;
			}
			double num3 = (double)proc.m_pregnancyDuration - (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds;
			if (num3 <= 0.5)
			{
				return HoverStyle.Done(ModLocalization.L("hearthwait_birth_ready"));
			}
			return HoverStyle.Waiting(ModLocalization.L("hearthwait_births_in", Format(num3)));
		}

		private static string FedLine(Tameable tame, ZDO zdo)
		{
			long num = zdo.GetLong(ZDOVars.s_tameLastFeeding, 0L);
			if (num <= 0 || (Object)(object)ZNet.instance == (Object)null || tame.m_fedDuration <= 0f)
			{
				return HoverStyle.Paused(ModLocalization.L("hearthwait_hungry"));
			}
			double num2 = (double)tame.m_fedDuration - (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds;
			if (num2 <= 0.5)
			{
				return HoverStyle.Paused(ModLocalization.L("hearthwait_hungry"));
			}
			return HoverStyle.Waiting(ModLocalization.L("hearthwait_fed_for", Format(num2)));
		}

		private static double GetFermentElapsedSeconds(Fermenter fer)
		{
			if (FermentElapsed != null)
			{
				try
				{
					object obj = FermentElapsed.Invoke(fer, null);
					if (obj is double result)
					{
						return result;
					}
					if (obj is float num)
					{
						return num;
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return -1.0;
			}
			long num2 = fer.m_nview.GetZDO().GetLong(ZDOVars.s_startTime, 0L);
			if (num2 <= 0)
			{
				return -1.0;
			}
			return (ZNet.instance.GetTime() - new DateTime(num2)).TotalSeconds;
		}

		private static double SecondsSinceZdoStart(ZDO zdo)
		{
			if (zdo == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return 0.0;
			}
			long num = zdo.GetLong(ZDOVars.s_startTime, 0L);
			if (num <= 0)
			{
				return 0.0;
			}
			double totalSeconds = (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds;
			if (!(totalSeconds < 0.0))
			{
				return totalSeconds;
			}
			return 0.0;
		}

		private static bool TryReadCookSlot(CookingStation station, int index, out string item, out float cooked)
		{
			item = "";
			cooked = 0f;
			if (CookGetSlot == null)
			{
				return false;
			}
			ParameterInfo[] parameters = CookGetSlot.GetParameters();
			object[] array = new object[parameters.Length];
			array[0] = index;
			for (int i = 1; i < parameters.Length; i++)
			{
				Type type = parameters[i].ParameterType;
				if (type.IsByRef)
				{
					type = type.GetElementType();
				}
				array[i] = ((type != null && type.IsValueType) ? Activator.CreateInstance(type) : null);
			}
			CookGetSlot.Invoke(station, array);
			item = ((array.Length > 1) ? ((array[1] as string) ?? "") : "");
			if (array.Length > 2 && array[2] is float num)
			{
				cooked = num;
			}
			return !string.IsNullOrEmpty(item);
		}

		private static string GetPickableItemLabel(Pickable pick)
		{
			try
			{
				if ((Object)(object)pick.m_itemPrefab != (Object)null)
				{
					ItemDrop component = pick.m_itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)component != (Object)null && component.m_itemData?.m_shared != null)
					{
						return Localization.instance.Localize(component.m_itemData.m_shared.m_name);
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static float InvokeFloat(MethodInfo method, object instance)
		{
			if (method == null || instance == null)
			{
				return 0f;
			}
			object obj = method.Invoke(instance, null);
			if (obj is float)
			{
				return (float)obj;
			}
			if (obj is double num)
			{
				return (float)num;
			}
			return 0f;
		}

		internal static string Format(double seconds)
		{
			if (seconds < 0.0)
			{
				seconds = 0.0;
			}
			int num = Math.Max(0, (int)Math.Floor(seconds));
			int num2 = num / 86400;
			int num3 = num % 86400 / 3600;
			int num4 = num % 3600 / 60;
			int num5 = num % 60;
			if (num2 > 0)
			{
				return $"{num2}d {num3}h";
			}
			if (num3 > 0)
			{
				return $"{num3}h {num4:D2}m";
			}
			if (num4 > 0)
			{
				return $"{num4}m {num5:D2}s";
			}
			return $"{num5}s";
		}
	}
}