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 GearAndStorage v0.5.17
plugins/GearAndStorage/GearAndStorage.dll
Decompiled 2 months 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.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GaS; using GearAndStorage.Core; using HarmonyLib; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("GearAndStorage")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.17.0")] [assembly: AssemblyInformationalVersion("0.5.17")] [assembly: AssemblyProduct("GearAndStorage")] [assembly: AssemblyTitle("GearAndStorage")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.5.17.0")] [module: UnverifiableCode] namespace GaS { internal static class L { internal static Func<string> Language = () => "English"; internal static readonly Dictionary<string, string[]> Catalog = Load(); internal static string Code { get { switch (Language()?.ToLowerInvariant() ?? "") { default: return "en"; case "german": case "deutsch": case "de": return "de"; case "czech": case "čeština": case "cs": return "cs"; } } } private static Dictionary<string, string[]> Load() { Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal); using StreamReader streamReader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("GaS.Translations.tsv")); string text; while ((text = streamReader.ReadLine()) != null) { if (text.Length != 0 && !text.StartsWith("#")) { string[] array = text.Split('\t'); if (array.Length != 4) { throw new InvalidDataException("Invalid localization row: " + array[0]); } dictionary.Add(array[0], new string[3] { array[1], array[2], array[3] }); } } return dictionary; } internal static string T(string source) { if (source == null) { return ""; } if (!Catalog.TryGetValue(source, out var value)) { return source; } return value[(Code == "cs") ? 1 : ((Code == "de") ? 2 : 0)]; } internal static string F(string source, params object[] args) { return string.Format(CultureInfo.CurrentCulture, T(source), args); } internal static string Notice(string text) { if (string.IsNullOrEmpty(text)) { return text; } foreach (KeyValuePair<string, string[]> item in Catalog) { string[] value = item.Value; foreach (string text2 in value) { if (text == text2) { return T(item.Key); } if (item.Key.EndsWith(": ", StringComparison.Ordinal) && text.StartsWith(text2, StringComparison.Ordinal)) { return T(item.Key) + text.Substring(text2.Length); } } } return text; } } internal static class LanguageRuntime { private sealed class ConfigurationManagerAttributes { public string DispName; public string Category; public string Description; public Action<ConfigEntryBase> CustomDrawer; private bool expanded; public ConfigurationManagerAttributes(ConfigEntryBase entry) { DispName = Label(entry.Definition.Key); Category = Label(entry.Definition.Section); Description = Describe(entry.Description.Description); if (entry.SettingType.IsEnum && entry.Definition.Key == "Difficulty") { CustomDrawer = DrawDifficulty; } } private void DrawDifficulty(ConfigEntryBase entry) { GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); if (GUILayout.Button(L.T(entry.BoxedValue.ToString()) + " ▾", Array.Empty<GUILayoutOption>())) { expanded = !expanded; } if (expanded) { foreach (object value in Enum.GetValues(entry.SettingType)) { if (GUILayout.Button(L.T(value.ToString()), Array.Empty<GUILayoutOption>())) { entry.BoxedValue = value; expanded = false; } } } GUILayout.EndVertical(); } } private static string language; private static readonly List<ConfigFile> files = new List<ConfigFile>(); internal static void Register(ConfigFile file) { if (!files.Contains(file)) { files.Add(file); } } internal static void Tick() { L.Language = () => (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "English"; if (language == L.Code) { return; } language = L.Code; foreach (ConfigFile file in files) { foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in file) { Attach(item.Value); } } foreach (PluginInfo value in Chainloader.PluginInfos.Values) { BaseUnityPlugin instance = value.Instance; if (!((Object)(object)instance == (Object)null) && !(((object)instance).GetType().FullName != "ConfigurationManager.ConfigurationManager")) { ((object)instance).GetType().GetMethod("BuildSettingList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(instance, null); } } } private static void Attach(ConfigEntryBase entry) { object[] tags = entry.Description.Tags; tags = tags.Where((object t) => !(t is ConfigurationManagerAttributes)).ToArray(); typeof(ConfigDescription).GetField("<Tags>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(entry.Description, tags.Concat(new object[1] { new ConfigurationManagerAttributes(entry) }).ToArray()); } internal static string Label(string key) { if (key.StartsWith("Storage.")) { return L.T("Storage") + ": " + L.T(key.Substring(8)); } if (key.StartsWith("Allow") && key.Length > 5) { return L.T("Allow") + ": " + L.T(key.Substring(5)); } if (key.StartsWith("Hotkey")) { return L.T("Hotkey") + " " + key.Substring(6); } return L.T(key); } internal static string Describe(string source) { if (source.StartsWith("Additionally allow restricted items of ") && source.EndsWith(" even before the trophy unlock. False does not revoke an altar unlock.")) { return L.F("portal.allow.description", L.T(source.Substring("Additionally allow restricted items of ".Length, source.Length - "Additionally allow restricted items of ".Length - " even before the trophy unlock. False does not revoke an altar unlock.".Length))); } return L.T(source); } internal static string Creature(string prefab) { ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab2 = instance.GetPrefab(prefab); obj = ((prefab2 == null) ? null : prefab2.GetComponent<Character>()?.m_name); } if (obj == null) { obj = prefab; } string text = (string)obj; if (Localization.instance != null) { return Localization.instance.Localize(text); } return text; } } } namespace GearAndStorage { internal static class ChestCrafting { [HarmonyPatch(typeof(Container), "Awake")] private static class Register { private static void Postfix(Container __instance) { if (__instance.m_inventory != null) { Containers.Add(__instance); nextScan = 0f; } } } [HarmonyPatch(typeof(Player), "HaveRequirementItems")] private static class Requirements { private static void Prefix(Player __instance, bool discover, out bool __state) { __state = !discover && Enter(__instance); } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(Player), "GetFirstRequiredItem")] private static class OneIngredient { private static void Prefix(Player __instance, out bool __state) { __state = Enter(__instance); } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] private static class RequirementLabel { private static void Prefix(Player player, bool craft, out bool __state) { __state = Enter(player); } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] private static class Craft { private static void Prefix(Player player, out bool __state) { __state = Enter(player, force: true); if (__state) { craftingDepth++; } } private static void Finalizer(bool __state) { if (__state) { craftingDepth--; } Exit(__state); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] private static class BuildRequirements { private static void Prefix(Player __instance, RequirementMode mode, out bool __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 __state = (int)mode != 1 && Enter(__instance); } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static class Build { private static void Prefix(Player __instance, bool takeInput, out bool __state) { __state = takeInput && ((Character)__instance).InPlaceMode() && Enter(__instance); } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static class Place { private static bool Prefix(Player __instance, Piece piece, ref bool __result, out bool __state) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) __state = Enter(__instance, force: true); if (__state && !__instance.m_noPlacementCost && !ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()) && !__instance.HaveRequirements(piece, (RequirementMode)0)) { __result = false; return false; } return true; } private static void Finalizer(bool __state) { Exit(__state); } } [HarmonyPatch(typeof(Player), "ConsumeResources")] private static class Consume { private static void Prefix(Player __instance, out bool __state) { __state = !__instance.m_noPlacementCost && Enter(__instance, queryDepth == 0); if (__state) { craftingDepth++; } } private static void Finalizer(bool __state) { if (__state) { craftingDepth--; } Exit(__state); } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] private static class HaveMaterial { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (__result || !Query(__instance)) { return; } foreach (Container source in sources) { if (Accessible(source, Player.m_localPlayer) && source.m_inventory.HaveItem(name, matchWorldLevel)) { __result = true; break; } } } } [HarmonyPatch(typeof(Inventory), "CountItems")] private static class Count { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (!Query(__instance)) { return; } foreach (Container source in sources) { if (Accessible(source, Player.m_localPlayer)) { __result += source.m_inventory.CountItems(name, quality, matchWorldLevel); } } } } [HarmonyPatch(typeof(Inventory), "GetItem", new Type[] { typeof(string), typeof(int), typeof(bool) })] private static class Lookup { private static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result) { if (__result != null || !Query(__instance)) { return; } foreach (Container source in sources) { if (Accessible(source, Player.m_localPlayer)) { __result = source.m_inventory.GetItem(name, quality, isPrefabName); if (__result != null) { break; } } } } } [HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[] { typeof(string), typeof(int), typeof(bool) })] private static class CheatedIngredients { private static void Postfix(Inventory __instance, string itemName, int quality, bool matchWorldLevel, ref bool __result) { if (__result || !Query(__instance)) { return; } foreach (Container source in sources) { if (Accessible(source, Player.m_localPlayer) && source.m_inventory.ItemCheated(itemName, quality, matchWorldLevel)) { __result = true; break; } } } } [HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[] { typeof(Requirement[]), typeof(int), typeof(bool) })] private static class CheatedRecipe { private static void Postfix(Inventory __instance, Requirement[] resources, int quality, bool matchWorldLevel, ref bool __result) { if (__result || !Query(__instance)) { return; } foreach (Container source in sources) { if (Accessible(source, Player.m_localPlayer) && source.m_inventory.ItemCheated(resources, quality, matchWorldLevel)) { __result = true; break; } } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] private static class Payment { private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased) { if (craftingDepth == 0 || !Query(__instance)) { return true; } List<Inventory> list = new List<Inventory>(); list.Add(__instance); list.AddRange(from c in sources where Accessible(c, Player.m_localPlayer) select c.m_inventory); List<Tuple<Inventory, ItemData>> list2 = new List<Tuple<Inventory, ItemData>>(); foreach (Inventory item2 in list) { foreach (ItemData allItem in item2.GetAllItems()) { if (!(allItem.m_shared.m_name != name) && (itemQuality < 0 || allItem.m_quality == itemQuality) && (!worldLevelBased || allItem.m_worldLevel >= Game.m_worldLevel)) { list2.Add(Tuple.Create<Inventory, ItemData>(item2, allItem)); } } } int[] array = PaymentPlanner.Allocate(list2.Select((Tuple<Inventory, ItemData> entry) => entry.Item2.m_stack).ToArray(), amount); if (array == null) { throw new InvalidOperationException("GearAndStorage: crafting materials changed during native crafting; payment aborted."); } HashSet<Inventory> changed = new HashSet<Inventory>(); for (int num = 0; num < array.Length; num++) { if (array[num] != 0) { ItemData item = list2[num].Item2; item.m_stack -= array[num]; changed.Add(list2[num].Item1); } } foreach (Inventory item3 in changed) { item3.m_inventory.RemoveAll((ItemData val) => val.m_stack <= 0); } foreach (Container item4 in sources.Where((Container c) => changed.Contains(c.m_inventory))) { SaveChest(item4); } if (changed.Contains(__instance)) { __instance.Changed(false, false); } return false; } } private static ConfigEntry<bool> Enabled; private static ConfigEntry<float> Radius; private static readonly HashSet<Container> Containers = new HashSet<Container>(); private static List<Container> nearby = new List<Container>(); private static float nextScan; private static Vector3 scanPosition; private static Player scanningPlayer; private static int queryDepth; private static int craftingDepth; private static List<Container> sources; internal static IEnumerable<Container> LoadedContainers => Containers.Where((Container c) => (Object)(object)c != (Object)null); internal static bool IsEnabled => Enabled.Value; internal static float ChestRadius => Radius.Value; internal static void Initialize(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown Enabled = config.Bind<bool>("Crafting", "CraftFromChests", false, "Use materials from nearby accessible closed chests for crafting, upgrades and building. One toggle and range for all three. Furnace feeding is separate."); Radius = config.Bind<float>("Crafting", "ChestRange", 20f, new ConfigDescription("Maximum distance from the player in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); } internal static bool Eligible(Container c, Player player) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && c.m_inventory != null && (Object)(object)((Component)c).GetComponentInParent<TombStone>() == (Object)null && (Object)(object)player != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid() && !c.IsInUse() && c.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)c.m_wagon == (Object)null || !c.m_wagon.InUse()) && Vector3.Distance(((Component)player).transform.position, ((Component)c).transform.position) <= Radius.Value && c.CheckAccess(player.GetPlayerID())) { if (c.m_checkGuardStone) { return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false); } return true; } return false; } private static bool Accessible(Container c, Player player) { return Eligible(c, player); } internal static void SaveChest(Container chest) { chest.Save(); chest.m_inventory.Changed(false, false); } internal static List<Container> ExternalSources() { Player localPlayer = Player.m_localPlayer; if (!Enabled.Value || (Object)(object)localPlayer == (Object)null) { return new List<Container>(); } List<Container> list = Scan(localPlayer, force: false); foreach (Container item in list) { item.Load(); } return list; } internal static bool MachineAccess(Vector3 position, long creator) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) bool flag = false; foreach (PrivateArea allArea in PrivateArea.m_allAreas) { if (!((Object)(object)allArea == (Object)null) && allArea.IsEnabled() && allArea.IsInside(position, 0f)) { if (allArea.m_piece.GetCreator() == creator || allArea.IsPermitted(creator)) { return true; } flag = true; } } return !flag; } internal static List<Container> ForMachine(Vector3 center, long creator, float range = 10f) { //IL_0007: 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) Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null); return (from c in Containers where MachineChestAccess.Allowed(c, center, creator, range) orderby Vector3.SqrMagnitude(((Component)c).transform.position - center) select c).ToList(); } private static List<Container> Scan(Player player, bool force) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_00fb: Unknown result type (might be due to invalid IL or missing references) if (force || (Object)(object)player != (Object)(object)scanningPlayer || Time.time >= nextScan || Vector3.Distance(scanPosition, ((Component)player).transform.position) > 1f) { Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null); nearby = (from c in Containers where Accessible(c, player) orderby Vector3.SqrMagnitude(((Component)c).transform.position - ((Component)player).transform.position) select c).ToList(); foreach (Container item in nearby) { item.Load(); } scanningPlayer = player; scanPosition = ((Component)player).transform.position; nextScan = Time.time + 3f; } return nearby.Where((Container c) => Accessible(c, player)).ToList(); } private static bool Enter(Player player, bool force = false) { if (!Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } if (queryDepth == 0 || force) { sources = Scan(player, force); if (force) { foreach (Container source in sources) { source.Load(); } } } queryDepth++; return true; } private static void Exit(bool entered) { if (entered) { queryDepth--; if (queryDepth == 0) { sources = null; } } } private static bool Query(Inventory inventory) { if (queryDepth > 0 && sources != null && (Object)(object)Player.m_localPlayer != (Object)null) { return inventory == ((Humanoid)Player.m_localPlayer).GetInventory(); } return false; } } internal static class CoinPouchEpicLoot { [HarmonyPatch] private static class Passive { private static bool Prepare() { return Available; } private static MethodBase TargetMethod() { return Aggregate; } private static void Postfix(Player player, string effectType, object __result) { int num = CoinPouches.Tier(CoinPouches.Equipped(player)); if (num >= 0 && (effectType == null || !(effectType != Effects[num])) && __result is IList list) { list.Add(CreateEffect.Invoke(new object[2] { Effects[num], Values[num] })); } } } internal static readonly string[] Effects = new string[8] { "ModifyStaminaRegen", "ModifyMovementSpeed", "AddPoisonResistancePercentage", "AddFrostResistancePercentage", "ModifyStaminaRegen", "ModifyEitrRegen", "AddFireResistancePercentage", "ModifyHealthRegen" }; internal static readonly float[] Values = new float[8] { 3f, 2f, 5f, 5f, 6f, 6f, 8f, 8f }; private static readonly Type Extensions = AccessTools.TypeByName("EpicLoot.PlayerExtensions"); private static readonly MethodInfo Aggregate = ((Extensions == null) ? null : AccessTools.Method(Extensions, "GetAllActiveMagicEffects", (Type[])null, (Type[])null)); private static readonly Type Cache = ((Extensions == null) ? null : AccessTools.TypeByName("EpicLoot.EquipmentEffectCache")); private static readonly MethodInfo Reset = ((Cache == null) ? null : AccessTools.Method(Cache, "Reset", new Type[1] { typeof(Player) }, (Type[])null)); private static readonly Type Effect = ((Extensions == null) ? null : AccessTools.TypeByName("EpicLoot.MagicItemEffect")); private static readonly ConstructorInfo CreateEffect = Effect?.GetConstructor(new Type[2] { typeof(string), typeof(float) }); internal static bool Available { get { if (Aggregate != null) { return CreateEffect != null; } return false; } } internal static void Invalidate(Player player) { if ((Object)(object)player != (Object)null) { Reset?.Invoke(null, new object[1] { player }); } } } internal static class CoinPouches { [HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[] { typeof(ItemData), typeof(int) })] private static class CapacityCheck { private static void Postfix(Inventory __instance, ItemData item, int stack, ref bool __result) { if (!__result && OrdinaryCoin(item) && PlayerSlots.TryState(__instance, out var state) && !state.Loading) { int num = ((stack <= 0) ? item.m_stack : stack); __result = RewardRoom(__instance) + __instance.FindFreeStackSpace(item.m_shared.m_name, (float)item.m_worldLevel) + __instance.GetEmptySlots() * 10000 >= num; } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static class Pickup { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { if (Busy || !OrdinaryCoin(item) || __instance.ContainsItem(item) || RewardRoom(__instance) == 0 || !PlayerSlots.TryState(__instance, out var state) || state.Loading) { return true; } __result = Grant(__instance, state.NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { item }); if (__result) { __instance.Changed(false, false); } return false; } } [HarmonyPatch(typeof(Inventory), "CountItems")] private static class Count { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (!Busy) { __result += Available(__instance, name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] private static class Have { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result && !Busy) { __result = Available(__instance, name, -1, matchWorldLevel) > 0; } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] private static class Spend { [HarmonyPriority(800)] private static void Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased) { if (Busy || amount <= 0) { return; } int num = Available(__instance, name, itemQuality, worldLevelBased); if (num != 0) { int num2 = __instance.m_inventory.Where((ItemData i) => i.m_shared.m_name == name && (itemQuality < 0 || i.m_quality == itemQuality) && (!worldLevelBased || i.m_worldLevel >= Game.m_worldLevel)).Sum((ItemData i) => i.m_stack); int num3 = Math.Min(num, Math.Max(0, amount - num2)); if (num3 > 0) { ItemData item = Wallet(__instance); Set(item, Balance(item) - num3); amount -= num3; } } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] private static class Equip { [HarmonyPriority(800)] private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result) { if (IsPouch(item)) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { __result = false; Inventory inventory = ((Humanoid)val).GetInventory(); if (!inventory.ContainsItem(item) || ((Character)val).IsDead() || inventory.GetWidth() < 8) { return false; } ItemData val2 = Equipped(val); if (val2 != null && val2 != item) { if (!Read(val2, out var balance) || !Read(item, out var balance2) || (balance > 0 && val2.m_worldLevel != item.m_worldLevel)) { return false; } int num = balance + balance2; int num2 = Math.Min(num, Capacity(item)); int num3 = num - num2; if (num3 > 0) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); ItemData val3 = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); val3.m_dropPrefab = itemPrefab; val3.m_worldLevel = item.m_worldLevel; val3.m_stack = num3; if (!QuestRewardInventory.TryGrant(inventory, PlayerSlots.Track(val).NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { val3 })) { return false; } } Set(val2, 0); Set(item, num2); val2.m_equipped = false; PlayerSlots.SetTag(val2, -1); } item.m_equipped = true; PlayerSlots.SetTag(item, 13); PlayerSlots.Normalize(val); CoinPouchEpicLoot.Invalidate(val); inventory.Changed(false, false); __result = true; return false; } } return true; } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] private static class Unequip { private static bool Prefix(Humanoid __instance, ItemData item) { if (IsPouch(item)) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { item.m_equipped = false; PlayerSlots.SetTag(item, -1); CoinPouchEpicLoot.Invalidate(val); return false; } } return true; } } [HarmonyPatch(typeof(Humanoid), "UnequipAllItems")] private static class UnequipAll { private static void Prefix(Humanoid __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { ItemData val2 = Equipped(val); if (val2 != null) { val2.m_equipped = false; CoinPouchEpicLoot.Invalidate(val); } } } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] private static class Tooltip { private static void Postfix(ItemData item, ref string __result) { int num = Tier(item); if (num >= 0) { __result = __result + "\n\n" + L.T("Click: withdraw coins. Shift: split stack. Right-click: unequip. Ctrl + right-click: upgrade at workbench."); __result = __result + "\n\n" + (Read(item, out var balance) ? (balance.ToString("N0") + " / " + Capacity(item).ToString("N0") + " " + L.T("coins")) : L.T("Pouch data cannot be read. Contents preserved.")); __result = __result + "\n" + (CoinPouchEpicLoot.Available ? (L.T("Passive (Epic Loot)") + ": " + L.T(CoinPouchEpicLoot.Effects[num]) + " +" + CoinPouchEpicLoot.Values[num] + "%") : L.T("Install Epic Loot to enable the passive.")); } } } [HarmonyPatch(typeof(Humanoid), "IsItemEquiped")] private static class IsEquipped { private static void Postfix(Humanoid __instance, ItemData item, ref bool __result) { if (IsPouch(item)) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { __result = item.m_equipped && ((Humanoid)val).GetInventory().ContainsItem(item); } } } } [HarmonyPatch(typeof(Player), "UnequipDeathDropItems")] private static class Death { private static void Prefix(Player __instance) { ItemData val = Equipped(__instance); if (val != null) { val.m_equipped = false; CoinPouchEpicLoot.Invalidate(__instance); } } } internal static ConfigEntry<bool> AutoDeposit; internal static bool Busy; private static float nextTick; internal static void Initialize(ConfigFile config) { AutoDeposit = config.Bind<bool>("CoinPouch", "AutoDeposit", true, "Automatically store ordinary picked-up coins and quest coin rewards in the equipped pouch. Overflow stays in normal inventory."); } internal static int Tier(ItemData item) { object obj; if (item == null) { obj = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } string text = (string)obj; if (text == null || !text.StartsWith("GaS_CoinPouch_", StringComparison.Ordinal)) { return -1; } if (!int.TryParse(text.Substring(14), out var result) || result < 1 || result > 8) { return -1; } return result - 1; } internal static bool IsPouch(ItemData item) { return Tier(item) >= 0; } internal static int Capacity(ItemData item) { if (Tier(item) < 0) { return 0; } return CoinPouchState.Capacities[Tier(item)]; } internal static bool Read(ItemData item, out int balance) { balance = 0; if (!IsPouch(item)) { return false; } item.m_customData.TryGetValue("GearAndStorage.pouch.coins.v1", out var value); return CoinPouchState.TryRead(value, out balance); } internal static int Balance(ItemData item) { if (!Read(item, out var balance)) { return 0; } return balance; } internal static void Set(ItemData item, int balance) { item.m_customData["GearAndStorage.pouch.coins.v1"] = balance.ToString(CultureInfo.InvariantCulture); } internal static ItemData Equipped(Player p) { if (p == null) { return null; } return ((IEnumerable<ItemData>)((Humanoid)p).GetInventory().m_inventory).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.m_equipped && PlayerSlots.Tag(i) == 13 && IsPouch(i))); } internal static ItemData Wallet(Inventory inv) { if (!PlayerSlots.TryState(inv, out var state) || state.Loading || state.Player.m_isLoading || ((Character)state.Player).IsDead()) { return null; } return Equipped(state.Player); } internal static bool OrdinaryCoin(ItemData item) { object obj; if (item == null) { obj = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if ((string?)obj == "Coins" && item.m_quality == 1 && item.m_worldLevel == Game.m_worldLevel && item.m_customData.Count == 0) { return !item.m_equipped; } return false; } internal static int Available(Inventory inv, string name, int quality, bool matchWorld) { if (name != "$item_coins" || (quality != -1 && quality != 1)) { return 0; } ItemData val = Wallet(inv); if (val == null || (matchWorld && val.m_worldLevel < Game.m_worldLevel)) { return 0; } return Balance(val); } internal static int RewardRoom(Inventory inv) { ItemData val = Wallet(inv); if (!AutoDeposit.Value || val == null || val.m_worldLevel != Game.m_worldLevel || !Read(val, out var balance)) { return 0; } return Math.Max(0, Capacity(val) - balance); } internal static bool Grant(Inventory inv, int normalRows, IEnumerable<ItemData> rewards) { List<ItemData> list = new List<ItemData>(); int num = RewardRoom(inv); int num2 = 0; foreach (ItemData reward in rewards) { int num3 = (OrdinaryCoin(reward) ? Math.Min(num, reward.m_stack) : 0); num -= num3; num2 += num3; if (reward.m_stack > num3) { ItemData val = reward.Clone(); val.m_stack -= num3; list.Add(val); } } if (!QuestRewardInventory.TryGrant(inv, normalRows, list)) { return false; } if (num2 > 0) { ItemData item = Wallet(inv); Set(item, Balance(item) + num2); } return true; } internal static void Tick(Player player) { if (Busy || Time.realtimeSinceStartup < nextTick) { return; } nextTick = Time.realtimeSinceStartup + 0.25f; CoinPouchPrefabs.RefreshNames(); foreach (ItemData item in ((Humanoid)player).GetInventory().m_inventory.Where(IsPouch)) { if (((Object)(object)InventoryGui.instance == (Object)null || InventoryGui.instance.m_dragItem == null) && PlayerSlots.Tag(item) == 13 && !item.m_equipped && Equipped(player) == null && item.m_gridPos.x == 7 && item.m_gridPos.y == PlayerSlots.Track(player).NormalRows) { ((Humanoid)player).EquipItem(item, false); } if (PlayerSlots.Tag(item) != 13 && item.m_equipped) { ((Humanoid)player).UnequipItem(item, false); } } } internal static bool Withdraw(Player player, ItemData pouch, int amount, Vector2i? position = null) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_010d: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); if (amount <= 0 || amount > 10000 || Equipped(player) != pouch || !Read(pouch, out var balance) || amount > balance) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_worldLevel = pouch.m_worldLevel; val.m_stack = amount; if (position.HasValue) { Vector2i value = position.Value; if (value.x < 0 || value.x >= inventory.GetWidth() || value.y < 0 || value.y >= PlayerSlots.Track(player).NormalRows) { return false; } ItemData itemAt = inventory.GetItemAt(value.x, value.y); if (itemAt != null) { if (!OrdinaryCoin(itemAt) || itemAt.m_worldLevel != val.m_worldLevel || itemAt.m_stack + amount > 10000) { return false; } itemAt.m_stack += amount; } else { val.m_gridPos = value; inventory.m_inventory.Add(val); } } else if (!QuestRewardInventory.TryGrant(inventory, PlayerSlots.Track(player).NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { val })) { return false; } Set(pouch, balance - amount); CoinPouchUi.Withdrawn = true; inventory.Changed(false, false); return true; } } internal static class CoinPouchPrefabs { [HarmonyPatch] private static class Database { private static IEnumerable<MethodBase> TargetMethods() { return new string[2] { "Awake", "CopyOtherDB" }.Select((string n) => AccessTools.Method(typeof(ObjectDB), n, (Type[])null, (Type[])null)); } [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { Register(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class Network { private static void Postfix(ZNetScene __instance) { if ((Object)(object)ObjectDB.instance != (Object)null) { Register(ObjectDB.instance); } RegisterNetwork(__instance); } } internal static readonly string[] Names = new string[8] { "Meadows pouch", "Black Forest pouch", "Swamp pouch", "Mountain pouch", "Plains pouch", "Mistlands pouch", "Ashlands pouch", "Deep North pouch" }; internal static readonly string[][] Materials = new string[8][] { new string[2] { "LeatherScraps", "Wood" }, new string[2] { "DeerHide", "Bronze" }, new string[2] { "Guck", "Iron" }, new string[2] { "WolfPelt", "Silver" }, new string[3] { "LoxPelt", "Flax", "BlackMetal" }, new string[3] { "ScaleHide", "Carapace", "Softtissue" }, new string[2] { "AskHide", "FlametalNew" }, new string[2] { "SealHide", "ElakingHairBundle" } }; internal static readonly int[][] Costs = new int[8][] { new int[2] { 10, 5 }, new int[2] { 10, 4 }, new int[2] { 10, 4 }, new int[2] { 10, 4 }, new int[3] { 6, 20, 6 }, new int[3] { 10, 10, 10 }, new int[2] { 10, 6 }, new int[2] { 10, 10 } }; internal static readonly GameObject[] Prefabs = (GameObject[])(object)new GameObject[8]; private static GameObject root; private static string language; private static Sprite[] icons; private static void Icons() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_009c: 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) if (icons != null) { return; } icons = (Sprite[])(object)new Sprite[8]; using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("GaS.Pouches.png"); if (stream == null) { return; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { for (int i = 0; i < 8; i++) { icons[i] = Sprite.Create(val, new Rect((float)(i % 4 * ((Texture)val).width / 4), (float)((1 - i / 4) * ((Texture)val).height / 2), (float)(((Texture)val).width / 4), (float)(((Texture)val).height / 2)), new Vector2(0.5f, 0.5f)); } } } internal static void Register(ObjectDB db) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown GameObject val = ((IEnumerable<GameObject>)db.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "BeltStrength")); if ((Object)(object)val == (Object)null) { return; } Icons(); if ((Object)(object)root == (Object)null) { root = new GameObject("GaS.CoinPouchPrefabs"); root.SetActive(false); Object.DontDestroyOnLoad((Object)(object)root); } int i; for (i = 0; i < 8; i++) { if ((Object)(object)Prefabs[i] == (Object)null) { GameObject val2 = Object.Instantiate<GameObject>(val, root.transform); ((Object)val2).name = "GaS_CoinPouch_" + (i + 1); ItemData itemData = val2.GetComponent<ItemDrop>().m_itemData; itemData.m_shared = (SharedData)AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null).Invoke(itemData.m_shared, null); itemData.m_dropPrefab = val2; itemData.m_customData = new Dictionary<string, string>(); itemData.m_stack = 1; itemData.m_shared.m_name = L.T(Names[i]); itemData.m_shared.m_description = L.T("Coin storage. Equip in the pouch slot. Coins inside add no weight."); itemData.m_shared.m_weight = 1f + (float)i * 0.25f; itemData.m_shared.m_maxStackSize = 1; itemData.m_shared.m_maxQuality = 1; itemData.m_shared.m_equipStatusEffect = null; itemData.m_shared.m_setStatusEffect = null; itemData.m_shared.m_setName = ""; itemData.m_shared.m_useDurability = false; itemData.m_shared.m_value = 0; itemData.m_shared.m_questItem = false; if ((Object)(object)icons[i] != (Object)null) { itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { icons[i] }; } CoinPouches.Set(itemData, 0); Prefabs[i] = val2; } GameObject prefab = Prefabs[i]; if (!db.m_items.Contains(prefab)) { db.m_items.Add(prefab); } db.m_itemByHash[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab; db.m_itemByData[prefab.GetComponent<ItemDrop>().m_itemData.m_shared] = prefab; if (db.m_recipes.Any((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == "Recipe_" + ((Object)prefab).name)) { continue; } ItemDrop[] source = Materials[i].Select(delegate(string n) { GameObject itemPrefab = db.GetItemPrefab(n); return (itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>(); }).ToArray(); if (source.Any((ItemDrop m) => (Object)(object)m == (Object)null)) { Plugin.Log.LogWarning((object)("Pouch recipe unavailable: missing materials for " + Names[i])); continue; } Recipe val3 = ScriptableObject.CreateInstance<Recipe>(); ((Object)val3).name = "Recipe_" + ((Object)prefab).name; val3.m_item = prefab.GetComponent<ItemDrop>(); val3.m_amount = 1; val3.m_enabled = true; val3.m_minStationLevel = 1; val3.m_craftingStation = ((IEnumerable<Recipe>)db.m_recipes).FirstOrDefault((Func<Recipe, bool>)delegate(Recipe r) { object obj; if (r == null) { obj = null; } else { CraftingStation craftingStation = r.m_craftingStation; obj = ((craftingStation != null) ? ((Object)craftingStation).name : null); } return (string?)obj == "piece_workbench"; })?.m_craftingStation; if ((Object)(object)val3.m_craftingStation == (Object)null) { Object.Destroy((Object)(object)val3); continue; } val3.m_resources = ((IEnumerable<ItemDrop>)source).Select((Func<ItemDrop, int, Requirement>)((ItemDrop m, int j) => new Requirement { m_resItem = m, m_amount = Costs[i][j], m_amountPerLevel = 0, m_recover = true })).ToArray(); db.m_recipes.Add(val3); } RegisterNetwork(ZNetScene.instance); } private static void RegisterNetwork(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return; } GameObject[] prefabs = Prefabs; foreach (GameObject val in prefabs) { if ((Object)(object)val != (Object)null) { if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } scene.m_namedPrefabs[StringExtensionMethods.GetStableHashCode(((Object)val).name)] = val; } } } internal static void RefreshNames() { if (language == L.Code) { return; } language = L.Code; for (int i = 0; i < 8; i++) { if ((Object)(object)Prefabs[i] != (Object)null) { SharedData shared = Prefabs[i].GetComponent<ItemDrop>().m_itemData.m_shared; shared.m_name = L.T(Names[i]); shared.m_description = L.T("Coin storage. Equip in the pouch slot. Coins inside add no weight."); } } } internal static bool Upgrade(Player player, ItemData item) { int tier = CoinPouches.Tier(item) + 1; if (tier < 1 || tier >= 8 || CoinPouches.Equipped(player) != item || !CoinPouches.Read(item, out var _)) { return false; } Recipe val = ((IEnumerable<Recipe>)ObjectDB.instance.m_recipes).FirstOrDefault((Func<Recipe, bool>)((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == "Recipe_GaS_CoinPouch_" + (tier + 1))); if ((Object)(object)val == (Object)null || !player.HaveRequirements(val, false, 1, 1)) { return false; } player.ConsumeResources(val.m_resources, 1, -1, 1); item.m_dropPrefab = Prefabs[tier]; item.m_shared = val.m_item.m_itemData.m_shared; CoinPouchEpicLoot.Invalidate(player); ((Humanoid)player).GetInventory().Changed(false, false); return true; } } internal static class CoinPouchUi { [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] private static class KeepLocalCurrencyCursor { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null); MethodInfo operand = AccessTools.Method(typeof(CoinPouchUi), "ContainerCleanup", (Type[])null, (Type[])null); int num = 0; foreach (CodeInstruction item in list) { if (CodeInstructionExtensions.Calls(item, methodInfo)) { item.opcode = OpCodes.Call; item.operand = operand; num++; } } if (num != 1) { throw new InvalidOperationException("Unexpected InventoryGui.UpdateContainer cursor cleanup; expected one SetupDragItem call, found " + num); } return list; } } [HarmonyPatch(typeof(InventoryGui), "OnDropOutside")] private static class Outside { private static bool Prefix(InventoryGui __instance) { if (!IsPreview(__instance.m_dragInventory)) { return true; } DropOutside(__instance); return false; } } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] private static class GuardVirtualTransfer { private static bool Prefix(Inventory fromInventory, ref bool __result) { if (!IsPreview(fromInventory)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Humanoid), "DropItem")] private static class GuardVirtualDrop { private static bool Prefix(Inventory inventory, ref bool __result) { if (!IsPreview(inventory) || dropping) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")] private static class RightClick { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || __instance.m_dragItem != null || item == null || CoinPouches.Equipped(localPlayer) != item || grid.GetInventory() != ((Humanoid)localPlayer).GetInventory()) { return true; } if (ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true)) { ((Character)localPlayer).Message((MessageType)2, L.T(CoinPouchPrefabs.Upgrade(localPlayer, item) ? "Pouch upgraded. Coins preserved." : "Missing materials or workbench requirements."), 0, (Sprite)null, false); } else { ((Humanoid)localPlayer).RemoveEquipAction(item); ((Humanoid)localPlayer).UnequipItem(item, false); PlayerSlots.Normalize(localPlayer); ((Humanoid)localPlayer).GetInventory().Changed(false, false); } return false; } } internal static bool Withdrawn; private static ItemData sourcePouch; private static Inventory preview; private static bool dropping; private static bool Ready(Player player, int count, out int balance) { balance = 0; if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && !player.m_isLoading && !((Character)player).IsTeleporting() && sourcePouch != null && CoinPouches.Equipped(player) == sourcePouch && CoinPouches.Read(sourcePouch, out balance) && count > 0 && count <= balance) { return count <= 10000; } return false; } private static bool IsPreview(Inventory inv) { if (preview != null) { return inv == preview; } return false; } private static void Clear(InventoryGui gui) { gui.SetupDragItem((ItemData)null, (Inventory)null, 1); } internal static void Tick() { InventoryGui instance = InventoryGui.instance; if (!InventoryGui.IsVisible() || !((Object)(object)Player.m_localPlayer != (Object)null) || ((Character)Player.m_localPlayer).IsDead()) { Withdrawn = false; if ((Object)(object)instance != (Object)null && IsPreview(instance.m_dragInventory)) { Clear(instance); } sourcePouch = null; preview = null; } } internal static bool Select(InventoryGui gui, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_021e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || localPlayer.m_isLoading || ((Character)localPlayer).IsTeleporting()) { return false; } if (IsPreview(gui.m_dragInventory)) { if (grid.GetInventory() == ((Humanoid)localPlayer).GetInventory() && PlayerSlots.SlotAt(PlayerSlots.Track(localPlayer), pos.x, pos.y) == 13) { Clear(gui); return true; } if (!Ready(localPlayer, gui.m_dragAmount, out var _)) { Clear(gui); return true; } Place(gui, localPlayer, grid.GetInventory(), pos); return true; } if (grid.GetInventory() != ((Humanoid)localPlayer).GetInventory() || PlayerSlots.SlotAt(PlayerSlots.Track(localPlayer), pos.x, pos.y) != 13) { return false; } if (gui.m_dragItem != null) { if (CoinPouches.IsPouch(gui.m_dragItem) && gui.m_dragInventory == ((Humanoid)localPlayer).GetInventory()) { if (((Humanoid)localPlayer).EquipItem(gui.m_dragItem, false)) { Clear(gui); } } else if (CoinPouches.OrdinaryCoin(gui.m_dragItem)) { Inventory dragInventory = gui.m_dragInventory; ItemData val = CoinPouches.Equipped(localPlayer); if ((dragInventory != ((Humanoid)localPlayer).GetInventory() && (!((Object)(object)gui.m_currentContainer != (Object)null) || dragInventory != gui.m_currentContainer.GetInventory())) || val == null || !dragInventory.ContainsItem(gui.m_dragItem) || val.m_worldLevel != gui.m_dragItem.m_worldLevel || !CoinPouches.Read(val, out var balance2)) { return true; } int num = CoinPouchState.Deposit(balance2, CoinPouches.Capacity(val), Math.Max(0, Math.Min(gui.m_dragAmount, gui.m_dragItem.m_stack))); if (num > 0) { ItemData dragItem = gui.m_dragItem; dragItem.m_stack -= num; if (dragItem.m_stack == 0) { dragInventory.m_inventory.Remove(dragItem); } CoinPouches.Set(val, balance2 + num); Clear(gui); dragInventory.Changed(false, false); if (dragInventory != ((Humanoid)localPlayer).GetInventory()) { ((Humanoid)localPlayer).GetInventory().Changed(false, false); } } } return true; } if (!CoinPouches.IsPouch(item) || CoinPouches.Equipped(localPlayer) != item) { return true; } StartDrag(gui, localPlayer, item, mod); return true; } private static void StartDrag(InventoryGui gui, Player player, ItemData pouch, Modifier mod) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Invalid comparison between Unknown and I4 //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Invalid comparison between Unknown and I4 //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 //IL_0102: 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) if (!CoinPouches.Read(pouch, out var balance) || balance <= 0) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_stack = Math.Min(balance, 10000); val.m_worldLevel = pouch.m_worldLevel; sourcePouch = pouch; preview = new Inventory("GaS.CoinPouchCursor", (Sprite)null, 1, 1); preview.m_inventory.Add(val); if ((int)mod == 1 && val.m_stack > 1) { gui.ShowSplitDialog(val, preview); return; } gui.SetupDragItem(val, preview, val.m_stack); if ((int)mod == 3 || ((int)mod == 2 && (Object)(object)gui.m_currentContainer == (Object)null)) { DropOutside(gui); } else { if ((int)mod != 2) { return; } Inventory inventory = gui.m_currentContainer.GetInventory(); for (int i = 0; i < inventory.GetHeight(); i++) { if (gui.m_dragItem == null) { break; } for (int j = 0; j < inventory.GetWidth(); j++) { if (gui.m_dragItem == null) { break; } ItemData itemAt = inventory.GetItemAt(j, i); if (itemAt != null && CoinPouches.OrdinaryCoin(itemAt)) { Place(gui, player, inventory, new Vector2i(j, i)); } } } for (int k = 0; k < inventory.GetHeight(); k++) { if (gui.m_dragItem == null) { break; } for (int l = 0; l < inventory.GetWidth(); l++) { if (gui.m_dragItem == null) { break; } if (inventory.GetItemAt(l, k) == null) { Place(gui, player, inventory, new Vector2i(l, k)); } } } } } private static void Place(InventoryGui gui, Player player, Inventory target, Vector2i pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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) ItemData itemAt = target.GetItemAt(pos.x, pos.y); int num = gui.m_dragAmount; if (itemAt != null) { num = Math.Min(num, Math.Max(0, 10000 - itemAt.m_stack)); } if (Transfer(player, target, pos, num)) { int num2 = gui.m_dragAmount - num; if (num2 == 0) { Clear(gui); return; } gui.m_dragItem.m_stack = num2; gui.SetupDragItem(gui.m_dragItem, preview, num2); } } private static bool Transfer(Player player, Inventory target, Vector2i pos, int count) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_006b: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) if (!Ready(player, count, out var balance)) { return false; } if (target == ((Humanoid)player).GetInventory()) { return CoinPouches.Withdraw(player, sourcePouch, count, pos); } InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance.m_currentContainer == (Object)null || target != instance.m_currentContainer.GetInventory() || pos.x < 0 || pos.x >= target.GetWidth() || pos.y < 0 || pos.y >= target.GetHeight()) { return false; } ItemData itemAt = target.GetItemAt(pos.x, pos.y); if (itemAt != null && (!CoinPouches.OrdinaryCoin(itemAt) || itemAt.m_worldLevel != sourcePouch.m_worldLevel || itemAt.m_stack + count > 10000)) { return false; } ItemData val = preview.m_inventory[0].Clone(); val.m_stack = count; val.m_gridPos = pos; if (itemAt != null) { itemAt.m_stack += count; } else { target.m_inventory.Add(val); } CoinPouches.Set(sourcePouch, balance - count); Withdrawn = true; target.Changed(false, false); ((Humanoid)player).GetInventory().Changed(false, false); return true; } private static void DropOutside(InventoryGui gui) { Player localPlayer = Player.m_localPlayer; int dragAmount = gui.m_dragAmount; if (!Ready(localPlayer, dragAmount, out var balance)) { Clear(gui); return; } CoinPouches.Set(sourcePouch, balance - dragAmount); dropping = true; try { if (!((Humanoid)localPlayer).DropItem(preview, gui.m_dragItem, dragAmount)) { CoinPouches.Set(sourcePouch, balance); return; } Withdrawn = true; ((Humanoid)localPlayer).GetInventory().Changed(false, false); Clear(gui); } finally { dropping = false; } } private static void ContainerCleanup(InventoryGui gui, ItemData item, Inventory inventory, int amount) { if (item != null || !IsPreview(gui.m_dragInventory) || gui.m_dragItem == null || !preview.ContainsItem(gui.m_dragItem) || !InventoryGui.IsVisible() || !Ready(Player.m_localPlayer, gui.m_dragAmount, out var _)) { gui.SetupDragItem(item, inventory, amount); } } } internal static class DualUtility { private sealed class State { internal ItemData Extra; internal bool Swapping; } private sealed class Swap { internal Player Player; internal State State; internal ItemData Primary; } [HarmonyPatch(typeof(Humanoid), "EquipItem")] private static class Equip { private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result, out Swap __state) { __state = null; Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val == null || item == null || PlayerSlots.EquipmentIndex(item) != 4 || Get(val).Swapping) { return true; } State state = Get(val); if (((Humanoid)val).IsItemEquiped(item)) { return true; } int num = PlayerSlots.Tag(item); bool flag = num == 11 || (num != 4 && ((Humanoid)val).m_utilityItem != null && state.Extra == null); ItemData val2 = (flag ? ((Humanoid)val).m_utilityItem : state.Extra); if (val2 != null && val2.m_shared.m_name == item.m_shared.m_name) { __result = false; return false; } if (flag) { __state = Begin(val); } return true; } private static void Postfix(Humanoid __instance, ItemData item, bool __result, Swap __state) { if (__result && __instance is Player && PlayerSlots.EquipmentIndex(item) == 4) { PlayerSlots.SetTag(item, (__state == null) ? 4 : 11); } } private static void Finalizer(Swap __state) { End(__state); } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] private static class Unequip { private static void Prefix(Humanoid __instance, ItemData item, out Swap __state) { __state = null; Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !Get(val).Swapping && item != null && Extra((Humanoid)(object)val) == item) { __state = Begin(val); } } private static void Finalizer(Swap __state) { End(__state); } } [HarmonyPatch(typeof(Humanoid), "IsItemEquiped")] private static class IsEquipped { private static void Postfix(Humanoid __instance, ItemData item, ref bool __result) { if (item != null && Extra(__instance) == item) { __result = true; } } } [HarmonyPatch(typeof(Humanoid), "UnequipAllItems")] private static class UnequipAll { private static void Prefix(Humanoid __instance) { ItemData val = Extra(__instance); if (val != null) { __instance.UnequipItem(val, false); } } } [HarmonyPatch(typeof(Player), "UnequipDeathDropItems")] private static class Death { private static void Prefix(Player __instance) { ItemData val = Extra((Humanoid)(object)__instance); if (val != null) { ((Humanoid)__instance).UnequipItem(val, false); } } } [HarmonyPatch(typeof(Humanoid), "UpdateEquipmentStatusEffects")] private static class Effects { private static bool Prefix(Humanoid __instance) { if (!(__instance is Player)) { return true; } ItemData[] obj = new ItemData[9] { __instance.m_leftItem, __instance.m_rightItem, __instance.m_chestItem, __instance.m_legItem, __instance.m_helmetItem, __instance.m_shoulderItem, __instance.m_utilityItem, __instance.m_trinketItem, Extra(__instance) }; HashSet<StatusEffect> hashSet = new HashSet<StatusEffect>(); ItemData[] array = (ItemData[])(object)obj; foreach (ItemData val in array) { if (val != null) { if ((Object)(object)val.m_shared.m_equipStatusEffect != (Object)null) { hashSet.Add(val.m_shared.m_equipStatusEffect); } if (__instance.HaveSetEffect(val)) { hashSet.Add(val.m_shared.m_setStatusEffect); } } } foreach (StatusEffect equipmentStatusEffect in __instance.m_equipmentStatusEffects) { if (!hashSet.Contains(equipmentStatusEffect)) { ((Character)__instance).m_seman.RemoveStatusEffect(equipmentStatusEffect.NameHash(), false); } } foreach (StatusEffect item in hashSet) { if (!__instance.m_equipmentStatusEffects.Contains(item)) { ((Character)__instance).m_seman.AddStatusEffect(item, false, 0, 0f, (short)(-1)); } } __instance.m_equipmentStatusEffects.Clear(); __instance.m_equipmentStatusEffects.UnionWith(hashSet); return false; } } [HarmonyPatch(typeof(Humanoid), "GetSetCount")] private static class SetCount { private static void Postfix(Humanoid __instance, string setName, ref int __result) { if (Extra(__instance)?.m_shared.m_setName == setName) { __result++; } } } [HarmonyPatch(typeof(Humanoid), "UpdateEquipment")] private static class Durability { private static void Postfix(Humanoid __instance, float dt) { ItemData val = Extra(__instance); if (val != null && val.m_shared.m_useDurability) { __instance.DrainEquipedItemDurability(val, dt); } } } [HarmonyPatch(typeof(Player), "UpdateModifiers")] private static class Modifiers { private static void Postfix(Player __instance) { ItemData val = Extra((Humanoid)(object)__instance); if (val != null && Player.s_equipmentModifierSourceFields != null) { for (int i = 0; i < __instance.m_equipmentModifierValues.Length; i++) { __instance.m_equipmentModifierValues[i] += (float)Player.s_equipmentModifierSourceFields[i].GetValue(val.m_shared); } } } } [HarmonyPatch(typeof(Humanoid), "GetEquipmentWeight")] private static class EquipmentWeight { private static void Postfix(Humanoid __instance, ref float __result) { __result += Extra(__instance)?.m_shared.m_weight ?? 0f; } } [HarmonyPatch(typeof(Player), "GetEquipmentEitrRegenModifier")] private static class Eitr { private static void Postfix(Player __instance, ref float __result) { __result += Extra((Humanoid)(object)__instance)?.m_shared.m_eitrRegenModifier ?? 0f; } } private static readonly ConditionalWeakTable<Player, State> States = new ConditionalWeakTable<Player, State>(); private static State Get(Player player) { return States.GetValue(player, (Player _) => new State()); } internal static ItemData Extra(Humanoid player) { Player val = (Player)(object)((player is Player) ? player : null); if (val == null) { return null; } return Get(val).Extra; } internal static void Validate(Player player) { ItemData val = Extra((Humanoid)(object)player); if (val != null && !((Humanoid)player).GetInventory().ContainsItem(val)) { ((Humanoid)player).UnequipItem(val, false); } } private static Swap Begin(Player player) { State state = Get(player); Swap swap = new Swap { Player = player, State = state, Primary = ((Humanoid)player).m_utilityItem }; ((Humanoid)player).m_utilityItem = state.Extra; state.Extra = swap.Primary; state.Swapping = true; return swap; } private static void End(Swap swap) { if (swap != null) { swap.State.Extra = ((Humanoid)swap.Player).m_utilityItem; ((Humanoid)swap.Player).m_utilityItem = swap.Primary; swap.State.Swapping = false; ((Humanoid)swap.Player).SetupEquipment(); } } } internal static class EpicLootCrafting { private static MethodInfo unregister; internal static void Initialize() { try { Type type = (from a in AppDomain.CurrentDomain.GetAssemblies() select a.GetType("EpicLoot.API", throwOnError: false)).FirstOrDefault((Type t) => t != null); if (!(type == null)) { Type[] types = new Type[5] { typeof(string), typeof(Func<List<ItemData>>), typeof(Func<string, int>), typeof(Func<string, int, int>), typeof(Func<ItemData, int, int>) }; MethodInfo method = type.GetMethod("RegisterInventoryProvider", types); if (method == null || !method.IsStatic || method.ReturnType != typeof(bool)) { Plugin.Log.LogWarning((object)"Epic Loot inventory API unavailable; chest integration skipped."); } else if ((bool)method.Invoke(null, new object[5] { "mkova.GearAndStorage", new Func<List<ItemData>>(Items), new Func<string, int>(Count), new Func<string, int, int>(Remove), new Func<ItemData, int, int>(RemoveExact) })) { unregister = type.GetMethod("UnregisterInventoryProvider", new Type[1] { typeof(string) }); Plugin.Log.LogInfo((object)"Epic Loot optional crafting-material provider registered."); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Epic Loot integration skipped: " + ex.GetBaseException().Message)); } } internal static void Shutdown() { try { unregister?.Invoke(null, new object[1] { "mkova.GearAndStorage" }); } catch (Exception ex) { Plugin.Log.LogWarning((object)ex.GetBaseException().Message); } unregister = null; } private static bool Material(ItemData item) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (item?.m_shared != null) { return (int)item.m_shared.m_itemType == 1; } return false; } internal static List<ItemData> Items() { return ChestCrafting.ExternalSources().SelectMany((Container c) => c.m_inventory.GetAllItems()).Where(Material) .ToList(); } internal static int Count(string name) { return (from i in Items() where i.m_shared.m_name == name select i).Sum((ItemData i) => i.m_stack); } internal static int Remove(string name, int amount) { return Spend(name, null, amount); } internal static int RemoveExact(ItemData item, int amount) { if (!Material(item)) { return 0; } return Spend(null, item, amount); } private static int Spend(string name, ItemData exact, int amount) { if (amount <= 0) { return 0; } int num = amount; foreach (Container item in ChestCrafting.ExternalSources()) { bool flag = false; ItemData[] array = (from i in item.m_inventory.GetAllItems() where Material(i) && ((exact == null) ? (i.m_shared.m_name == name) : (i == exact)) select i).ToArray(); foreach (ItemData val in array) { int num3 = Math.Min(num, val.m_stack); val.m_stack -= num3; num -= num3; flag = flag || num3 > 0; if (val.m_stack == 0) { item.m_inventory.m_inventory.Remove(val); } if (num == 0) { break; } } if (flag) { ChestCrafting.SaveChest(item); } if (num == 0) { break; } } return amount - num; } } internal static class EquipmentRecovery { private static readonly ConditionalWeakTable<Player, HashSet<ItemData>> Pending = new ConditionalWeakTable<Player, HashSet<ItemData>>(); internal static void Clear(Player player) { Pending.Remove(player); } internal static void Queue(Inventory inventory, ItemData item) { if (item == null || !PlayerSlots.TryState(inventory, out var state) || state.Loading || state.Player.m_isLoading || ((Character)state.Player).IsDead()) { return; } int slot = PlayerSlots.Tag(item); if (LayoutPlanner.IsEquipment(slot) && PlayerSlots.ValidSlot(item, slot) && inventory.ContainsItem(item) && !((Humanoid)state.Player).IsItemEquiped(item)) { Pending.GetValue(state.Player, (Player _) => new HashSet<ItemData>()).Add(item); } } internal static void Tick(Player player) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00d8: Unknown result type (might be due to invalid IL or missing references) if (!Pending.TryGetValue(player, out var value)) { return; } if (((Character)player).IsDead() || player.m_isLoading || PlayerSlots.Track(player).Loading) { Clear(player); } else { if (((Character)player).InAttack() || ((Character)player).InDodge() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround())) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); foreach (ItemData item in new List<ItemData>(value)) { value.Remove(item); int slot = PlayerSlots.Tag(item); if (inventory.ContainsItem(item) && LayoutPlanner.IsEquipment(slot) && PlayerSlots.ValidSlot(item, slot) && !((Humanoid)player).IsItemEquiped(item)) { Vector2i val = PlayerSlots.Position(PlayerSlots.Track(player), slot); if (item.m_gridPos.x == val.x && item.m_gridPos.y == val.y && Worn(player, slot) == null) { ((Humanoid)player).EquipItem(item, false); } } } if (value.Count == 0) { Clear(player); } } } private static ItemData Worn(Player player, int slot) { return (ItemData)(slot switch { 0 => ((Humanoid)player).m_helmetItem, 1 => ((Humanoid)player).m_chestItem, 2 => ((Humanoid)player).m_legItem, 3 => ((Humanoid)player).m_shoulderItem, 4 => ((Humanoid)player).m_utilityItem, 11 => DualUtility.Extra((Humanoid)(object)player), 13 => CoinPouches.Equipped(player), 12 => ((Humanoid)player).m_trinketItem, _ => null, }); } } internal static class FurnaceFeeding { [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] private static class Feed { private static void Prefix(Smelter __instance) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (!Enabled.Value || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner() || (Object)(object)__instance.m_fuelItem == (Object)null || ((Object)((Component)__instance.m_fuelItem).gameObject).name != "Coal") { return; } Piece val = ((Component)__instance).GetComponent<Piece>() ?? ((Component)__instance).GetComponentInParent<Piece>(); long num = (((Object)(object)val != (Object)null) ? val.GetCreator() : 0); if (num == 0L || !ChestCrafting.MachineAccess(((Component)__instance).transform.position, num)) { return; } int num2 = FurnacePolicy.FuelSpace(__instance.GetFuel(), __instance.m_maxFuel); int num3 = Math.Max(0, __instance.m_maxOre - __instance.GetQueueSize()); if (num2 == 0 && num3 == 0) { return; } foreach (Container item in ChestCrafting.ForMachine(((Component)__instance).transform.position, num)) { Inventory inventory = item.GetInventory(); bool flag = false; ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData val2 in array) { string text = (((Object)(object)val2.m_dropPrefab != (Object)null) ? ((Object)val2.m_dropPrefab).name : ""); int num4 = 0; if (text == "Coal" && num2 > 0) { num4 = Math.Min(val2.m_stack, num2); __instance.SetFuel(__instance.GetFuel() + (float)num4); num2 -= num4; } else if (num3 > 0 && FurnacePolicy.IsRawOre(text) && __instance.IsItemAllowed(text)) { num4 = Math.Min(val2.m_stack, num3); for (int j = 0; j < num4; j++) { __instance.QueueOre(text, val2.m_cheated); } num3 -= num4; } if (num4 != 0) { val2.m_stack -= num4; flag = true; } } if (flag) { inventory.m_inventory.RemoveAll((ItemData item) => item.m_stack <= 0); inventory.Changed(false, false); } if (num2 == 0 && num3 == 0) { break; } } } } private static ConfigEntry<bool> Enabled; internal static void Initialize(ConfigFile config) { Enabled = config.Bind<bool>("Furnaces", "AutoFeedCoalAndOre", false, "Automatically supply coal and raw ore from accessible closed chests within a fixed 10 metres of the furnace. Only coal-fuelled furnaces and accepted raw ores; no wood, food or refined bars. Output collection has its own Production setting."); } } internal static class MachineChestAccess { internal static bool Allowed(Container chest, Vector3 center, long creator, float range) { //IL_0091: 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) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest != (Object)null && chest.m_inventory != null && (Object)(object)((Component)chest).GetComponentInParent<TombStone>() == (Object)null && (Object)(object)chest.m_nview != (Object)null && chest.m_nview.IsValid() && chest.m_nview.IsOwner() && !chest.IsInUse() && chest.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)chest.m_wagon == (Object)null || !chest.m_wagon.InUse()) && Vector3.Distance(center, ((Component)chest).transform.position) <= range && chest.CheckAccess(creator)) { if (chest.m_checkGuardStone) { return ChestCrafting.MachineAccess(((Component)chest).transform.position, creator); } return true; } return false; } } internal static class PlayerSlots { internal sealed class State { internal Player Player; internal int NormalRows = 6; internal bool Loading; } [HarmonyPatch(typeof(Player), "Awake")] private static class AwakePatch { private static void Postfix(Player __instance) { Track(__instance); } } [HarmonyPatch(typeof(Player), "Load")] private static class LoadPatch { private static void Prefix(Player __instance) { State state = Track(__instance); state.Loading = true; EquipmentRecovery.Clear(__instance); ((Humanoid)__instance).GetInventory().m_height = Math.Max(((Humanoid)__instance).GetInventory().GetHeight(), state.NormalRows + 2); } private static void Postfix(Player __instance) { Track(__instance).Loading = false; Normalize(__instance); } private static void Finalizer(Player __instance) { Track(__instance).Loading = false; } } [HarmonyPatch(typeof(Player), "Save")] private static class SavePatch { private static void Prefix(Player __instance) { Normalize(__instance); } } [HarmonyPatch(typeof(Humanoid), "DropInvalidItems")] private static class InvalidItemsPatch { private static bool Prefix(Humanoid __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val == null) { return true; } if (Track(val).Loading || val.m_isLoading) { return false; } Normalize(val); return true; } } [HarmonyPatch(typeof(Player), "SetInventorySize")] private static class ResizePatch { private static bool Prefix(Player __instance, int rows) { __instance.AddUniqueKeyValue("invrows", Math.Max(0, Math.Min(9, rows)).ToString()); Normalize(__instance); if ((Object)(object)InventoryGui.instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { InventoryGui.instance.SetInventorySize(Track(__instance).NormalRows); } return false; } } [HarmonyPatch(typeof(Inventory), "FindEmptySlot")] private static class EmptyCellPatch { private static bool Prefix(Inventory __instance, bool topFirst, ref Vector2i __result) { //IL_0063: 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) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!TryState(__instance, out var state) || state.Loading) { return true; } for (int i = 0; i < state.NormalRows; i++) { int num = (topFirst ? i : (state.NormalRows - i - 1)); for (int j = 0; j < __instance.GetWidth(); j++) { if (__instance.GetItemAt(j, num) == null) { __result = new Vector2i(j, num); return false; } } } __result = new Vector2i(-1, -1); return false; } } [HarmonyPatch(typeof(Inventory), "GetEmptySlots")] private static class EmptyCountPatch { private static bool Prefix(Inventory __instance, ref int __result) { if (!TryState(__instance, out var state) || state.Loading) { return true; } __result = 0; for (int i = 0; i < state.NormalRows; i++) { for (int j = 0; j < __instance.GetWidth(); j++) { if (__instance.GetItemAt(j, i) == null) { __result++; } } } return false; } } [HarmonyPatch(typeof(Inventory), "HaveEmptySlot")] private static class HasSpacePatch { private static bool Prefix(Inventory __instance, ref bool __result) { if (!TryState(__instance, out var state) || state.Loading) { return true; } __result = __instance.GetEmptySlots() > 0; return false; } } [HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[] { typeof(ItemData), typeof(int) })] private static class CanAddPatch { private static bool Prefix(Inventory __instance, ItemData item, int stack, ref bool __result) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!TryState(__instance, out var state) || state.Loading) { return true; } int slot = Tag(item); Vector2i val = Position(state, slot); __result = (ValidSlot(item, slot) && __instance.GetItemAt(val.x, val.y) == null) || __instance.FindFreeStackSpace(item.m_shared.m_name, (float)item.m_worldLevel) + __instance.GetEmptySlots() * item.m_shared.m_maxStackSize >= ((stack <= 0) ? item.m_stack : stack); return false; } } [HarmonyPatch(typeof(Inventory), "StackAll")] private static class StackPatch { private static void Prefix(Inventory fromInventory, out bool __state) { __state = Plugin.ProtectSlots.Value && TryState(fromInventory, out var _); if (__state) { stackDepth++; } } private static void Finalizer(bool __state) { if (__state) { stackDepth--; } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static class AddPatch { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result, out bool __state) { //IL_0067: 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) //IL_006e: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) __state = !__instance.ContainsItem(item); if (stackDepth > 0 && Tag(item) >= 0) { __result = false; return false; } if (QuickStack.Transferring && QuickStack.Protected(item)) { __result = false; return false; } if (!TryState(__instance, out var state) || state.Loading || __instance.ContainsItem(item)) { return true; } int slot = Tag(item); if (!ValidSlot(item, slot)) { return true; } Vector2i val = Position(state, slot); if (__instance.GetItemAt(val.x, val.y) != null) { return true; } item.m_gridPos = val; __instance.m_inventory.Add(item); __instance.Changed(true, item.m_cheated && !Achievements.IsCheatedAtAll()); __result = true; return false; } private static void Postfix(Inventory __instance, ItemData item, bool __result, bool __state) { if (__result && __state) { EquipmentRecovery.Queue(__instance, item); } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] private static class CoordinatePatch { private static bool Prefix(Inventory __instance, ItemData item, int x, int y, ref bool __result) { if (!TryState(__instance, out var state) || state.Loading || y < state.NormalRows) { return true; } int slot = SlotAt(state, x, y); if (ValidSlot(item, slot)) { return true; } __result = false; return false; } private static void Postfix(Inventory __instance, int x, int y, bool __result) { if (__result) { EquipmentRecovery.Queue(__instance, __instance.GetItemAt(x, y)); } } } internal const string SlotKey = "GearAndStorage.slot"; private static readonly ConditionalWeakTable<Inventory, State> States = new ConditionalWeakTable<Inventory, State>(); private static int stackDepth; internal static bool TryState(Inventory inventory, out State state) { return States.TryGetValue(inventory, out state); } internal static State Track(Player player) { return States.GetValue(((Humanoid)player).GetInventory(), (Inventory _) => new State { Player = player, NormalRows = Plugin.Rows.Value }); } internal static int EquipmentIndex(ItemData item) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001a: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 if (CoinPouches.IsPouch(item)) { return 13; } ItemType itemType = item.m_shared.m_itemType; if ((int)itemType <= 11) { if ((int)itemType == 6) { return 0; } if ((int)itemType == 7) { return 1; } if ((int)itemType == 11) { return 2; } } else { if ((int)itemType == 17) { return 3; } if ((int)itemType == 18) { return 4; } if ((int)itemType == 24) { return 12; } } return -1; } internal static int Tag(ItemData item) { if (item != null && item.m_customData.TryGetValue("GearAndStorage.slot", out var value) && int.TryParse(value, out var result)) { return result; } return -1; } internal static void SetTag(ItemData item, int slot) { if (slot < 0) { item.m_customData.Remove("GearAndStorage.slot"); } else { item.m_customData["GearAndStorage.slot"] = slot.ToString(); } } internal static bool ValidSlot(ItemData item, int slot) { if (slot != 13) { if (slot != 12) { if (slot != 11) { if (slot >= 0 && slot < 5 + Plugin.QuickCount.Value) { if (slot < 5) { return EquipmentIndex(item) == slot; } return true; } return false; } return EquipmentIndex(item) == 4; } return EquipmentIndex(item) == 12; } return CoinPouches.IsPouch(item); } internal static Vector2i Position(State state, int slot) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) return new Vector2i((slot == 13) ? 7 : ((slot == 12) ? 6 : ((slot == 11) ? 5 : ((slot < 5) ? slot : (slot - 5)))), state.NormalRows + ((!LayoutPlanner.IsEquipment(slot)) ? 1 : 0)); } internal static int SlotAt(State state, int x, int y) { if (y == state.NormalRows && x >= 0 && x < 5) { return x; } if (y == state.NormalRows && x == 5) { return 11; } if (y == state.NormalRows && x == 7) { return 13; } if (y == state.NormalRows && x == 6) { return 12; } if (y == state.NormalRows + 1 && x >= 0 && x < Plugin.QuickCount.Value) { return 5 + x; } return -1; } internal static ItemData GetSlot(Player player, int slot) { return ((IEnumerable<ItemData>)((Humanoid)player).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData x) => Tag(x) == slot)); } internal static void Normalize(Player player) { //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) State state = Track(player); if (state.Loading || player.m_isLoading) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); int num = Plugin.Rows.Value; string s = default(string); if (player.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result)) { num = Math.Max(num, Math.Min(9, result)); } List<ItemData> allItems = inventory.GetAllItems(); List<LayoutItem> list = new List<LayoutItem>(allItems.Count); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; int slot = Tag(val); if (val.m_equipped && EquipmentIndex(val) >= 0) { slot = ((val == DualUtility.Extra((Humanoid)(object)player)) ? 11 : EquipmentIndex(val)); } if (!ValidSlot(val, slot)) { slot = -1; } list.Add(new LayoutItem { Id = i, X = val.m_gridPos.x, Y = val.m_gridPos.y, Slot = slot, Equipped = val.m_equipped }); } LayoutResult layoutResult = LayoutPlanner.Arrange(list, inventory.GetWidth(), num, Plugin.QuickCount.Value); if (layoutResult.NormalRows > num && layoutResult.NormalRows != state.NormalRows) { Plugin.Log.LogWarning((object)("Inventory recovery: added visible space to preserve all items (" + layoutResult.NormalRows + " normal rows).")); } state.NormalRows = layoutResult.NormalRows; foreach (LayoutItem item in layoutResult.Items) { ItemData obj = allItems[item.Id]; obj.m_gridPos = new Vector2i(item.X, item.Y); SetTag(obj, item.Slot); } inventory.m_height = state.NormalRows + 2; } } [BepInPlugin("mkova.GearAndStorage", "GearAndStorage", "0.5.17")] [BepInIncompatibility("randyknapp.mods.equipmentandquickslots")] [BepInIncompatibility("shudnal.ExtraSlots")] [BepInIncompatibility("Azumatt.AzuExtendedPlayerInventory")] [BepInIncompatibility("aedenthorn.ExtendedPlayerInventory")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "mkova.GearAndStorage"; public const string Version = "0.5.17"; internal static ModDataLayout Data; internal static ConfigEntry<int> Rows; internal static ConfigEntry<int> QuickCount; internal static ConfigEntry<bool> ProtectSlots; internal static ConfigEntry<bool> AnchorAboveGuardianPower; internal static ConfigEntry<Vector2> HudPosition; internal static ConfigEntry<Vector2> PanelPosition; internal static ConfigEntry<Vector2> GuardianPowerOffset; internal static readonly ConfigEntry<KeyboardShortcut>[] Keys = new ConfigEntry<KeyboardShortcut>[6]; internal static ManualLogSource Log; private Harmony harmony; private bool active; internal static bool CanUseQuickSlots { get { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && !localPlayer.m_isLoading && !QuestUi.Open) { return CanAct(localPlayer); } return false; } } private void Awake() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_0124: 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_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Expected O, but got Unknown //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Data = new ModDataLayout(Paths.ConfigPath); Data.Ensure(); if (!File.Exists(Data.SettingsPath) && File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { File.Copy(((BaseUnityPlugin)this).Config.ConfigFilePath, Data.SettingsPath); } ConfigFile value = new ConfigFile(Data.SettingsPath, false, ((BaseUnityPlugin)this).Info.Metadata); (AccessTools.Field(typeof(BaseUnityPlugin), "<Config>k__BackingField") ?? throw new MissingFieldException("BepInEx BaseUnityPlugin.Config backing field")).SetValue(this, value); Rows = ((BaseUnityPlugin)this).Config.Bind<int>("Player", "InventoryRows", 6, new ConfigDescription("Minimum normal inventory rows; purchased rows and recovery space are preserved.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 9), Array.Empty<object>())); QuickCount = ((BaseUnityPlugin)this).Config.Bind<int>("QuickSlots", "Count", 3, new ConfigDescription("Quick slots. Removed slots return items to normal inventory.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>())); HudPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("QuickSlots", "HudPosition", new Vector2(20f, 160f), "HUD position in pixels from the lower left corner."); AnchorAboveGuardianPower = ((BaseUnityPlugin)this).Config.Bind<bool>("QuickSlots", "AnchorAboveGuardianPower", true, "Center the quick bar above the guardian power (Eikthyr) icon. Disable to use HudPosition."); GuardianPowerOffset = ((BaseUnityPlugin)this).Config.Bind<Vector2>("QuickSlots", "GuardianPowerOffset", new Vector2(0f, 12f), "Offset in screen pixels from the top of the guardian power display: X horizontal, Y gap above it."); PanelPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("Equipment", "PanelPosition", new Vector2(12f, -20f), "Slot panel offset from the upper right corner of the player inventory."); ProtectSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protection", "ProtectFromStackAll", true, "Keep equipment and quick slot items out of bulk container stacking."); KeyCode[] array = new KeyCode[6]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); KeyCode[] array2 = (KeyCode[])(object)array; for (int i = 0; i < Keys.Length; i++) { Keys[i] = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("QuickSlots", "Hotkey" + (i + 1), (i < 3) ? new KeyboardShortcut(array2[i], (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }) : new KeyboardShortcut(array2[i], Array.Empty<KeyCode>()), "Use this quick slot once per key press, also while moving or sprinting. Letters follow the current keyboard layout; hold the specified modifiers (LeftAlt is the left Alt)."); } Storage.Initialize(((BaseUnityPlugin)this).Config); WeightSettings.Initialize(((BaseUnityPlugin)this).Config); StackSettings.Initialize(((BaseUnityPlugin)this).Config); PortalRules.Initialize(((BaseUnityPlugin)this).Config); ChestCrafting.Initialize(((BaseUnityPlugin)this).Config); FurnaceFeeding.Initialize(((BaseUnityPlugin)this).Config); ProductionStorage.Initialize(((BaseUnityPlugin)this).Config); WorkbenchNetwork.Initialize(((BaseUnityPlugin)this).Config); QuickStack.Initialize(((BaseUnityPlugin)this).Config); QuestSystem.Initialize(((BaseUnityPlugin)this).Config); QuestUi.Initialize(((BaseUnityPlugin)this).Config); TrashUi.Initialize(((BaseUnityPlugin)this).Config); CoinPouches.Initialize(((BaseUnityPlugin)this).Config); ServerSettings.Initialize(((BaseUnityPlugin)this).Config); LanguageRuntime.Register(((BaseUnityPlugin)this).Config); LanguageRuntime.Register(((ConfigEntryBase)QuestPreferences.AutoAcceptNext).ConfigFile); LanguageRuntime.Tick(); if (Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value2)) { BaseUnityPlugin instance = value2.Instance; ConfigEntry<bool> val = default(ConfigEntry<bool>); if ((Object)(object)instance == (Object)null || !instance.Config.TryGetEntry<bool>(new ConfigDefinition("Inventory", "enabled"), ref val) || val.Value) { ((BaseUnityPlugin)this).Logger.LogError((object)"GearAndStorage not started: disable Valheim Plus [Inventory] first. Other V+ features may remain enabled."); return; } } harmony = new Harmony("mkova.GearAndStorage"); harmony.PatchAll(typeof(Plugin).Assembly); EpicLootCrafting.Initialize(); active = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"GearAndStorage 0.5.17 loaded: independent storage, equipment and quick slots."); } private void LateUpdate() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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) if (!active) { return; } LanguageRuntime.Tick(); TrashUi.Tick(); ServerSettings.Tick(); StackSettings.Tick(); WorkbenchNetwork.Tick(); QuestNetwork.Tick(); Player localPlayer = Player.m_localPlayer; CoinPouchUi.Tick(); if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || localPlayer.m_isLoading) { if ((Object)(object)localPlayer != (Object)null) { EquipmentRecovery.Clear(localPlayer); } QuickStack.Tick(localPlayer, canAct: false); QuestUi.Close(); return; } CoinPouches.Tick(localPlayer); QuestSystem.Tick(localPlayer); DualUtility.Validate(localPlayer); if ((Object)(object)InventoryGui.instance == (Object)null || InventoryGui.instance.m_dragItem == null) { PlayerSlots.Normalize(localPlayer); EquipmentRecovery.Tick(localPlayer); } bool flag = CanAct(localPlayer); QuestUi.Tick(flag); QuickStack.Tick(localPlayer, flag && !QuestUi.Open); if (!flag || QuestUi.Open) { return; } KeyboardShortcut value = QuickStack.Key.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { QuickStack.Start(localPlayer); return; } for (int i = 0; i < QuickCount.Value; i++) { if (QuickSlotInput.IsDown(Keys[i].Value)) { ItemData slot = PlayerSlots.GetSlot(localPlayer, 5 + i); if (slot != null) { ((Humanoid)localPlayer).UseItem(((Humanoid)localPlayer).GetInventory(), slot, false); } } } } private static bool CanAct(Player player) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 if (Application.isFocused) { if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !Hud.InRadial()) { if ((Object)(object)Minimap.instance != (Object)null) { return (int)Minimap.instance.m_mode != 2; } return true; } return false; } return false; } private void OnGUI() { if (active) { if (!QuestUi.Open) { SlotUi.DrawHud(); } QuestUi.Draw(); } } private void OnDestroy() { TrashUi.Cancel(); EpicLootCrafting.Shutdown(); ServerSettings.Restore(); Harmony obj = harmony; if (obj != null) { obj.Unpat