Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Hearthwait v1.0.0
plugins/Hearthwait.dll
Decompiled 4 days agousing 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"; } } }