Decompiled source of HiccupsQOLFixes v1.2.1
BepInEx/plugins/HiccupsQOLFixes/HiccupsQOLFixes.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HiccupsQOLFixes.Features; using I2.Loc; using Microsoft.CodeAnalysis; using Mirror; using PSS; using QFSW.QC; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using Wish; using ZeroFormatter; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("HiccupsQOLFixes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1")] [assembly: AssemblyProduct("HiccupsQOLFixes")] [assembly: AssemblyTitle("HiccupsQOLFixes")] [assembly: AssemblyVersion("1.2.1.0")] [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 HiccupsQOLFixes { [BepInPlugin("com.hiccups.sunhaven.qolfixes", "HiccupsQOLFixes", "1.2.1")] public sealed class Plugin : BaseUnityPlugin { private void Awake() { //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown RuntimeServices.Install(((BaseUnityPlugin)this).Logger); if (((BaseUnityPlugin)this).Config.Bind<bool>("Fruit Tree Storage", "Enabled", true, "Let fruit trees accumulate up to 99 fruit and show a native item bubble above three. Enable on every multiplayer participant. Restart after changing this setting.").Value) { FruitTreeStorage.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Chest Hover Names", "Enabled", true, "Show a chest's native name when hovering its visible graphics at any distance. Does not extend interaction range. Restart after changing this setting.").Value) { ChestHoverName.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Safe Sleep at Home", "Enabled", true, "At midnight inside a player home, start normal sleep without a bed or pass-out penalties. Enable on each player who wants this behavior. Restart after changing this setting.").Value) { SafeSleepAtHome.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Dialogue Escape", "Enabled", true, "Use Escape to select an available native exit response. Required dialogue keeps normal pause behavior. Restart after changing this setting.").Value) { DialogueEscape.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Crafting Browser", "Enabled", true, "Add fuzzy and ingredient search, favorites, recent recipes, and live craftable filtering to station recipe lists. Restart after changing this setting.").Value) { CraftingBrowser.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Crafting Locations", "Enabled", true, "Show crafting stations in item tooltips. Restart after changing this setting.").Value) { CraftingLocations.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Save Backups", "Enabled", true, "Keep five verified backups per character after successful saves. Stored locally under the game's save-data folder. Restart after changing this setting.").Value) { SaveBackups.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Chest Crafting Exclusion", "Enabled", true, "Show the chest toggle and exclude selected chests from crafting stations. Restart the game after changing this setting.").Value) { ChestCraftingExclusion.Install(new Harmony("com.hiccups.sunhaven.qolfixes.chestcrafting")); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Chest Crafting Exclusion enabled"); } ConfigEntry<bool> obj = ((BaseUnityPlugin)this).Config.Bind<bool>("Museum Donation", "Enabled", true, "Add Donate All Eligible to Covington's museum dialogue. Enable on every multiplayer participant. Restart after changing this setting."); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Museum Tooltip", "Enabled", true, "Show museum donation status in item tooltips. Restart after changing this setting."); if (obj.Value || val.Value) { MuseumCatalog.Instance = new MuseumCatalog(); } if (val.Value) { MuseumTooltip.Install(((BaseUnityPlugin)this).Logger); } if (((BaseUnityPlugin)this).Config.Bind<bool>("Crafting Station Settings", "Enabled", true, "Save and synchronize each station's chest, material, and sort toggles. Enable on every player. Restart after changing this setting.").Value) { CraftingStationSettings.Install(); } if (obj.Value) { MuseumDonation.Install(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Museum Donation enabled"); } } } } namespace HiccupsQOLFixes.Features { internal static class ChestCraftingExclusion { internal const string Marker = "\u2063\u2063\u2063"; private const string ButtonName = "ChestCraftingExclusionButton"; private static readonly FieldInfo DataField = AccessTools.Field(typeof(Chest), "data"); private static readonly FieldInfo UiField = AccessTools.Field(typeof(Chest), "ui"); private static readonly FieldInfo NameField = AccessTools.Field(typeof(Chest), "chestName"); internal static void Install(Harmony harmony) { harmony.PatchAll(typeof(CraftingInventoriesPatch)); harmony.PatchAll(typeof(ChestInteractPatch)); harmony.PatchAll(typeof(ChestNamePatch)); harmony.PatchAll(typeof(ChestInteractionTextPatch)); harmony.PatchAll(typeof(ChestMetaPatch)); } internal static ChestData GetData(Chest chest) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (ChestData)DataField.GetValue(chest); } internal static SunHavenInputField GetNameInput(Chest chest) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (SunHavenInputField)NameField.GetValue(chest); } internal static bool IsExcluded(Chest chest) { ChestData data = GetData(chest); return ((data != null) ? data.name : null)?.EndsWith("\u2063\u2063\u2063", StringComparison.Ordinal) ?? false; } internal static string Clean(string name) { return name?.Replace("\u2063\u2063\u2063", string.Empty); } internal static void EnsureButton(Chest chest) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown if (chest is HungryMonster) { return; } GameObject val = (GameObject)UiField.GetValue(chest); if ((Object)(object)val == (Object)null || !val.activeInHierarchy || GetData(chest) == null) { return; } if ((Object)(object)((IEnumerable<Transform>)val.GetComponentsInChildren<Transform>(true)).FirstOrDefault((Func<Transform, bool>)((Transform child) => ((Object)child).name == "ChestCraftingExclusionButton")) != (Object)null) { UpdateButton(chest); return; } NativeUiAssets.WithCraftingUI(delegate(CraftingUI template) { CreateToggle(chest, template); }); } private static void CreateToggle(Chest chest, CraftingUI template) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: 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_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || (Object)(object)template == (Object)null) { return; } GameObject val = (GameObject)UiField.GetValue(chest); if ((Object)(object)val == (Object)null || !val.activeInHierarchy || val.GetComponentsInChildren<Transform>(true).Any((Transform child) => ((Object)child).name == "ChestCraftingExclusionButton")) { return; } Button val2 = ((IEnumerable<Button>)val.GetComponentsInChildren<Button>(true)).FirstOrDefault((Func<Button, bool>)((Button button) => Enumerable.Range(0, ((UnityEventBase)button.onClick).GetPersistentEventCount()).Any((int index) => ((UnityEventBase)button.onClick).GetPersistentTarget(index) == (Object)(object)chest.sellingInventory && ((UnityEventBase)button.onClick).GetPersistentMethodName(index) == "Sort"))); if ((Object)(object)val2 == (Object)null) { return; } Toggle sortUseNearbyChests = template.sortUseNearbyChests; Graphic targetGraphic = ((Selectable)sortUseNearbyChests).targetGraphic; Image val3 = (Image)(object)((targetGraphic is Image) ? targetGraphic : null); Graphic graphic = sortUseNearbyChests.graphic; Image val4 = (Image)(object)((graphic is Image) ? graphic : null); TextMeshProUGUI componentInChildren = ((Component)sortUseNearbyChests).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)componentInChildren == (Object)null) { return; } GameObject val5 = new GameObject("ChestCraftingExclusionButton", new Type[4] { typeof(RectTransform), typeof(Image), typeof(Toggle), typeof(NavigationElement) }); val5.transform.SetParent(((Component)val2).transform.parent, false); ((Graphic)val5.GetComponent<Image>()).color = Color.clear; RectTransform val6 = (RectTransform)val5.transform; RectTransform val7 = (RectTransform)((Component)val2).transform; val6.anchorMin = val7.anchorMin; val6.anchorMax = val7.anchorMax; val6.pivot = new Vector2(0f, 0.5f); val6.sizeDelta = new Vector2(170f, val7.sizeDelta.y); val6.anchoredPosition = val7.anchoredPosition + new Vector2(val7.sizeDelta.x / 2f + 8f, 0f); Image val8 = NativeUiAssets.Image(val5.transform, "Background", val3); RectTransform rectTransform = ((Graphic)val8).rectTransform; RectTransform rectTransform2 = ((Graphic)val8).rectTransform; Vector2 val9 = default(Vector2); ((Vector2)(ref val9))..ctor(0f, 0.5f); rectTransform2.anchorMax = val9; rectTransform.anchorMin = val9; ((Graphic)val8).rectTransform.sizeDelta = new Vector2(12f, 12f); ((Graphic)val8).rectTransform.anchoredPosition = new Vector2(6f, 0f); Image val10 = NativeUiAssets.Image(((Component)val8).transform, "Checkmark", val4); ((Graphic)val10).rectTransform.anchorMin = Vector2.zero; ((Graphic)val10).rectTransform.anchorMax = Vector2.one; val9 = (((Graphic)val10).rectTransform.offsetMin = (((Graphic)val10).rectTransform.offsetMax = Vector2.zero)); TextMeshProUGUI component = new GameObject("Label", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).transform.SetParent(val5.transform, false); ((TMP_Text)component).font = ((TMP_Text)componentInChildren).font; ((TMP_Text)component).fontSharedMaterial = ((TMP_Text)componentInChildren).fontSharedMaterial; ((TMP_Text)component).fontSize = ((TMP_Text)componentInChildren).fontSize; ((Graphic)component).color = ((Graphic)componentInChildren).color; ((TMP_Text)component).text = "Use for crafting"; ((TMP_Text)component).alignment = (TextAlignmentOptions)4097; ((TMP_Text)component).enableWordWrapping = false; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).rectTransform.anchorMin = Vector2.zero; ((TMP_Text)component).rectTransform.anchorMax = Vector2.one; ((TMP_Text)component).rectTransform.offsetMin = new Vector2(18f, 0f); ((TMP_Text)component).rectTransform.offsetMax = Vector2.zero; Toggle component2 = val5.GetComponent<Toggle>(); ((Selectable)component2).targetGraphic = (Graphic)(object)val8; component2.graphic = (Graphic)(object)val10; ((Selectable)component2).transition = ((Selectable)sortUseNearbyChests).transition; ((Selectable)component2).colors = ((Selectable)sortUseNearbyChests).colors; component2.SetIsOnWithoutNotify(!IsExcluded(chest)); ((UnityEvent<bool>)(object)component2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool included) { ChestData data = GetData(chest); if (data != null) { data.name = Clean(data.name) ?? ""; if (!included) { data.name += "\u2063\u2063\u2063"; } chest.SetChestName(data.name); chest.UpdateChestForMultiplayer(); UpdateButton(chest); } }); } internal static void UpdateButton(Chest chest) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown GameObject val = (GameObject)UiField.GetValue(chest); if (!((Object)(object)val == (Object)null)) { Toggle val2 = ((IEnumerable<Toggle>)val.GetComponentsInChildren<Toggle>(true)).FirstOrDefault((Func<Toggle, bool>)((Toggle control) => ((Object)control).name == "ChestCraftingExclusionButton")); if ((Object)(object)val2 != (Object)null) { val2.SetIsOnWithoutNotify(!IsExcluded(chest)); } } } } [HarmonyPatch(typeof(CraftingTable), "get_allInventories")] internal static class CraftingInventoriesPatch { private static void Postfix(ref List<Inventory> __result) { __result.RemoveAll((Inventory inventory) => ChestManager.associatedChests.TryGetValue(inventory, out var value) && ChestCraftingExclusion.IsExcluded(value)); } } [HarmonyPatch(typeof(Chest), "Interact")] internal static class ChestInteractPatch { private static void Postfix(Chest __instance, int interactType) { if (interactType == 0) { ChestCraftingExclusion.EnsureButton(__instance); } } } [HarmonyPatch(typeof(Chest), "SetChestName")] internal static class ChestNamePatch { private static void Prefix(Chest __instance, ref string newName) { if (ChestCraftingExclusion.IsExcluded(__instance)) { newName = ChestCraftingExclusion.Clean(newName) + "\u2063\u2063\u2063"; } } private static void Postfix(Chest __instance) { SunHavenInputField nameInput = ChestCraftingExclusion.GetNameInput(__instance); if ((Object)(object)nameInput != (Object)null) { ChestData data = ChestCraftingExclusion.GetData(__instance); nameInput.SetTextWithoutNotify(ChestCraftingExclusion.Clean((data != null) ? data.name : null)); } } } [HarmonyPatch(typeof(Chest), "get_InteractionPoint")] internal static class ChestInteractionTextPatch { private static void Postfix(InteractionInfo __result, string ___interactTextKey, string ___interactText) { if (__result?.interactionText != null) { for (int i = 0; i < __result.interactionText.Count; i++) { string text = ChestCraftingExclusion.Clean(__result.interactionText[i]); __result.interactionText[i] = (string.IsNullOrWhiteSpace(text) ? LocalizeText.TranslateText(___interactTextKey, ___interactText) : text); } } } } [HarmonyPatch(typeof(Chest), "ReceiveNewMeta")] internal static class ChestMetaPatch { private static void Postfix(Chest __instance) { ChestCraftingExclusion.UpdateButton(__instance); } } internal static class ChestHoverName { private static readonly FieldInfo[] Glyphs = new FieldInfo[4] { AccessTools.Field(typeof(WorldInteraction), "interactionImage"), AccessTools.Field(typeof(WorldInteraction), "interaction2Image"), AccessTools.Field(typeof(WorldInteraction), "controllerInteractionImage"), AccessTools.Field(typeof(WorldInteraction), "controllerInteraction2Image") }; private static WorldInteraction preview; private static string shownName; internal static void Install(ManualLogSource logger) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown new Harmony("com.hiccups.sunhaven.qolfixes.chesthover").Patch((MethodBase)AccessTools.Method(typeof(PlayerInteractions), "LateUpdate", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ChestHoverName), "AfterInteractions", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); logger.LogInfo((object)"Chest hover names enabled"); } private static void Hide() { if (Object.op_Implicit((Object)(object)preview)) { ((Component)preview).gameObject.SetActive(false); } } private static void AfterInteractions(PlayerInteractions __instance, WorldInteraction ___worldInteraction, Interaction ___currentTarget) { //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) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) Player instance = Player.Instance; if ((Object)(object)((Component)__instance).GetComponentInParent<Player>() != (Object)(object)instance) { return; } GameManager instance2 = SingletonBehaviour<GameManager>.Instance; if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance2) || !Object.op_Implicit((Object)(object)instance.Camera) || MouseVisualManager.UsingController || GameManager.SceneTransitioning || UIHandler.InventoryOpen || UIHandler.UIWasOpenThisFrame || Cutscene.Active || !PlayerInput.AllowInput || (Object.op_Implicit((Object)(object)DialogueController.Instance) && DialogueController.Instance.DialogueOnGoing) || (Object.op_Implicit((Object)(object)EventSystem.current) && EventSystem.current.IsPointerOverGameObject())) { Hide(); return; } Ray val = instance.Camera.ScreenPointToRay(MouseVisualManager.mousePosition); Chest val2 = null; float num = float.PositiveInfinity; float num2 = default(float); foreach (Decoration value2 in instance2.objects.Values) { Chest val3 = (Chest)(object)((value2 is Chest) ? value2 : null); if (!Object.op_Implicit((Object)(object)val3) || val3 is HungryMonster || !((Component)val3).gameObject.activeInHierarchy) { continue; } Renderer renderer = ((Entity)val3).Renderer; if (Object.op_Implicit((Object)(object)renderer) && renderer.enabled && ((Component)renderer).gameObject.activeInHierarchy) { Bounds bounds = renderer.bounds; if (((Bounds)(ref bounds)).IntersectRay(val, ref num2) && num2 >= 0f && num2 < num) { val2 = val3; num = num2; } } } if (!Object.op_Implicit((Object)(object)val2) || (Object.op_Implicit((Object)(object)___worldInteraction) && ((Component)___worldInteraction).gameObject.activeInHierarchy && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)(object)val2)) { Hide(); return; } InteractionInfo interactionPoint = val2.InteractionPoint; string text = ((interactionPoint.interactionText != null && interactionPoint.interactionText.Count > 0) ? ChestCraftingExclusion.Clean(interactionPoint.interactionText[0]) : null); if (string.IsNullOrWhiteSpace(text)) { Hide(); return; } if (!Object.op_Implicit((Object)(object)preview)) { Prefabs instance3 = SingletonBehaviour<Prefabs>.Instance; if (!Object.op_Implicit((Object)(object)instance3) || !Object.op_Implicit((Object)(object)instance3.interaction)) { return; } preview = Object.Instantiate<WorldInteraction>(instance3.interaction); ((Object)preview).name = "HiccupsChestNamePreview"; CanvasGroup[] componentsInChildren = ((Component)preview).GetComponentsInChildren<CanvasGroup>(true); foreach (CanvasGroup obj in componentsInChildren) { obj.alpha = 1f; obj.interactable = false; obj.blocksRaycasts = false; } shownName = null; } ((Component)preview).gameObject.SetActive(true); preview.interactionObject.SetActive(true); ((Component)preview).transform.position = interactionPoint.interactionPoint + new Vector3(0.125f, -11.05f, -11.05f); if (!(shownName != text)) { return; } preview.SetInteractionText(new List<string> { text }); FieldInfo[] glyphs = Glyphs; for (int i = 0; i < glyphs.Length; i++) { object? value = glyphs[i].GetValue(preview); Transform val4 = (Transform)((value is Transform) ? value : null); if (Object.op_Implicit((Object)(object)val4)) { ((Component)val4).gameObject.SetActive(false); } } Graphic[] componentsInChildren2 = ((Component)preview).GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].raycastTarget = false; } shownName = text; } } internal sealed class CraftingBrowser : MonoBehaviour { private static readonly FieldInfo UIField = AccessTools.Field(typeof(CraftingTable), "craftingUI"); private static readonly FieldInfo RecipesField = AccessTools.Field(typeof(CraftingTable), "_craftingRecipes"); private static readonly FieldInfo OriginalField = AccessTools.Field(typeof(CraftingTable), "_saveCraftingRecipes"); private static readonly FieldInfo FilterField = AccessTools.Field(typeof(CraftingTable), "filter"); private static readonly FieldInfo TabField = AccessTools.Field(typeof(CraftingTable), "tabFilter"); private static readonly FieldInfo MatsField = AccessTools.Field(typeof(CraftingTable), "hasMats"); private static readonly FieldInfo PanelPrefab = AccessTools.Field(typeof(CraftingTable), "_craftingPanelPrefab"); private static readonly FieldInfo PanelsField = AccessTools.Field(typeof(CraftingTable), "craftingPanels"); private static ManualLogSource log; private CraftingTable table; private CraftingUI ui; private string query = ""; private int mode; private float rebuildAt = -1f; private float checkAt; private int craftableHash; private bool hashReady; private TextMeshProUGUI status; private readonly List<TextMeshProUGUI> modeLabels = new List<TextMeshProUGUI>(); internal static void Install(ManualLogSource logger) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown //IL_016d: Expected O, but got Unknown log = logger; Harmony val = new Harmony("com.hiccups.sunhaven.qolfixes.craftingbrowser"); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "OpenUI", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingBrowser), "Opened", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "Filter", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingBrowser), "Search", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "InitializeCraftingPanelsRoutine", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingBrowser), "RecipeRoutine", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "Refresh", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingBrowser), "Refreshed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingPanel), "Initialize", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingBrowser), "PanelInitialized", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "Craft", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingBrowser), "BeforeCraft", (Type[])null), new HarmonyMethod(typeof(CraftingBrowser), "AfterCraft", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"Crafting Browser enabled"); } private static CraftingBrowser Get(CraftingTable table) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown CraftingBrowser craftingBrowser = ((Component)table).GetComponent<CraftingBrowser>(); if ((Object)(object)craftingBrowser == (Object)null) { craftingBrowser = ((Component)table).gameObject.AddComponent<CraftingBrowser>(); craftingBrowser.table = table; craftingBrowser.ui = (CraftingUI)UIField.GetValue(table); } return craftingBrowser; } private static void Opened(CraftingTable __instance) { CraftingBrowser craftingBrowser = Get(__instance); craftingBrowser.query = craftingBrowser.ui.sortByInput.text ?? ""; craftingBrowser.BuildToolbar(); craftingBrowser.Schedule(); CraftingLocations.Register(((Decoration)__instance).id, (List<Recipe>)OriginalField.GetValue(__instance)); } private static bool Search(CraftingTable __instance, string filterBy) { CraftingBrowser craftingBrowser = Get(__instance); craftingBrowser.query = (filterBy ?? "").Trim(); if (craftingBrowser.query.Length > 120) { craftingBrowser.query = craftingBrowser.query.Substring(0, 120); } craftingBrowser.Schedule(); return false; } private void Schedule() { rebuildAt = Time.unscaledTime + 0.15f; } private void Update() { if ((Object)(object)ui == (Object)null || !((Component)ui.craftingPane).gameObject.activeInHierarchy) { return; } if (rebuildAt >= 0f && Time.unscaledTime >= rebuildAt) { rebuildAt = -1f; table.Initialize(false); } if (Time.unscaledTime < checkAt || !(bool)MatsField.GetValue(table)) { return; } checkAt = Time.unscaledTime + 1f; List<Recipe> obj = (List<Recipe>)OriginalField.GetValue(table); int num = 17; foreach (Recipe item in obj) { if ((Object)(object)item != (Object)null) { num = num * 31 + (table.CanCraft(item, 1) ? 1 : 0); } } if (hashReady && num != craftableHash) { Schedule(); } craftableHash = num; hashReady = true; } private static void Refreshed(CraftingTable __instance) { CraftingBrowser component = ((Component)__instance).GetComponent<CraftingBrowser>(); if ((Object)(object)component != (Object)null && (bool)MatsField.GetValue(__instance)) { component.Schedule(); } } private static void RecipeRoutine(CraftingTable __instance, ref IEnumerator __result) { __result = Get(__instance).FilterRoutine(__result); } private IEnumerator FilterRoutine(IEnumerator original) { rebuildAt = -1f; string selectedQuery = query; var source = from recipe in ((List<Recipe>)RecipesField.GetValue(table)).Where((Recipe recipe) => (Object)(object)recipe != (Object)null).ToList() select new { Recipe = recipe, Score = RecipeSearch.Score(selectedQuery, CraftingLocations.ItemName(((SerializedItemData)recipe.output2).id, recipe.output2.name), recipe.Input.Select((SerializedItemDataNamedAmount input) => CraftingLocations.ItemName(((SerializedItemData)input).id, input.name))), Recent = CraftingPreferences.RecentIndex(recipe) } into entry where entry.Score >= 0 && (mode != 1 || CraftingPreferences.Favorite(entry.Recipe)) && (mode != 2 || entry.Recent >= 0) select entry; if (mode == 2) { source = from entry in source orderby entry.Recent, entry.Score select entry; } else if (!string.IsNullOrWhiteSpace(selectedQuery)) { source = source.OrderBy(entry => entry.Score); } List<Recipe> filtered = source.Select(entry => entry.Recipe).ToList(); if ((Object)(object)status != (Object)null) { ((TMP_Text)status).text = "Finding recipes..."; } try { while (true) { object value = RecipesField.GetValue(table); object value2 = FilterField.GetValue(table); object value3 = TabField.GetValue(table); bool flag; try { RecipesField.SetValue(table, filtered); FilterField.SetValue(table, ""); if (!string.IsNullOrWhiteSpace(selectedQuery)) { TabField.SetValue(table, ""); } flag = original.MoveNext(); } finally { RecipesField.SetValue(table, value); FilterField.SetValue(table, value2); TabField.SetValue(table, value3); } if (!flag) { break; } yield return original.Current; } int count = ((Dictionary<Recipe, CraftingPanel>)PanelsField.GetValue(table)).Count; if ((Object)(object)status != (Object)null) { ((TMP_Text)status).text = ((count == 0) ? "No matching recipes. Try All or clear a filter." : (count + " recipes | Search names or uses iron bar")); } } finally { (original as IDisposable)?.Dispose(); } } private static void BeforeCraft(CraftingTable __instance, Recipe recipe, int amount, out bool __state) { __state = amount > 0 && __instance.CanCraft(recipe, amount); } private static void AfterCraft(CraftingTable __instance, Recipe recipe, bool __state) { if (__state) { CraftingPreferences.Record(recipe); CraftingBrowser component = ((Component)__instance).GetComponent<CraftingBrowser>(); if ((Object)(object)component != (Object)null && (component.mode == 2 || (bool)MatsField.GetValue(__instance))) { component.Schedule(); } } } private void BuildToolbar() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00b4: 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_00c9: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0132: 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_0146: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Expected O, but got Unknown //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)status != (Object)null) { return; } ScrollRect componentInParent = ((Component)ui.craftingPane).GetComponentInParent<ScrollRect>(); if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent.viewport == (Object)null) { log.LogWarning((object)("Crafting browser could not find the recipe viewport for station " + ((Decoration)table).id)); return; } CraftingPanel val = (CraftingPanel)PanelPrefab.GetValue(table); RectTransform component = new GameObject("HiccupsRecipeBrowser", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>(); RectTransform val2 = (RectTransform)((Component)componentInParent).transform; ((Transform)component).SetParent(((Transform)val2).parent, false); component.anchorMin = new Vector2(val2.anchorMin.x, val2.anchorMax.y); component.anchorMax = val2.anchorMax; component.pivot = new Vector2(0.5f, 1f); component.sizeDelta = new Vector2(val2.sizeDelta.x, 38f); component.anchoredPosition = new Vector2(val2.anchoredPosition.x, val2.offsetMax.y); val2.offsetMax -= new Vector2(0f, 40f); string[] array = new string[3] { "All", "Favorites", "Recent" }; for (int i = 0; i < array.Length; i++) { int index = i; TextMeshProUGUI label; Button obj = NativeButton((Transform)(object)component, val.buyButton, val.buyButtonTMP, array[i], out label); RectTransform val3 = (RectTransform)((Component)obj).transform; val3.anchorMin = new Vector2((float)i / 3f, 1f); val3.anchorMax = new Vector2((float)(i + 1) / 3f, 1f); val3.pivot = new Vector2(0.5f, 1f); val3.sizeDelta = new Vector2(-4f, 22f); val3.anchoredPosition = Vector2.zero; modeLabels.Add(label); ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { mode = index; UpdateModeLabels(); Schedule(); }); } status = NativeLabel((Transform)(object)component, val.buyButtonTMP, "Search names or uses iron bar"); ((TMP_Text)status).fontSize = 9f; ((TMP_Text)status).rectTransform.anchorMin = new Vector2(0f, 0f); ((TMP_Text)status).rectTransform.anchorMax = new Vector2(1f, 0f); ((TMP_Text)status).rectTransform.sizeDelta = new Vector2(0f, 14f); ((TMP_Text)status).rectTransform.anchoredPosition = new Vector2(0f, 7f); Graphic placeholder = ui.sortByInput.placeholder; TMP_Text val4 = (TMP_Text)(object)((placeholder is TMP_Text) ? placeholder : null); if (val4 != null) { val4.text = "Search / uses ..."; } TextMeshProUGUI[] componentsInChildren = ((Component)ui.sortHasMats).GetComponentsInChildren<TextMeshProUGUI>(true); for (int num = 0; num < componentsInChildren.Length; num++) { ((TMP_Text)componentsInChildren[num]).text = "Craftable only"; } UpdateModeLabels(); } private void UpdateModeLabels() { string[] array = new string[3] { "All", "Favorites", "Recent" }; for (int i = 0; i < modeLabels.Count; i++) { ((TMP_Text)modeLabels[i]).text = ((i == mode) ? ("<color=#FEE463><b>" + array[i] + "</b></color>") : array[i]); } } private static void PanelInitialized(CraftingPanel __instance, CraftingTable craftingTable, Recipe recipe) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: 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_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Expected O, but got Unknown RecipeFavoriteControl recipeFavoriteControl = ((Component)__instance).GetComponent<RecipeFavoriteControl>(); if ((Object)(object)recipeFavoriteControl == (Object)null) { recipeFavoriteControl = ((Component)__instance).gameObject.AddComponent<RecipeFavoriteControl>(); GameObject val = new GameObject("FavoriteHeart", new Type[4] { typeof(RectTransform), typeof(Image), typeof(Button), typeof(NavigationElement) }); val.transform.SetParent(((Component)__instance.outputImage).transform, false); RectTransform val2 = (RectTransform)val.transform; Vector2 anchorMin = (val2.anchorMax = Vector2.one); val2.anchorMin = anchorMin; val2.pivot = Vector2.one; val2.sizeDelta = new Vector2(14f, 12f); val2.anchoredPosition = new Vector2(-1f, -1f); recipeFavoriteControl.Background = val.GetComponent<Image>(); recipeFavoriteControl.Background.preserveAspect = true; Image component = new GameObject("FilledHeart", new Type[2] { typeof(RectTransform), typeof(Image) }).GetComponent<Image>(); ((Component)component).transform.SetParent(val.transform, false); ((Graphic)component).rectTransform.anchorMin = Vector2.zero; ((Graphic)component).rectTransform.anchorMax = Vector2.one; RectTransform rectTransform = ((Graphic)component).rectTransform; anchorMin = (((Graphic)component).rectTransform.offsetMax = Vector2.zero); rectTransform.offsetMin = anchorMin; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; recipeFavoriteControl.Fill = component; Button component2 = val.GetComponent<Button>(); ((Selectable)component2).targetGraphic = (Graphic)(object)recipeFavoriteControl.Background; ((Selectable)component2).transition = (Transition)1; ((UnityEvent)component2.onClick).AddListener(new UnityAction(recipeFavoriteControl.Toggle)); } recipeFavoriteControl.Recipe = recipe; recipeFavoriteControl.Browser = Get(craftingTable); recipeFavoriteControl.Refresh(); } internal void FavoriteChanged() { if (mode == 1) { Schedule(); } } private static Button NativeButton(Transform parent, Button source, TextMeshProUGUI fontSource, string text, out TextMeshProUGUI label) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Hiccups" + text, new Type[5] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(NavigationElement) }); val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); Graphic targetGraphic = ((Selectable)source).targetGraphic; Image val2 = (Image)(((object)((targetGraphic is Image) ? targetGraphic : null)) ?? ((object)((Component)source).GetComponent<Image>())); if ((Object)(object)val2 != (Object)null) { component.sprite = val2.sprite; component.type = val2.type; ((Graphic)component).color = ((Graphic)val2).color; ((Graphic)component).material = ((Graphic)val2).material; component.pixelsPerUnitMultiplier = val2.pixelsPerUnitMultiplier; } Button component2 = val.GetComponent<Button>(); ((Selectable)component2).targetGraphic = (Graphic)(object)component; ((Selectable)component2).transition = (Transition)(((int)((Selectable)source).transition == 3) ? 1 : ((int)((Selectable)source).transition)); ((Selectable)component2).colors = ((Selectable)source).colors; ((Selectable)component2).spriteState = ((Selectable)source).spriteState; label = NativeLabel(val.transform, fontSource, text); return component2; } private static TextMeshProUGUI NativeLabel(Transform parent, TextMeshProUGUI source, string text) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI component = new GameObject("Label", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).transform.SetParent(parent, false); ((TMP_Text)component).font = ((TMP_Text)source).font; ((TMP_Text)component).fontSharedMaterial = ((TMP_Text)source).fontSharedMaterial; ((TMP_Text)component).fontSize = 14f; ((Graphic)component).color = ((Graphic)source).color; ((TMP_Text)component).alignment = (TextAlignmentOptions)514; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).enableWordWrapping = false; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((TMP_Text)component).text = text; ((TMP_Text)component).rectTransform.anchorMin = Vector2.zero; ((TMP_Text)component).rectTransform.anchorMax = Vector2.one; ((TMP_Text)component).rectTransform.offsetMin = new Vector2(2f, 0f); ((TMP_Text)component).rectTransform.offsetMax = new Vector2(-2f, 0f); return component; } } internal sealed class RecipeFavoriteControl : MonoBehaviour { internal Recipe Recipe; internal CraftingBrowser Browser; internal Image Background; internal Image Fill; internal void Refresh() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (NativeUiAssets.Hearts(out var empty, out var full, out var backgroundColor, out var fillColor)) { Background.sprite = empty; ((Graphic)Background).color = backgroundColor; Fill.sprite = full; ((Graphic)Fill).color = fillColor; } ((Behaviour)Background).enabled = (Object)(object)Background.sprite != (Object)null; ((Behaviour)Fill).enabled = (Object)(object)Fill.sprite != (Object)null && CraftingPreferences.Favorite(Recipe); } internal void Toggle() { CraftingPreferences.Toggle(Recipe); Refresh(); Browser.FavoriteChanged(); } } internal static class CraftingLocations { private static readonly Dictionary<int, HashSet<int>> stations = new Dictionary<int, HashSet<int>>(); private static readonly Dictionary<int, string> names = new Dictionary<int, string>(); internal static void Install(ManualLogSource logger) { using (Stream stream = typeof(CraftingLocations).Assembly.GetManifestResourceStream("HiccupsQOLFixes.CraftingLocations")) { if (stream == null) { throw new InvalidOperationException("Crafting location index is missing."); } using StreamReader streamReader = new StreamReader(stream); string text; while ((text = streamReader.ReadLine()) != null) { string[] array = text.Split(new char[1] { ':' }); if (array.Length == 2) { stations[int.Parse(array[0])] = new HashSet<int>(array[1].Split(new char[1] { ',' }).Select(int.Parse)); } } } ItemTooltipExtensions.Add(logger, Description); logger.LogInfo((object)("Crafting location tooltips ready for " + stations.Count + " items.")); } internal static void Register(int stationId, IEnumerable<Recipe> recipes) { if (stationId <= 0 || recipes == null) { return; } foreach (Recipe recipe in recipes) { if (!((Object)(object)recipe == (Object)null) && recipe.output2 != null) { if (!stations.TryGetValue(((SerializedItemData)recipe.output2).id, out var value)) { value = (stations[((SerializedItemData)recipe.output2).id] = new HashSet<int>()); } value.Add(stationId); } } } internal static string ItemName(int id, string fallback) { ItemInfoDatabase instance = SingletonBehaviour<ItemInfoDatabase>.Instance; string text; if (instance?.allItemSellInfos == null || !instance.allItemSellInfos.TryGetValue(id, out var value)) { text = fallback; if (text == null) { return ""; } } else { text = LocalizeText.TranslateText(value.keyName, value.name); } return text; } private static string Description(int id) { if (!stations.TryGetValue(id, out var value)) { return ""; } string value2; string[] array = (from name in (from station in ((IEnumerable<int>)value).Where((Func<int, bool>)Database.ValidID) select ItemName(station, names.TryGetValue(station, out value2) ? value2 : ("Station " + station)).Replace(" (Old)", "")).Distinct() orderby name select name).ToArray(); if (array.Length == 0) { return ""; } return "\n\n<line-height=100%><b>Crafted at:</b> " + string.Join(", ", array.Select((string name) => "<noparse>" + name + "</noparse>")) + "</line-height>"; } } internal static class CraftingPreferences { [Serializable] private sealed class Preferences { public List<string> favorites = new List<string>(); public List<string> recent = new List<string>(); } private const string Progress = "HiccupsQOLFixes.CraftingPreferences"; private static CharacterData character; private static Preferences current; private static Preferences Current { get { GameSave instance = SingletonBehaviour<GameSave>.Instance; object obj; if (instance == null) { obj = null; } else { GameSaveData currentSave = instance.CurrentSave; obj = ((currentSave != null) ? currentSave.characterData : null); } CharacterData val = (CharacterData)obj; if (current != null && val == character) { return current; } character = val; current = new Preferences(); if (val == null) { return current; } string progressStringCharacter = instance.GetProgressStringCharacter("HiccupsQOLFixes.CraftingPreferences"); if (!string.IsNullOrEmpty(progressStringCharacter)) { try { current = JsonUtility.FromJson<Preferences>(Encoding.UTF8.GetString(Convert.FromBase64String(progressStringCharacter))) ?? current; } catch (Exception ex) when (ex is FormatException || ex is ArgumentException) { } } current.favorites = current.favorites ?? new List<string>(); current.recent = (current.recent ?? new List<string>()).Distinct().Take(30).ToList(); return current; } } internal static string Key(Recipe recipe) { return ((SerializedItemData)recipe.output2).id + "|" + ((Object)recipe).name; } internal static bool Favorite(Recipe recipe) { return Current.favorites.Contains(Key(recipe)); } internal static int RecentIndex(Recipe recipe) { return Current.recent.IndexOf(Key(recipe)); } internal static void Toggle(Recipe recipe) { Preferences preferences = Current; if (!preferences.favorites.Remove(Key(recipe))) { preferences.favorites.Add(Key(recipe)); } Save(); } internal static void Record(Recipe recipe) { Preferences preferences = Current; preferences.recent.Remove(Key(recipe)); preferences.recent.Insert(0, Key(recipe)); if (preferences.recent.Count > 30) { preferences.recent.RemoveRange(30, preferences.recent.Count - 30); } Save(); } private static void Save() { if (character != null) { SingletonBehaviour<GameSave>.Instance.SetProgressStringCharacter("HiccupsQOLFixes.CraftingPreferences", Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonUtility.ToJson((object)current)))); } } } internal static class CraftingStationSettings { [Serializable] private sealed class Settings { public int version = 1; public bool nearby = true; public bool materials; public int sort = 1; public bool direction = true; } private const string Prefix = "HiccupsQOLFixes.Station."; private static readonly FieldInfo Nearby = AccessTools.Field(typeof(CraftingTable), "useNearbyChests"); private static readonly FieldInfo Materials = AccessTools.Field(typeof(CraftingTable), "hasMats"); private static readonly FieldInfo UI = AccessTools.Field(typeof(CraftingTable), "craftingUI"); private static readonly FieldInfo Recipes = AccessTools.Field(typeof(CraftingTable), "_craftingRecipes"); private static readonly FieldInfo Original = AccessTools.Field(typeof(CraftingTable), "_saveCraftingRecipes"); private static readonly FieldInfo Reversed = AccessTools.Field(typeof(CraftingTable), "_reversedSavedCraftingRecipes"); private static readonly HashSet<CraftingTable> Open = new HashSet<CraftingTable>(); private static bool applying; private static GameSave Save => SingletonBehaviour<GameSave>.Instance; internal static void Install() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_00fd: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown Harmony val = new Harmony("com.hiccups.sunhaven.qolfixes.stationsettings"); string[] array = new string[5] { "ToggleUseNearbyChests", "ToggleHasMats", "TogglePriceSorting", "ToggleRaritySorting", "ResetSorting" }; foreach (string text in array) { val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), text, (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingStationSettings), "Changed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "SetMeta", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingStationSettings), "Restore", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "OpenUI", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "Opening", (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "Opened", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "CloseUI", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "Closing", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CraftingTable), "OnDisable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "Closing", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(GameSave), "SetProgressStringWorld", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CraftingStationSettings), "ProgressChanged", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(NetworkWorld), "UserCode_CmdSetProgressStringWorldRPC__String__String", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "ServerProgress", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(GameSave), "RemoveDecorationFromSave", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CraftingStationSettings), "Removing", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static string Key(int scene, Vector3Int position, int id) { return "HiccupsQOLFixes.Station." + scene + "." + ((Vector3Int)(ref position)).x + "." + ((Vector3Int)(ref position)).y + "." + ((Vector3Int)(ref position)).z + "." + id; } private static string Key(CraftingTable table) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return Key(((Decoration)table).sceneID, ((Decoration)table).Position, ((Decoration)table).id); } private static Settings Read(CraftingTable table) { GameSave save = Save; if (((save != null) ? save.CurrentWorld : null) == null) { return new Settings(); } string progressStringWorld = Save.GetProgressStringWorld(Key(table)); if (string.IsNullOrEmpty(progressStringWorld)) { return new Settings(); } try { Settings settings = JsonUtility.FromJson<Settings>(progressStringWorld); return (settings != null && settings.version == 1 && (settings.sort == 1 || settings.sort == 2)) ? settings : new Settings(); } catch (ArgumentException) { return new Settings(); } } private static void Changed(CraftingTable __instance, MethodBase __originalMethod, object[] __args) { if (applying || !Open.Contains(__instance)) { return; } GameSave save = Save; if (((save != null) ? save.CurrentWorld : null) != null) { Settings settings = Read(__instance); switch (__originalMethod.Name) { case "ToggleUseNearbyChests": settings.nearby = (bool)__args[0]; break; case "ToggleHasMats": settings.materials = (bool)__args[0]; break; case "TogglePriceSorting": settings.sort = 2; settings.direction = (bool)__args[0]; break; case "ToggleRaritySorting": settings.sort = 1; settings.direction = (bool)__args[0]; break; case "ResetSorting": settings.sort = 1; settings.direction = true; break; } Save.SetProgressStringWorld(Key(__instance), JsonUtility.ToJson((object)settings), true); } } private static void Opening(CraftingTable __instance) { Restore(__instance); } private static void Opened(CraftingTable __instance) { Open.Add(__instance); Apply(__instance, refresh: true); } private static void Closing(CraftingTable __instance) { Open.Remove(__instance); } private static void Restore(CraftingTable __instance) { Apply(__instance, refresh: false); } private static void Apply(CraftingTable table, bool refresh) { if ((Object)(object)table == (Object)null) { return; } GameSave save = Save; if (((save != null) ? save.CurrentWorld : null) == null) { return; } Settings settings = Read(table); bool flag = applying; applying = true; try { Nearby.SetValue(table, settings.nearby); Materials.SetValue(table, settings.materials); object? value = UI.GetValue(table); CraftingUI val = (CraftingUI)((value is CraftingUI) ? value : null); if ((Object)(object)val == (Object)null) { return; } val.sortUseNearbyChests.SetIsOnWithoutNotify(settings.nearby); val.sortHasMats.SetIsOnWithoutNotify(settings.materials); val.sortLowToHighPriceToggle.SetIsOnWithoutNotify(settings.sort == 2 && !settings.direction); val.sortHighToLowPriceToggle.SetIsOnWithoutNotify(settings.sort == 2 && settings.direction); val.sortLowToHighRarityToggle.SetIsOnWithoutNotify(settings.sort == 1 && settings.direction); val.sortHighToLowRarityToggle.SetIsOnWithoutNotify(settings.sort == 1 && !settings.direction); if (!refresh || !(Original.GetValue(table) is List<Recipe> list)) { return; } if (settings.sort == 1) { Recipes.SetValue(table, settings.direction ? list : Reversed.GetValue(table)); } else { Func<Recipe, float> keySelector = (Recipe recipe) => SingletonBehaviour<ItemInfoDatabase>.Instance.allItemSellInfos[((SerializedItemData)recipe.output2).id].sellPrice * (float)recipe.output2.amount; Recipes.SetValue(table, (settings.direction ? list.OrderByDescending(keySelector) : list.OrderBy(keySelector)).ToList()); } if (refresh) { table.Initialize(false); } } finally { applying = flag; } } private static void ProgressChanged(string progressID) { if (!progressID.StartsWith("HiccupsQOLFixes.Station.", StringComparison.Ordinal)) { return; } CraftingTable[] array = Open.ToArray(); foreach (CraftingTable val in array) { if ((Object)(object)val == (Object)null) { Open.Remove(val); } else if (Key(val) == progressID) { Apply(val, refresh: true); } } } private static void ServerProgress(string progress, string value) { if (progress.StartsWith("HiccupsQOLFixes.Station.", StringComparison.Ordinal)) { GameSave save = Save; if (((save != null) ? save.CurrentWorld : null) != null) { Save.SetProgressStringWorld(progress, value, false); } } } private static void Removing(Vector3Int position, int sceneID) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) GameSave save = Save; object obj; if (save == null) { obj = null; } else { WorldData currentWorld = save.CurrentWorld; obj = ((currentWorld != null) ? currentWorld.Decorations : null); } if (obj != null && Save.CurrentWorld.Decorations.TryGetValue((short)sceneID, out var value) && value.TryGetValue(new DecorationKey((ushort)((Vector3Int)(ref position)).x, (ushort)((Vector3Int)(ref position)).y, (sbyte)((Vector3Int)(ref position)).z), out var value2)) { string text = Key(sceneID, position, value2.id); if (!string.IsNullOrEmpty(Save.GetProgressStringWorld(text))) { Save.SetProgressStringWorld(text, "", false); } } } } internal static class DialogueEscape { private static readonly Dictionary<MethodInfo, bool> safeActions = new Dictionary<MethodInfo, bool>(); private static readonly FieldInfo Options = AccessTools.Field(typeof(DialogueController), "_options"); private static readonly FieldInfo OptionIndex = AccessTools.Field(typeof(DialogueController), "_optionIndex"); private static ManualLogSource log; internal static void Install(ManualLogSource logger) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown log = logger; new Harmony("com.hiccups.sunhaven.qolfixes.dialogueescape").Patch((MethodBase)AccessTools.Method(typeof(UIHandler), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(DialogueEscape), "BeforeUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static bool BeforeUpdate(UIHandler __instance, bool ___uiOpen) { DialogueController instance = DialogueController.Instance; if (!Object.op_Implicit((Object)(object)instance) || !instance.DialogueOnGoing || instance.Scrolling || Cutscene.Active || !Object.op_Implicit((Object)(object)Player.Instance) || !PlayerInput.AllowInput || GameManager.SceneTransitioning || ___uiOpen || UIHandler.InventoryOpen || RewiredManager.KeybindsOpen || (Object.op_Implicit((Object)(object)QuantumConsole.Instance) && QuantumConsole.Instance.IsActive)) { return true; } if (!PlayerInput.GetButtonDown((Button)40, true) && !PlayerInput.GetButtonDown((Button)9, true)) { return true; } try { List<(TextMeshProUGUI, UnityAction, Image, string, bool)> options = (List<(TextMeshProUGUI, UnityAction, Image, string, bool)>)Options.GetValue(instance); string[] exitLabels = new string[5] { ScriptLocalization.SeeYa, ScriptLocalization.SeeYaExcited, ScriptLocalization.ActuallyNevermind00, ScriptLocalization.NoThanks, ScriptLocalization.NPC_Covington_O4 }; int[] array = (from index in Enumerable.Range(0, options.Count) where options[index].Item5 && Object.op_Implicit((Object)(object)options[index].Item1) && ((Component)options[index].Item1).gameObject.activeInHierarchy && IsExit(options[index].Item2) select index).ToArray(); int num = array.Where((int index) => exitLabels.Any((string label) => string.Equals(label?.Trim(), options[index].Item4?.Trim(), StringComparison.Ordinal))).DefaultIfEmpty(-1).First(); if (num < 0 && array.Length == 1 && options[array[0]].Item2 != null) { num = array[0]; } if (num >= 0) { OptionIndex.SetValue(instance, num); instance.Continue(); return false; } } catch (Exception ex) { log.LogError((object)ex); } return true; } private static bool IsExit(UnityAction action) { if (action == null) { return true; } if (((Delegate)(object)action).GetInvocationList().Length != 1) { return false; } if (!safeActions.TryGetValue(((Delegate)(object)action).Method, out var value)) { value = (safeActions[((Delegate)(object)action).Method] = IsExitMethod(((Delegate)(object)action).Method)); } if (!value) { return false; } object? target = ((Delegate)(object)action).Target; Object val = (Object)((target is Object) ? target : null); if (val != null && !Object.op_Implicit(val)) { return false; } object? target2 = ((Delegate)(object)action).Target; LocalizedDialogueTree val2 = (LocalizedDialogueTree)((target2 is LocalizedDialogueTree) ? target2 : null); if (val2 == null && ((Delegate)(object)action).Target != null) { val2 = (from field in ((Delegate)(object)action).Target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where field.FieldType == typeof(LocalizedDialogueTree) select field).Select(delegate(FieldInfo field) { object? value2 = field.GetValue(((Delegate)(object)action).Target); return (LocalizedDialogueTree)((value2 is LocalizedDialogueTree) ? value2 : null); }).FirstOrDefault(); } if (val2 != null) { return val2.OnEnd != null; } return true; } private static bool IsExitMethod(MethodInfo method) { if (method.Module != typeof(DialogueController).Module || method.GetMethodBody() == null) { return false; } byte[] iLAsByteArray = method.GetMethodBody().GetILAsByteArray(); int num = 0; bool flag = false; int num2 = 0; while (num2 < iLAsByteArray.Length) { short num3 = iLAsByteArray[num2++]; OpCode ldfld = OpCodes.Ldfld; if (num3 == ldfld.Value) { if (num2 + 4 > iLAsByteArray.Length) { return false; } FieldInfo fieldInfo = method.Module.ResolveField(BitConverter.ToInt32(iLAsByteArray, num2)); num2 += 4; if (fieldInfo == null) { return false; } if (fieldInfo.DeclaringType == typeof(LocalizedDialogueTree) && fieldInfo.Name == "OnEnd") { flag = true; } else if (fieldInfo.Name != "<>4__this") { return false; } continue; } ldfld = OpCodes.Call; if (num3 != ldfld.Value) { ldfld = OpCodes.Callvirt; if (num3 != ldfld.Value) { ldfld = OpCodes.Brtrue_S; if (num3 != ldfld.Value) { ldfld = OpCodes.Brtrue; if (num3 != ldfld.Value) { ldfld = OpCodes.Nop; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Ldarg_0; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Ldc_I4_0; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Dup; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Pop; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Brtrue_S; if (num3 == ldfld.Value) { continue; } ldfld = OpCodes.Brtrue; if (num3 != ldfld.Value) { ldfld = OpCodes.Ret; if (num3 != ldfld.Value) { return false; } } continue; } } ldfld = OpCodes.Brtrue_S; int num4 = ((num3 == ldfld.Value) ? 1 : 4); if (num2 + num4 > iLAsByteArray.Length) { return false; } int num5 = ((num4 == 1) ? ((sbyte)iLAsByteArray[num2]) : BitConverter.ToInt32(iLAsByteArray, num2)); num2 += num4; if (num5 < 0 || num2 + num5 >= iLAsByteArray.Length) { return false; } continue; } } if (num2 + 4 > iLAsByteArray.Length) { return false; } MethodInfo methodInfo = method.Module.ResolveMethod(BitConverter.ToInt32(iLAsByteArray, num2)) as MethodInfo; num2 += 4; if (methodInfo == null) { return false; } if (methodInfo.DeclaringType == typeof(UnityAction) && methodInfo.Name == "Invoke" && flag) { num++; continue; } if ((methodInfo.DeclaringType == typeof(NPC) || methodInfo.DeclaringType == typeof(NPCAI)) && methodInfo.Name == "EndInteract") { num++; continue; } return false; } return num == 1; } } internal static class DonationCodec { internal static string Encode(DonationPacket packet) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) DonationProtocol.Validate(packet); using MemoryStream memoryStream = new MemoryStream(); ((XmlObjectSerializer)new DataContractJsonSerializer(typeof(DonationPacket))).WriteObject((Stream)memoryStream, (object)packet); string text = Encoding.UTF8.GetString(memoryStream.ToArray()); if (text.Length > 30000) { throw new InvalidOperationException("Museum donation message is too large."); } return text; } internal static DonationPacket Decode(string json, bool network = false) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(json) || json.Length > 30000) { throw new InvalidOperationException("Invalid museum donation message size."); } using MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); DonationPacket donationPacket = (DonationPacket)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(DonationPacket))).ReadObject((Stream)memoryStream); if (network && donationPacket != null && donationPacket.items == null && (donationPacket.operation == "prepare" || donationPacket.operation == "offer" || donationPacket.operation == "success")) { throw new InvalidOperationException("Museum donation message is missing its item list. Update the mod on every player."); } return DonationProtocol.Validate(donationPacket); } } [DataContract] internal sealed class DonationAmount { [DataMember] public int id; [DataMember] public int amount; } [DataContract] internal sealed class DonationPacket { [DataMember] public int version = 1; [DataMember] public string operation; [DataMember] public string transaction; [DataMember] public string world; [DataMember] public string error; [DataMember] public DonationAmount[] items; [DataMember] public string[] completed; [DataMember] public int collections; [DataMember] public string escrow; } internal static class DonationProtocol { internal static DonationPacket Validate(DonationPacket packet) { if (packet == null || packet.version != 1 || !Guid.TryParse(packet.transaction, out var _)) { throw new InvalidOperationException("Incompatible museum donation message."); } switch (packet.operation) { default: throw new InvalidOperationException("Unknown museum donation operation."); case "prepare": case "success": case "offer": case "query": case "commit": case "rejected": case "otherworld": packet.items = packet.items ?? new DonationAmount[0]; packet.completed = packet.completed ?? new string[0]; if (packet.items.Length > 50 || packet.items.Any((DonationAmount item) => item == null || item.id <= 0 || item.amount <= 0) || packet.items.Select((DonationAmount item) => item.id).Distinct().Count() != packet.items.Length) { throw new InvalidOperationException("Invalid museum donation item list."); } return packet; } } } internal static class DonationRoutine { internal static IEnumerator Guard(IEnumerator routine, Action<Exception> failed) { try { while (true) { object current; try { if (!routine.MoveNext()) { break; } current = routine.Current; } catch (Exception obj) { failed(obj); break; } yield return current; } } finally { (routine as IDisposable)?.Dispose(); } } } internal static class FruitTreeStorage { private sealed class TreeState { public int extra; public bool loading; public CraftingNotificationBubble bubble; } private struct OvernightState { public int extra; public int occupied; } private const int Capacity = 99; private const int Marker = 827606600; private static ManualLogSource log; private static bool reportedVisualError; private static readonly ConditionalWeakTable<ForageTree, TreeState> States = new ConditionalWeakTable<ForageTree, TreeState>(); private static readonly MethodInfo GetFruit = AccessTools.Method(typeof(ForageTree), "GetFruit", (Type[])null, (Type[])null); internal static void Install(ManualLogSource logger) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0059: Expected O, but got Unknown //IL_0059: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00a3: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_00ed: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown //IL_016d: Expected O, but got Unknown //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_01b7: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown log = logger; Harmony val = new Harmony("com.hiccups.sunhaven.qolfixes.fruittrees"); val.Patch((MethodBase)AccessTools.Method(typeof(DecorationUpdater), "FruitTree_UpdateMetaOvernight", (Type[])null, (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "BeforeGrowth", (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "AfterGrowth", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ForageTree), "SetMeta", (Type[])null, (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "Loading", (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "Refresh", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ForageTree), "ReceiveNewMeta", (Type[])null, (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "Receiving", (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "Refresh", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ForageTree), "SaveMeta", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(FruitTreeStorage), "Saving", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ForageTree), "Shake", (Type[])null, (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "BeforeShake", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(FruitTreeStorage), "AfterHarvest", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ForageTree), "HandleWoodDrop", (Type[])null, (Type[])null), new HarmonyMethod(typeof(FruitTreeStorage), "BeforeChop", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(FruitTreeStorage), "AfterHarvest", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(NetworkGamePlayer), "UserCode_RpcShakeTree__Vector3Int__Int16__Byte[]", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(FruitTreeStorage), "ReceivedShake", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); logger.LogInfo((object)"Fruit Tree Storage enabled (99 fruit per tree)"); } private static int Count(ForageTreeSaveData data) { if (data != null) { return (data.spot1 ? 1 : 0) + (data.spot2 ? 1 : 0) + (data.spot3 ? 1 : 0); } return 0; } private static int ReadExtra(byte[] meta) { if (meta == null || meta.Length < 12) { return 0; } int num = BitConverter.ToInt32(meta, 0); if (num < 4 || num != meta.Length - 8 || BitConverter.ToInt32(meta, num) != 827606600) { return 0; } return Mathf.Clamp(BitConverter.ToInt32(meta, num + 4), 0, 96); } private static byte[] WithExtra(byte[] meta, int extra) { if (meta == null || meta.Length < 4) { return meta; } int num = BitConverter.ToInt32(meta, 0); if (num < 4 || num > meta.Length) { return meta; } byte[] array = new byte[num + ((extra > 0) ? 8 : 0)]; Buffer.BlockCopy(meta, 0, array, 0, num); if (extra > 0) { Buffer.BlockCopy(BitConverter.GetBytes(827606600), 0, array, num, 4); Buffer.BlockCopy(BitConverter.GetBytes(Mathf.Clamp(extra, 0, 96)), 0, array, num + 4, 4); } return array; } private static void BeforeGrowth(ref DecorationPositionData decorationData, out OvernightState __state) { ForageTreeSaveData data = null; Decoration.DeserializeMeta<ForageTreeSaveData>(decorationData.meta, ref data); __state = new OvernightState { extra = ReadExtra(decorationData.meta), occupied = Count(data) }; } private static void AfterGrowth(ref DecorationPositionData decorationData, OvernightState __state) { ForageTreeSaveData fruitTreeData = DecorationUpdater.fruitTreeData; int num = __state.extra; if (fruitTreeData != null && fruitTreeData.stage == 7) { for (int i = 0; i < __state.occupied; i++) { if (fruitTreeData.golden || Utilities.Chance(0.33f)) { num++; } } } num = Mathf.Clamp(num, 0, 99 - Count(fruitTreeData)); decorationData.meta = WithExtra(decorationData.meta, num); } private static void Loading(ForageTree __instance, DecorationPositionData decorationData) { States.GetOrCreateValue(__instance).extra = ReadExtra(decorationData.meta); } private static void Receiving(ForageTree __instance, byte[] newMeta) { States.GetOrCreateValue(__instance).extra = ReadExtra(newMeta); } private static void Saving(ForageTree __instance) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) ((Decoration)__instance).meta = WithExtra(((Decoration)__instance).meta, States.GetOrCreateValue(__instance).extra); GameSave instance = SingletonBehaviour<GameSave>.Instance; CommitMeta((instance != null) ? instance.CurrentWorld : null, ((Decoration)__instance).sceneID, ((Decoration)__instance).Position, ((Decoration)__instance).id, ((Decoration)__instance).meta); Refresh(__instance); } private static void CommitMeta(WorldData world, int scene, Vector3Int position, int id, byte[] meta) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (((world != null) ? world.Decorations : null) != null && world.Decorations.TryGetValue((short)scene, out var value)) { DecorationKey key = default(DecorationKey); ((DecorationKey)(ref key))..ctor((ushort)((Vector3Int)(ref position)).x, (ushort)((Vector3Int)(ref position)).y, (sbyte)((Vector3Int)(ref position)).z); if (value.TryGetValue(key, out var value2) && value2 != null && value2.id == id) { value2.meta = meta; } } } private static void ReceivedShake(Vector3Int position, short scene, byte[] meta) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) GameManager instance = SingletonBehaviour<GameManager>.Instance; ForageTree val = default(ForageTree); if (scene == ScenePortalManager.ActiveSceneIndex && (Object)(object)instance != (Object)null && instance.TryGetObjectSubTile<ForageTree>(position, ref val)) { ((Decoration)val).sceneID = scene; GameSave instance2 = SingletonBehaviour<GameSave>.Instance; CommitMeta((instance2 != null) ? instance2.CurrentWorld : null, scene, position, ((Decoration)val).id, meta); } } private static void BeforeShake(ForageTree __instance, bool fromLocalPlayer, bool ___requireRelationship, out SpriteRenderer[] __state) { __state = null; if (!fromLocalPlayer) { return; } if (___requireRelationship) { Dictionary<string, float> relationships = SingletonBehaviour<GameSave>.Instance.CurrentSave.characterData.Relationships; if (!relationships.ContainsKey("Claude") || relationships["Claude"] < 10f) { return; } } ExpandHarvest(__instance, out __state); } private static void BeforeChop(ForageTree __instance, out SpriteRenderer[] __state) { __state = null; if ((Object)(object)__instance.seedItem != (Object)null) { ExpandHarvest(__instance, out __state); } } private static void ExpandHarvest(ForageTree tree, out SpriteRenderer[] original) { original = null; TreeState orCreateValue = States.GetOrCreateValue(tree); if (orCreateValue.extra > 0 && tree.spots != null && tree.spots.Length == 3) { original = tree.spots; SpriteRenderer[] array = (SpriteRenderer[])(object)new SpriteRenderer[3 + orCreateValue.extra]; for (int i = 0; i < array.Length; i++) { array[i] = original[i % 3]; } tree.spots = array; orCreateValue.extra = 0; } } private static void AfterHarvest(ForageTree __instance, SpriteRenderer[] __state) { if (__state != null) { __instance.spots = __state; } if ((Object)(object)__instance != (Object)null) { Refresh(__instance); } } private static void Visual(Action action) { try { action(); } catch (Exception ex) { if (!reportedVisualError) { log.LogWarning((object)("Could not refresh fruit notification: " + ex)); } reportedVisualError = true; } } private static void Refresh(ForageTree __instance) { Visual(delegate { RefreshCore(__instance); }); } private static void RefreshCore(ForageTree __instance) { if ((Object)(object)__instance == (Object)null) { return; } TreeState state = States.GetOrCreateValue(__instance); int num = Count(__instance.data) + state.extra; if (num <= 3) { if ((Object)(object)state.bubble != (Object)null) { ((Component)state.bubble).gameObject.SetActive(false); } } else if ((Object)(object)state.bubble != (Object)null) { UpdateBubble(__instance, state, num); } else { if (state.loading) { return; } state.loading = true; Database.GetData<ItemData>(10684, (Action<ItemData>)delegate(ItemData item) { Visual(delegate { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: 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_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) state.loading = false; if (!((Object)(object)__instance == (Object)null) && Count(__instance.data) + state.extra > 3) { UseItem useItem = item.useItem; Placeable val = (Placeable)(object)((useItem is Placeable) ? useItem : null); if (val != null) { Decoration decoration = val.Decoration; CraftingTable val2 = (CraftingTable)(object)((decoration is CraftingTable) ? decoration : null); if (val2 != null) { object? value = AccessTools.Field(typeof(CraftingTable), "craftingUI").GetValue(val2); CraftingUI val3 = (CraftingUI)((value is CraftingUI) ? value : null); object? value2 = AccessTools.Field(typeof(CraftingTable), "_notificationBubblePrefab").GetValue(val2); CraftingNotificationBubble val4 = (CraftingNotificationBubble)((value2 is CraftingNotificationBubble) ? value2 : null); if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.notificationContainer == (Object)null) && !((Object)(object)val4 == (Object)null)) { Transform val5 = Object.Instantiate<Transform>(val3.notificationContainer, ((Component)__instance).transform, false); ((Object)val5).name = "HiccupsFruitNotification"; for (int num2 = val5.childCount - 1; num2 >= 0; num2--) { GameObject gameObject = ((Component)val5.GetChild(num2)).gameObject; gameObject.SetActive(false); Object.Destroy((Object)(object)gameObject); } Vector3 position = ((Component)__instance.spots[0]).transform.position; SpriteRenderer[] spots = __instance.spots; foreach (SpriteRenderer val6 in spots) { if (((Component)val6).transform.position.y > position.y) { position = ((Component)val6).transform.position; } } position.x = ((Decoration)__instance).Center.x; val5.position = position + new Vector3(0f, 0.5f, -0.5f); ((Component)val5).gameObject.SetActive(true); GraphicRaycaster[] componentsInChildren = ((Component)val5).GetComponentsInChildren<GraphicRaycaster>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } state.bubble = Object.Instantiate<CraftingNotificationBubble>(val4, val5, false); RectTransform component = ((Component)state.bubble).GetComponent<RectTransform>(); Vector2 val7 = default(Vector2); ((Vector2)(ref val7))..ctor(0.5f, 0f); component.anchorMax = val7; component.anchorMin = val7; component.pivot = new Vector2(0.5f, 0f); component.anchoredPosition = Vector2.zero; ((Transform)component).localScale = Vector3.one * (2f / 3f); Graphic[] componentsInChildren2 = ((Component)state.bubble).GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].raycastTarget = false; } UpdateBubble(__instance, state, Count(__instance.data) + state.extra); } } } } }); }, (Action)null); } } private static void UpdateBubble(ForageTree tree, TreeState state, int total) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)state.bubble.notificationText).text = "x" + total; ((Component)state.bubble).gameObject.SetActive(true); State state2 = Random.state; int num; try { num = (int)GetFruit.Invoke(tree, new object[1] { 0 }); } finally { Random.state = state2; } Database.GetData<ItemData>(num, (Action<ItemData>)delegate(ItemData item) { Visual(delegate { if (!((Object)(object)tree == (Object)null) && !((Object)(object)state.bubble == (Object)null)) { state.bubble.notificationImage.sprite = item.icon; state.bubble.notificationImage.preserveAspect = true; } }); }, (Action)null); } } internal static class HomeSleepRules { internal static bool IsPlayerHome(string scene) { if (string.IsNullOrEmpty(scene)) { return false; } switch (scene) { default: return Regex.IsMatch(scene, "\\ATier[1-3]House[0-7]\\z"); case "NelvariPlayerHouse": case "WithergatePlayerApartment": case "GCMainDistrictPlayerApartment": return true; } } } internal sealed class ItemTooltipExtensions { private sealed class Display { internal int Item; internal int Height; internal string Name; internal string Description; internal string Status; } [ThreadStatic] private static int itemContext; private static readonly Dictionary<Tooltip, Display> displays = new Dictionary<Tooltip, Display>(); private static ManualLogSource log; private static float nextRefresh; private static bool installed; private static readonly List<Func<int, string>> providers = new List<Func<int, string>>(); internal static void Add(ManualLogSource logger, Func<int, string> provider) { providers.Add(provider); if (!installed) { installed = true; Install(logger); } } private static string Status(int id) { return string.Concat(providers.Select((Func<int, string> provider) => provider(id))); } private static void Install(ManualLogSource logger) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_010d: Expected O, but got Unknown log = logger; Harmony val = new Harmony("com.hiccups.sunhaven.qolfixes.itemtooltips"); MethodInfo[] array = (from method in (from type in typeof(Item).Assembly.GetTypes() where typeof(Item).IsAssignableFrom(type) || HasItemParent(type) select type).SelectMany((Type type) => type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) where method.Name.Contains("<GetToolTip>") && method.GetParameters().Length == 1 && typeof(ItemData).IsAssignableFrom(method.GetParameters()[0].ParameterType) select method).ToArray(); val.Patch((MethodBase)AccessTools.Method(typeof(Tooltip), "SetText", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ItemTooltipExtensions), "SetText", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo[] array2 = array; foreach (MethodInfo methodInfo in array2) { val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ItemTooltipExtensions), "Enter", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ItemTooltipExtensions), "Leave", (Type[])null), (HarmonyMethod)null); } log.LogInfo((object)("Item tooltip extensions enabled for " + array.Length + " item callbacks")); } private static bool HasItemParent(Type type) { Type declaringType = type.DeclaringType; while (declaringType != null) { if (typeof(Item).IsAssignableFrom(declaringType)) { return true; } declaringType = declaringType.DeclaringType; } return false; } private static void Enter(object[] __args, out int __state) { __state = itemContext; object obj = __args[0]; itemContext = ((ItemData)(((obj is ItemData) ? obj : null)?)).id ?? 0; } private static void Leave(int __state) { itemContext = __state; } private static void SetText(Tooltip __instance, string name, ref string description, int lineHeight) { if (itemContext <= 0) { displays.Remove(__instance); return; } Display display = new Display { Item = itemContext, Name = name, Description = description, Height = lineHeight }; display.Status = Status(display.Item); displays[__instance] = display; if (!string.IsNullOrEmpty(display.Status)) { description = "<align=center><line-height=100%>" + display.Status.TrimStart('\n', '\r') + "\n\n</line-height></align>" + description; } } internal static void Tick() { if (Time.unscaledTime < nextRefresh) { return; } nextRefresh = Time.unscaledTime + 0.5f; KeyValuePair<Tooltip, Display>[] array = displays.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<Tooltip, Display> keyValuePair = array[i]; if ((Object)(object)keyValuePair.Key == (Object)null) { displays.Remove(keyValuePair.Key); } else if (((Component)keyValuePair.Key).gameObject.activeInHierarchy && !(Status(keyValuePair.Value.Item) == keyValuePair.Value.Status)) { int num = itemContext; try { itemContext = keyValuePair.Value.Item; keyValuePair.Key.SetText(keyValuePair.Value.Name, keyValuePair.Value.Description, keyValuePair.Value.Height); } finally { itemContext = num; } } } } } internal sealed class MuseumCatalog { internal sealed class BundleDefinition { public string Progress; public string Name; public readonly Dictionary<short, DonationRequirement> Slots = new Dictionary<short, DonationRequirement>(); } private readonly Dictionary<int, BundleDefinition> definitions = new Dictionary<int, BundleDefinition>(); private bool catalogLoading; private bool catalogReady; private string catalogError; private float retryAfter; private GameSave loadingHost; private readonly ManualLogSource log = Logger.CreateLogSource("HiccupsQOLFixes.MuseumCatalog"); internal static MuseumCatalog Instance; private static readonly FieldInfo InventoryPanel = AccessTools.Field(typeof(Inventory), "_inventoryPanel"); internal Dictionary<int, BundleDefinition> Definitions => definitions; internal bool Ready => catalogReady; internal bool Loading => catalogLoading; internal string Error => catalogError; private static bool IsMuseumScene(int scene) { return MuseumDonation.IsMuseumScene(scene); } internal void EnsureLoaded() { if (catalogLoading && !Object.op_Implicit((Object)(object)loadingHost)) { catalogLoading = false; } GameSave instance = SingletonBehaviour<GameSave>.Instance; if (!catalogReady && !catalogLoading && Object.op_Implicit((Object)(object)instance) && Time.realtimeSinceStartup >= retryAfter) { loadingHost = instance; ((MonoBehaviour)instance).StartCoroutine(LoadCatalogSafely()); } } internal void Tick() { GameSave instance = SingletonBehaviour<GameSave>.Instance; if (((instance != null) ? instance.CurrentWorld : null) != null && (Object)(object)SceneSettingsManager.Instance != (Object)null) { EnsureLoaded(); } } private