Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Food Unlock v0.3.4
plugins/FoodUnlock/FoodUnlock.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FoodUnlock")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.4.0")] [assembly: AssemblyInformationalVersion("0.3.4+2e5ffad9de5f0359f8ef114d6f776ed16487429b")] [assembly: AssemblyProduct("FoodUnlock")] [assembly: AssemblyTitle("FoodUnlock")] [assembly: AssemblyVersion("0.3.4.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 FoodUnlock { [BepInPlugin("local.foodunlock", "Food Unlock", "0.3.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "EatFood", new Type[] { typeof(ItemData) })] private static class LearnFood { private static void Postfix(Player __instance, ItemData item, bool __result) { if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !IsFood(item)) { return; } HashSet<string> hashSet = ReadUnlocks(__instance); if (hashSet.Add(((Object)item.m_dropPrefab).name)) { __instance.m_customData["local.foodunlock.foods.v1"] = string.Join("\n", hashSet.OrderBy<string, string>((string x) => x, StringComparer.Ordinal)); ((Character)__instance).Message((MessageType)1, "Food unlocked: " + Localization.instance.Localize(item.m_shared.m_name), 0, (Sprite)null, false); ((BaseUnityPlugin)instance).Logger.LogInfo((object)("Unlocked food: " + ((Object)item.m_dropPrefab).name)); if (instance.visible) { instance.RefreshFoods(); instance.RebuildRows(); } } } } [HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })] private static class AvoidAutoRun { private static void Postfix(string __0, ref bool __result) { if (__result && !(__0 != "AutoRun") && !((Object)(object)instance == (Object)null) && !instance.visible) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && instance.PressedSlot(localPlayer, held: true) >= 0) { __result = false; } } } } [HarmonyPatch(typeof(Player), "UpdateFood")] private static class RestoreAssignedFood { private static void Prefix(Player __instance) { instance?.SyncPermanentFood(__instance); } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static class FullAssignedFoodBonuses { private static void Prefix(Player __instance) { instance?.SyncPermanentFood(__instance); } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class RestoreFoodOnSpawn { private static void Postfix(Player __instance) { instance?.RefreshPermanentFood(__instance); } } private GameObject panel; private RectTransform listContent; private ScrollRect foodScroll; private Text statusText; private readonly Text[] slotLabels = (Text[])(object)new Text[3]; private readonly Image[] slotIcons = (Image[])(object)new Image[3]; private readonly UITooltip[] slotTips = (UITooltip[])(object)new UITooltip[3]; private readonly List<Button> eatButtons = new List<Button>(); private readonly List<Text[]> assignmentLabels = new List<Text[]>(); private readonly List<Button> sortButtons = new List<Button>(); private GameObject tooltipPrefab; private int sortMode; private float nextBookUpdate; private static readonly Color Gold = new Color(1f, 0.73f, 0.3f); private static readonly Vector2 TopLeft = new Vector2(0f, 1f); private const string Id = "local.foodunlock"; private const string SaveKey = "local.foodunlock.foods.v1"; private const string SlotKey = "local.foodunlock.slot.v1."; private static Plugin instance; private Harmony harmony; private static readonly MethodInfo TakeInput = AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null); private ConfigEntry<KeyboardShortcut> toggle; private ConfigEntry<bool> permanentAssignedFood; private readonly ConfigEntry<KeyCode>[] slotKeys = new ConfigEntry<KeyCode>[3]; private Player bookPlayer; private bool visible; private readonly List<ItemData> foods = new List<ItemData>(); private string status = ""; private static readonly MethodInfo TotalFood = AccessTools.Method(typeof(Player), "GetTotalFoodValue", (Type[])null, (Type[])null); private static readonly Action<Player, float, bool> SetEitr = AccessTools.MethodDelegate<Action<Player, float, bool>>(AccessTools.Method(typeof(Player), "SetMaxEitr", (Type[])null, (Type[])null), (object)null, true); private Player lastFoodPlayer; private bool lastPermanentMode; private Player cachedFoodPlayer; private ObjectDB cachedFoodDB; private string cachedAssignments; private PermanentFoodState.Selection[] cachedPermanentFoods = Array.Empty<PermanentFoodState.Selection>(); private static void Place(RectTransform rect, float x, float y, float width, float height) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Vector2 val = (rect.pivot = TopLeft); Vector2 anchorMin = (rect.anchorMax = val); rect.anchorMin = anchorMin; rect.anchoredPosition = new Vector2(x, 0f - y); rect.sizeDelta = new Vector2(width, height); } private Text Label(Transform parent, string text, float x, float y, float width, float height, int size = 18, bool heading = false) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateText(text, parent, TopLeft, TopLeft, Vector2.zero, heading ? GUIManager.Instance.NorseBold : GUIManager.Instance.AveriaSerif, size, heading ? Gold : Color.white, true, Color.black, width, height, false); Place(obj.GetComponent<RectTransform>(), x, y, width, height); Text component = obj.GetComponent<Text>(); component.alignment = (TextAnchor)3; ((Graphic)component).raycastTarget = false; return component; } private Button ButtonAt(Transform parent, string text, float x, float y, float width, float height, Action action) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateButton(text, parent, TopLeft, TopLeft, Vector2.zero, width, height); Place(obj.GetComponent<RectTransform>(), x, y, width, height); Button component = obj.GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { action(); }); Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)component).navigation = navigation; return component; } private static Image ImageAt(Transform parent, float x, float y, float width, float height, Color color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_004d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Food image", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); Place(val.GetComponent<RectTransform>(), x, y, width, height); Image component = val.GetComponent<Image>(); ((Graphic)component).color = color; return component; } private UITooltip Tooltip(GameObject target, ItemData food) { UITooltip obj = target.AddComponent<UITooltip>(); obj.m_tooltipPrefab = tooltipPrefab; SetTooltip(obj, food); return obj; } private static void SetTooltip(UITooltip tip, ItemData food) { tip.m_topic = ((food != null) ? DisplayName(food) : "Unassigned"); tip.m_text = ((food != null) ? Localization.instance.Localize(food.GetTooltip(-1)) : "Choose a food below and click Assign."); } private bool CreateBook() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0176: 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_0326: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Expected O, but got Unknown if ((Object)(object)GUIManager.CustomGUIFront == (Object)null || (Object)(object)InventoryGui.instance == (Object)null) { return false; } try { UITooltip componentInChildren = InventoryGui.instance.m_playerGrid.m_elementPrefab.GetComponentInChildren<UITooltip>(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.m_tooltipPrefab == (Object)null) { throw new InvalidOperationException("Valheim's inventory tooltip prefab is unavailable."); } tooltipPrefab = componentInChildren.m_tooltipPrefab; panel = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 800f, 760f, false); RectTransform component = panel.GetComponent<RectTransform>(); component.pivot = new Vector2(0.5f, 0.5f); ((Object)panel).name = "FoodUnlockBook"; Label((Transform)(object)component, "FOOD BOOK", 28f, 18f, 560f, 46f, 34, heading: true); Label((Transform)(object)component, "Eat once to unlock. Hover a food for details.", 28f, 65f, 740f, 28f); for (int i = 0; i < 3; i++) { int slot = i; int num = 28 + i * 250; Image val = ImageAt((Transform)(object)component, num, 106f, 240f, 104f, new Color(0f, 0f, 0f, 0.28f)); slotIcons[i] = ImageAt(((Component)val).transform, 10f, 12f, 48f, 48f, Color.white); slotIcons[i].preserveAspect = true; slotTips[i] = Tooltip(((Component)val).gameObject, null); slotLabels[i] = Label(((Component)val).transform, "", 66f, 8f, 166f, 58f, 17); ButtonAt(((Component)val).transform, "Clear", 66f, 68f, 100f, 28f, delegate { bookPlayer.m_customData.Remove("local.foodunlock.slot.v1." + slot); RefreshPermanentFood(bookPlayer); RefreshAssignments(); }); } Label((Transform)(object)component, "Sort by", 28f, 224f, 84f, 32f); string[] array = new string[4] { "Name", "Health", "Stamina", "Eitr" }; for (int num2 = 0; num2 < array.Length; num2++) { int mode = num2; sortButtons.Add(ButtonAt((Transform)(object)component, array[num2], 116 + num2 * 142, 224f, 130f, 34f, delegate { sortMode = mode; SortFoods(); RebuildRows(); })); } Image val2 = ImageAt((Transform)(object)component, 28f, 274f, 744f, 366f, new Color(0f, 0f, 0f, 0.22f)); ((Component)val2).gameObject.AddComponent<RectMask2D>(); foodScroll = ((Component)val2).gameObject.AddComponent<ScrollRect>(); foodScroll.viewport = ((Graphic)val2).rectTransform; foodScroll.horizontal = false; foodScroll.movementType = (MovementType)2; foodScroll.scrollSensitivity = 320f; GameObject val3 = new GameObject("Foods", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent(((Component)val2).transform, false); listContent = val3.GetComponent<RectTransform>(); foodScroll.content = listContent; statusText = Label((Transform)(object)component, "", 28f, 650f, 744f, 42f, 17); Label((Transform)(object)component, "Scroll for more • World is not paused", 28f, 705f, 540f, 30f, 16); ButtonAt((Transform)(object)component, "Close", 630f, 701f, 140f, 36f, Close); RebuildRows(); UpdateBookScale(); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not create food book: " + ex)); DestroyBook(); ((Character)bookPlayer).Message((MessageType)1, "Food book could not open. Check the mod log.", 0, (Sprite)null, false); return false; } } private void SortFoods() { foods.Sort(delegate(ItemData a, ItemData b) { int num = ((sortMode == 1) ? b.m_shared.m_food.CompareTo(a.m_shared.m_food) : ((sortMode == 2) ? b.m_shared.m_foodStamina.CompareTo(a.m_shared.m_foodStamina) : ((sortMode == 3) ? b.m_shared.m_foodEitr.CompareTo(a.m_shared.m_foodEitr) : 0))); return (num == 0) ? string.Compare(DisplayName(a), DisplayName(b), StringComparison.CurrentCulture) : num; }); } private void RebuildRows() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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) foreach (Transform item in (Transform)listContent) { ((Component)item).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)item).gameObject); } eatButtons.Clear(); assignmentLabels.Clear(); Place(listContent, 0f, 0f, 744f, Mathf.Max(366f, (float)foods.Count * 140f)); for (int i = 0; i < foods.Count; i++) { ItemData food = foods[i]; Image val2 = ImageAt((Transform)(object)listContent, 4f, (float)i * 140f + 4f, 736f, 132f, new Color(0f, 0f, 0f, (i % 2 == 0) ? 0.23f : 0.1f)); Image obj = ImageAt(((Component)val2).transform, 12f, 18f, 64f, 64f, Color.white); obj.sprite = food.GetIcon(); obj.preserveAspect = true; Tooltip(((Component)val2).gameObject, food); Label(((Component)val2).transform, DisplayName(food), 92f, 4f, 474f, 31f, 22, heading: true); SharedData shared = food.m_shared; Label(((Component)val2).transform, $"Health {shared.m_food:0} Stamina {shared.m_foodStamina:0} Eitr {shared.m_foodEitr:0}", 92f, 35f, 510f, 27f); Label(((Component)val2).transform, $"Health regen {shared.m_foodRegen:0.##} HP / 10 sec (base)", 92f, 62f, 510f, 25f); eatButtons.Add(ButtonAt(((Component)val2).transform, "Eat", 616f, 16f, 102f, 38f, delegate { Eat(bookPlayer, food); })); Text[] array = (Text[])(object)new Text[3]; for (int num = 0; num < 3; num++) { int slot = num; Button val3 = ButtonAt(((Component)val2).transform, "", 92 + num * 164, 92f, 152f, 30f, delegate { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) bookPlayer.m_customData["local.foodunlock.slot.v1." + slot] = ((Object)food.m_dropPrefab).name; RefreshPermanentFood(bookPlayer); status = DisplayName(food) + " assigned to " + ((object)slotKeys[slot].Value/*cast due to .constrained prefix*/).ToString() + "."; RefreshAssignments(); }); array[num] = ((Component)val3).GetComponentInChildren<Text>(); } assignmentLabels.Add(array); } if (foods.Count == 0) { Label((Transform)(object)listContent, "No foods unlocked yet. Eat a food to begin.", 24f, 26f, 680f, 60f, 22); } foodScroll.verticalNormalizedPosition = 1f; for (int num2 = 0; num2 < sortButtons.Count; num2++) { ((Selectable)sortButtons[num2]).interactable = num2 != sortMode; } RefreshAssignments(); nextBookUpdate = 0f; } private void RefreshAssignments() { //IL_0047: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 3; i++) { string name = AssignedFood(bookPlayer, i); ItemData val = foods.Find((ItemData f) => ((Object)f.m_dropPrefab).name == name); slotLabels[i].text = ((object)slotKeys[i].Value/*cast due to .constrained prefix*/).ToString() + "\n" + ((val != null) ? DisplayName(val) : ((name.Length == 0) ? "Unassigned" : "Unavailable")); slotIcons[i].sprite = ((val != null) ? val.GetIcon() : null); ((Behaviour)slotIcons[i]).enabled = val != null; SetTooltip(slotTips[i], val); for (int num = 0; num < foods.Count; num++) { if ((Object)(object)assignmentLabels[num][i] != (Object)null) { assignmentLabels[num][i].text = ((((Object)foods[num].m_dropPrefab).name == name) ? "Assigned " : "Assign ") + ((object)slotKeys[i].Value/*cast due to .constrained prefix*/).ToString(); } } } } private void UpdateBookScale() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)panel == (Object)null)) { Transform parent = panel.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (!((Object)(object)val == (Object)null)) { float[] obj = new float[3] { 1f, 0f, 0f }; Rect rect = val.rect; obj[1] = (((Rect)(ref rect)).width - 32f) / 800f; rect = val.rect; obj[2] = (((Rect)(ref rect)).height - 32f) / 760f; float num = Mathf.Min(obj); panel.transform.localScale = Vector3.one * Mathf.Max(0.1f, num); } } } private void UpdateBook() { if ((Object)(object)panel == (Object)null) { Close(); } else if (!(Time.unscaledTime < nextBookUpdate)) { nextBookUpdate = Time.unscaledTime + 0.2f; UpdateBookScale(); for (int i = 0; i < eatButtons.Count; i++) { ((Selectable)eatButtons[i]).interactable = bookPlayer.CanEat(foods[i], false); } statusText.text = ((!string.IsNullOrEmpty(status)) ? status : (permanentAssignedFood.Value ? "Assigned foods stay at full strength through death. Clear a slot to remove its effect." : "Assign three foods above, then use their keys during play.")); } } private void DestroyBook() { if ((Object)(object)panel != (Object)null) { panel.SetActive(false); Object.Destroy((Object)(object)panel); } panel = null; eatButtons.Clear(); assignmentLabels.Clear(); sortButtons.Clear(); } private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_00eb: 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) instance = this; toggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Food book", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Open or close the food book. Eat a real food once to unlock it."); permanentAssignedFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "Permanent assigned food", false, "Automatically apply assigned foods at full health/stamina/eitr bonuses, without timer decay. Restores them after death. Clearing or replacing a slot immediately removes its old effect unless another slot still assigns that food. Assigned foods take priority within the three-food limit. Disabling returns active food to normal timers; this does not refill current health/stamina/eitr."); KeyCode[] array = new KeyCode[3]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); KeyCode[] array2 = (KeyCode[])(object)array; for (int i = 0; i < slotKeys.Length; i++) { slotKeys[i] = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Food slot " + (i + 1), array2[i], "Eat the food assigned in the book. Works outside menus, without Alt/Ctrl/Shift."); } harmony = new Harmony("local.foodunlock"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Food Unlock 0.3.4 loaded. Food book shortcut: " + ((object)toggle.Value/*cast due to .constrained prefix*/).ToString())); } private static bool IsFood(ItemData item) { if (item?.m_shared != null && (Object)(object)item.m_dropPrefab != (Object)null && item.m_shared.m_food > 0f && item.m_shared.m_foodBurnTime > 0f) { return (Object)(object)item.m_shared.m_consumeStatusEffect == (Object)null; } return false; } private static HashSet<string> ReadUnlocks(Player player) { string value; return new HashSet<string>(player.m_customData.TryGetValue("local.foodunlock.foods.v1", out value) ? ((IEnumerable<string>)value.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) : ((IEnumerable<string>)Array.Empty<string>()), StringComparer.Ordinal); } private void Update() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_008b: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; UpdatePermanentMode(localPlayer); if (visible && ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer != (Object)(object)bookPlayer || ((Character)localPlayer).IsDead())) { Close(); } KeyboardShortcut value; if (visible) { value = toggle.Value; if (((KeyboardShortcut)(ref value)).IsDown() || Input.GetKeyDown((KeyCode)27)) { Close(); } else { UpdateBook(); } } else { if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return; } value = toggle.Value; bool flag = ((KeyboardShortcut)(ref value)).IsDown(); int num = PressedSlot(localPlayer); if ((!flag && num < 0) || !(bool)TakeInput.Invoke(localPlayer, null)) { return; } if (!flag) { string text = AssignedFood(localPlayer, num); ItemData val = ResolveFood(text); if (val != null && ReadUnlocks(localPlayer).Contains(text)) { Eat(localPlayer, val); } else { ((Character)localPlayer).Message((MessageType)1, "Assigned food unavailable. Choose another in the food book.", 0, (Sprite)null, false); } return; } bookPlayer = localPlayer; status = ""; RefreshFoods(); if (!CreateBook()) { bookPlayer = null; return; } visible = true; GUIManager.BlockInput(true); } } private static string AssignedFood(Player player, int slot) { if (!player.m_customData.TryGetValue("local.foodunlock.slot.v1." + slot, out var value)) { return ""; } return value; } private int PressedSlot(Player player, bool held = false) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307) || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305) || Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) { return -1; } for (int i = 0; i < slotKeys.Length; i++) { if ((int)slotKeys[i].Value != 0 && (held ? Input.GetKey(slotKeys[i].Value) : Input.GetKeyDown(slotKeys[i].Value)) && AssignedFood(player, i).Length > 0) { return i; } } return -1; } private static ItemData ResolveFood(string name) { if (string.IsNullOrEmpty(name) || (Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); ItemData val = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData); if (val == null) { return null; } ItemData val2 = val.Clone(); val2.m_dropPrefab = itemPrefab; if (!IsFood(val2)) { return null; } return val2; } private void Eat(Player player, ItemData food) { if (permanentAssignedFood.Value && DesiredPermanentFoods(player).Any((PermanentFoodState.Selection selection) => selection.Item.m_shared.m_name == food.m_shared.m_name)) { RefreshPermanentFood(player); status = DisplayName(food) + " is already applied permanently while assigned."; return; } ItemData val = food.Clone(); val.m_dropPrefab = food.m_dropPrefab; if (((Humanoid)player).CanConsumeItem(val, true) && player.EatFood(val)) { status = "Ate " + DisplayName(val) + ". No item consumed."; ((Character)player).Message((MessageType)1, status, 0, (Sprite)null, false); } else { status = "You cannot eat that right now."; } } private void RefreshFoods() { foods.Clear(); if ((Object)(object)bookPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } foreach (string item in ReadUnlocks(bookPlayer)) { ItemData val = ResolveFood(item); if (val != null) { foods.Add(val); } } SortFoods(); } private static string DisplayName(ItemData food) { return Localization.instance.Localize(food.m_shared.m_name); } private void Close() { if (visible) { visible = false; GUIManager.BlockInput(false); DestroyBook(); bookPlayer = null; foods.Clear(); } } private void OnDestroy() { Close(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } private IReadOnlyList<PermanentFoodState.Selection> DesiredPermanentFoods(Player player) { player.m_customData.TryGetValue("local.foodunlock.foods.v1", out var value); string text = value + "\0" + string.Join("\0", from i in Enumerable.Range(0, 3) select AssignedFood(player, i)); if ((Object)(object)cachedFoodPlayer != (Object)(object)player || (Object)(object)cachedFoodDB != (Object)(object)ObjectDB.instance || cachedAssignments != text) { HashSet<string> hashSet = ReadUnlocks(player); cachedPermanentFoods = (from name in (from i in Enumerable.Range(0, 3) select AssignedFood(player, i)).Where(hashSet.Contains) select new PermanentFoodState.Selection { PrefabName = name, Item = ResolveFood(name) } into selection where selection.Item != null select selection).ToArray(); cachedFoodPlayer = player; cachedFoodDB = ObjectDB.instance; cachedAssignments = text; } return cachedPermanentFoods; } private void SyncPermanentFood(Player player) { if (!((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Character)player).IsDead() && !((Object)(object)ObjectDB.instance == (Object)null)) { List<Food> active = player.GetFoods(); Dictionary<string, string> customData = player.m_customData; IReadOnlyList<PermanentFoodState.Selection> assigned; if (!permanentAssignedFood.Value) { IReadOnlyList<PermanentFoodState.Selection> readOnlyList = Array.Empty<PermanentFoodState.Selection>(); assigned = readOnlyList; } else { assigned = DesiredPermanentFoods(player); } PermanentFoodState.Reconcile(active, customData, assigned, permanentAssignedFood.Value); } } private void RefreshPermanentFood(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Character)player).IsDead() && !((Object)(object)ObjectDB.instance == (Object)null) && (permanentAssignedFood.Value || player.m_customData.ContainsKey("local.foodunlock.permanent.v1"))) { SyncPermanentFood(player); object[] array = new object[3] { 0f, 0f, 0f }; TotalFood.Invoke(player, array); player.SetMaxHealth((float)array[0], true); player.SetMaxStamina((float)array[1], true); SetEitr(player, (float)array[2], arg3: true); } } private void UpdatePermanentMode(Player player) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead()) { lastFoodPlayer = null; } else if (!((Object)(object)ObjectDB.instance == (Object)null) && (!((Object)(object)player == (Object)(object)lastFoodPlayer) || lastPermanentMode != permanentAssignedFood.Value)) { lastFoodPlayer = player; lastPermanentMode = permanentAssignedFood.Value; RefreshPermanentFood(player); } } } internal static class PermanentFoodState { internal sealed class Selection { internal string PrefabName; internal ItemData Item; } internal const string ManagedKey = "local.foodunlock.permanent.v1"; internal static void Reconcile(List<Food> active, IDictionary<string, string> data, IReadOnlyList<Selection> assigned, bool enabled) { //IL_01a7: 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_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Expected O, but got Unknown if (!enabled) { data.Remove("local.foodunlock.permanent.v1"); return; } Selection[] array = (from @group in assigned.GroupBy<Selection, string>((Selection selection2) => selection2.Item.m_shared.m_name, StringComparer.Ordinal) select @group.First()).Take(3).ToArray(); HashSet<string> names = new HashSet<string>(array.Select((Selection selection2) => selection2.Item.m_shared.m_name), StringComparer.Ordinal); string value; HashSet<string> previous = new HashSet<string>(data.TryGetValue("local.foodunlock.permanent.v1", out value) ? ((IEnumerable<string>)value.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) : ((IEnumerable<string>)Array.Empty<string>()), StringComparer.Ordinal); active.RemoveAll((Food food) => previous.Contains(food.m_item.m_shared.m_name) && !names.Contains(food.m_item.m_shared.m_name)); Selection[] array2 = array; foreach (Selection selection in array2) { ItemData item = selection.Item; Food val = active.Find((Food entry) => entry.m_item.m_shared.m_name == item.m_shared.m_name); if (val == null) { while (active.Count >= 3) { Food val2 = (from entry in active where !names.Contains(entry.m_item.m_shared.m_name) orderby entry.m_time select entry).FirstOrDefault(); if (val2 == null) { break; } active.Remove(val2); } if (active.Count >= 3) { continue; } val = new Food { m_name = selection.PrefabName, m_item = item }; active.Add(val); } val.m_time = item.m_shared.m_foodBurnTime; val.m_health = item.m_shared.m_food; val.m_stamina = item.m_shared.m_foodStamina; val.m_eitr = item.m_shared.m_foodEitr; } if (names.Count == 0) { data.Remove("local.foodunlock.permanent.v1"); return; } data["local.foodunlock.permanent.v1"] = string.Join("\n", names.OrderBy<string, string>((string name) => name, StringComparer.Ordinal)); } } }