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.7
plugins/GearAndStorage/GearAndStorage.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; 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 GearAndStorage.Core; using HarmonyLib; using Newtonsoft.Json; using TMPro; using UnityEngine; 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.7.0")] [assembly: AssemblyInformationalVersion("0.5.7")] [assembly: AssemblyProduct("GearAndStorage")] [assembly: AssemblyTitle("GearAndStorage")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.5.7.0")] [module: UnverifiableCode] 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> hashSet = 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]; hashSet.Add(list2[num].Item1); } } foreach (Inventory item3 in hashSet) { item3.m_inventory.RemoveAll((ItemData val) => val.m_stack <= 0); } foreach (Inventory item4 in hashSet) { item4.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 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>())); } private static bool Accessible(Container c, Player player) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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)c.m_nview != (Object)null && c.m_nview.IsValid() && c.m_nview.IsOwner() && !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; } 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_003e: 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(); scanningPlayer = player; scanPosition = ((Component)player).transform.position; nextScan = Time.time + 0.5f; } 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 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 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) { Track(__instance).Loading = true; } 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(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) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) 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; } } [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; } } 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_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_000f: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 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 != 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; } internal static Vector2i Position(State state, int slot) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) return new Vector2i((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 == 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.7")] [BepInIncompatibility("randyknapp.mods.equipmentandquickslots")] [BepInIncompatibility("shudnal.ExtraSlots")] [BepInIncompatibility("Azumatt.AzuExtendedPlayerInventory")] [BepInIncompatibility("aedenthorn.ExtendedPlayerInventory")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "mkova.GearAndStorage"; public const string Version = "0.5.7"; 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_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Expected O, but got Unknown //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0319: 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); ServerSettings.Initialize(((BaseUnityPlugin)this).Config); 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); active = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"GearAndStorage 0.5.7 loaded: independent storage, equipment and quick slots."); } private void LateUpdate() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (!active) { return; } ServerSettings.Tick(); StackSettings.Tick(); WorkbenchNetwork.Tick(); QuestNetwork.Tick(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || localPlayer.m_isLoading) { QuickStack.Tick(localPlayer, canAct: false); QuestUi.Close(); return; } QuestSystem.Tick(localPlayer); DualUtility.Validate(localPlayer); if ((Object)(object)InventoryGui.instance == (Object)null || InventoryGui.instance.m_dragItem == null) { PlayerSlots.Normalize(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() { ServerSettings.Restore(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } internal static class PortalRules { private sealed class Rule { internal string Biome; internal ConfigEntry<bool> Manual; internal ConfigEntry<string> Items; internal ConfigEntry<string> Trophy; internal string WorldKey => "gearandstorage_portals_" + Biome.ToLowerInvariant(); } [HarmonyPatch(typeof(ItemStand), "SetVisualItem")] private static class Altar { private static void Postfix(ItemStand __instance) { if (!AutoUnlock.Value || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)__instance.m_guardianPower == (Object)null || !__instance.HaveAttachment() || (Object)(object)ObjectDB.instance == (Object)null) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(__instance.GetAttachedItem()); if ((Object)(object)itemPrefab == (Object)null) { return; } foreach (Rule rule in Rules) { if (!string.IsNullOrWhiteSpace(rule.Trophy.Value) && Contains(rule.Trophy.Value, ((Object)itemPrefab).name) && !ZoneSystem.instance.GetGlobalKey(rule.WorldKey)) { ZoneSystem.instance.SetGlobalKey(rule.WorldKey); Plugin.Log.LogInfo((object)("Portal transport unlocked for this world: " + rule.Biome + " (trophy at altar).")); } } } } [HarmonyPatch(typeof(Inventory), "IsTeleportable")] private static class Teleport { private static void Postfix(Inventory __instance, ref bool __result) { if (__result) { return; } foreach (ItemData allItem in __instance.GetAllItems()) { if (allItem.m_shared.m_toolTier >= 1000) { return; } if (allItem.m_shared.m_teleportable) { continue; } GameObject dropPrefab = allItem.m_dropPrefab; if ((Object)(object)dropPrefab == (Object)null) { return; } bool flag = false; foreach (Rule rule in Rules) { if (Contains(rule.Items.Value, ((Object)dropPrefab).name) && (rule.Manual.Value || (AutoUnlock.Value && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(rule.WorldKey)))) { flag = true; break; } } if (!flag) { return; } } __result = true; } } private static readonly List<Rule> Rules = new List<Rule>(); private static ConfigEntry<bool> AutoUnlock; internal static void Initialize(ConfigFile config) { AutoUnlock = config.Bind<bool>("Portals", "UnlockOnTrophyAtAltar", true, "Permanently unlock the biome for this world when its boss trophy is mounted at the sacrificial stones. Boss kills and decorative item stands do not count."); Add(config, "Meadows", "TrophyEikthyr", ""); Add(config, "BlackForest", "TrophyTheElder", "CopperOre,TinOre,Copper,Tin,Bronze"); Add(config, "Swamp", "TrophyBonemass", "IronScrap,Iron"); Add(config, "Mountain", "TrophyDragonQueen", "SilverOre,Silver,DragonEgg"); Add(config, "Plains", "TrophyGoblinKing", "BlackMetalScrap,BlackMetal"); Add(config, "Mistlands", "TrophySeekerQueen", "DvergrNeedle,CopperScrap"); Add(config, "AshLands", "TrophyFader", "FlametalOreNew,FlametalNew,FlametalOre,Flametal"); Add(config, "DeepNorth", "", "Gold"); Add(config, "Ocean", "", ""); } private static void Add(ConfigFile config, string biome, string trophy, string items) { Rules.Add(new Rule { Biome = biome, Manual = config.Bind<bool>("Portals", "Allow" + biome, false, "Additionally allow restricted items of " + biome + " even before the trophy unlock. False does not revoke an altar unlock."), Items = config.Bind<string>("Portals.Items", biome, items, "Comma-separated item prefab names assigned to this biome. Unknown items remain blocked; extend for modded items."), Trophy = config.Bind<string>("Portals.Trophies", biome, trophy, "Boss trophy prefab mounted on a guardian-power altar. Empty means no automatic trophy unlock for this biome.") }); } private static bool Contains(string csv, string value) { string[] array = csv.Split(','); for (int i = 0; i < array.Length; i++) { if (string.Equals(array[i].Trim(), value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } } internal static class ProductionOptions { internal static readonly Dictionary<string, ConfigEntry<bool>> Stations = new Dictionary<string, ConfigEntry<bool>>(StringComparer.Ordinal); internal static readonly Dictionary<string, string> Keys = new Dictionary<string, string>(StringComparer.Ordinal) { ["smelter"] = "Smelter", ["blastfurnace"] = "BlastFurnace", ["charcoal_kiln"] = "CharcoalKiln", ["windmill"] = "Windmill", ["spinningwheel"] = "SpinningWheel", ["eitrrefinery"] = "EitrRefinery", ["piece_cookingstation"] = "CookingStation", ["piece_cookingstation_iron"] = "IronCookingStation", ["piece_oven"] = "Oven", ["fermenter"] = "Fermenter", ["sapcollector"] = "SapCollector" }; internal static void Initialize(ConfigFile config) { ConfigEntry<bool> val = config.Bind<bool>("Production", "StoreOutputInChests", false, (ConfigDescription)null); bool value = val.Value; foreach (string value2 in Keys.Values) { Stations[value2] = config.Bind<bool>("Production", value2, value, "Automatically store finished output from this station in accessible closed chests. Server controlled."); } config.Remove(((ConfigEntryBase)val).Definition); } internal static bool Allows(string prefab) { if (prefab == null) { return false; } if (prefab.EndsWith("(Clone)", StringComparison.Ordinal)) { prefab = prefab.Substring(0, prefab.Length - "(Clone)".Length); } if (Keys.TryGetValue(prefab, out var value) && Stations.TryGetValue(value, out var value2)) { return value2.Value; } return false; } } internal static class ProductionStorage { private sealed class OutputBatch { internal Component Machine; internal OutputBatch Parent; internal readonly HashSet<ItemDrop> Drops = new HashSet<ItemDrop>(); internal HashSet<string> Prefabs; } [HarmonyPatch] private static class CaptureOutput { private static IEnumerable<MethodBase> TargetMethods() { yield return typeof(Smelter).GetMethod("Spawn", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); yield return typeof(Fermenter).GetMethod("DelayedTap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); yield return typeof(CookingStation).GetMethod("SpawnItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); yield return typeof(SapCollector).GetMethod("RPC_Extract", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static void Prefix(Component __instance, out OutputBatch __state) { __state = new OutputBatch { Machine = __instance, Parent = current, Prefabs = (CanRun(__instance, out var _) ? OutputNames(__instance) : new HashSet<string>()) }; current = __state; } [HarmonyPriority(0)] private static void Postfix(OutputBatch __state) { ProductionStorage.current = __state.Parent; foreach (ItemDrop drop in __state.Drops) { Store(__state.Machine, drop); } } private static void Finalizer(OutputBatch __state) { if (__state != null) { current = __state.Parent; } } } [HarmonyPatch(typeof(ItemDrop), "Awake")] private static class CaptureItem { private static void Postfix(ItemDrop __instance) { if (current != null && (Object)(object)__instance.m_itemData?.m_dropPrefab != (Object)null && current.Prefabs.Contains(((Object)__instance.m_itemData.m_dropPrefab).name)) { current.Drops.Add(__instance); } } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] private static class FlushSmelter { private static void Postfix(Smelter __instance) { if (!CanRun((Component)(object)__instance, out var _)) { return; } int processedQueueSize = __instance.GetProcessedQueueSize(); if (processedQueueSize > 0) { ItemConversion itemConversion = __instance.GetItemConversion(__instance.m_nview.GetZDO().GetString(ZDOVars.s_spawnOre, "")); if (itemConversion != null && CanStoreAll((Component)(object)__instance, itemConversion.m_to, processedQueueSize)) { __instance.SpawnProcessed(); } } } } [HarmonyPatch(typeof(Fermenter), "SlowUpdate")] private static class CollectFermenter { private static void Postfix(Fermenter __instance) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if (CanRun((Component)(object)__instance, out var _) && (int)__instance.GetStatus() == 3) { ItemConversion itemConversion = __instance.GetItemConversion(__instance.GetContent()); if ((Object)(object)itemConversion?.m_to != (Object)null && CanStoreAll((Component)(object)__instance, itemConversion.m_to, (long)itemConversion.m_producedItems * (long)itemConversion.m_to.m_itemData.m_stack)) { __instance.RPC_Tap(0L); } } } } [HarmonyPatch(typeof(SapCollector), "UpdateTick")] private static class CollectSap { private static void Postfix(SapCollector __instance) { if (CanRun((Component)(object)__instance, out var _) && !((Object)(object)Game.instance == (Object)null) && !((Object)(object)__instance.m_spawnItem == (Object)null)) { int num = Game.instance.ScaleDrops(__instance.m_spawnItem.m_itemData, 1); if (CanStoreAll((Component)(object)__instance, __instance.m_spawnItem, (long)__instance.GetLevel() * (long)num, newWorldLevel: false)) { __instance.RPC_Extract(0L); } } } } [HarmonyPatch(typeof(CookingStation), "UpdateCooking")] private static class CollectCooking { private static void Postfix(CookingStation __instance) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (!CanRun((Component)(object)__instance, out var _) || (Object)(object)Game.instance == (Object)null || Game.instance.GetPlayerProfile() == null || __instance.m_recordCrafter) { return; } string text = default(string); float num = default(float); Status val = default(Status); bool flag = default(bool); for (int i = 0; i < __instance.m_slots.Length; i++) { __instance.GetSlot(i, ref text, ref num, ref val, ref flag); if (!(text == "") && __instance.IsItemDone(text)) { ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab(text); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } ItemDrop val2 = (ItemDrop)obj; if (!CanStoreAll((Component)(object)__instance, val2, ((Object)(object)val2 != (Object)null) ? val2.m_itemData.m_stack : 0)) { break; } __instance.RPC_RemoveDoneItem(0L, ((Component)__instance).transform.position + ((Component)__instance).transform.forward, 1); } } } } internal static ConfigEntry<float> Range; [ThreadStatic] private static OutputBatch current; internal static void Initialize(ConfigFile config) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown ProductionOptions.Initialize(config); Range = config.Bind<float>("Production", "ChestRange", 10f, new ConfigDescription("Maximum distance from the producing machine to a chest in metres. Server controlled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); } internal static bool CanRun(Component machine, out long creator) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) creator = 0L; if ((Object)(object)machine == (Object)null || !ProductionOptions.Allows(((Object)machine.gameObject).name)) { return false; } ZNetView component = machine.GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return false; } Piece val = machine.GetComponent<Piece>() ?? machine.GetComponentInParent<Piece>(); creator = (((Object)(object)val != (Object)null) ? val.GetCreator() : 0); if (creator != 0L) { return ChestCrafting.MachineAccess(machine.transform.position, creator); } return false; } internal static bool CanStoreAll(Component machine, ItemDrop prefab, long amount, bool newWorldLevel = true) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || amount <= 0 || !CanRun(machine, out var creator)) { return false; } ItemData val = prefab.m_itemData.Clone(); StackSettings.Apply(val); if (newWorldLevel) { val.m_worldLevel = (byte)Game.m_worldLevel; } foreach (Container item in ChestCrafting.ForMachine(machine.transform.position, creator, Range.Value)) { item.Load(); amount -= ProductionTransfer.Capacity(item.GetInventory(), val); if (amount <= 0) { return true; } } return false; } internal static void Store(Component machine, ItemDrop drop) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)drop == (Object)null || drop.m_itemData == null || drop.m_itemData.m_stack <= 0 || (Object)(object)drop.m_nview == (Object)null || !drop.m_nview.IsValid() || !drop.m_nview.IsOwner() || (Object)(object)ZNetScene.instance == (Object)null || !CanRun(machine, out var creator)) { return; } int stack = drop.m_itemData.m_stack; try { StackSettings.Apply(drop.m_itemData); foreach (Container item in ChestCrafting.ForMachine(machine.transform.position, creator, Range.Value)) { item.Load(); if (ProductionTransfer.Deposit(item.GetInventory(), drop.m_itemData) > 0) { item.Save(); } if (drop.m_itemData.m_stack == 0) { break; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Production storage: output kept at machine: " + ex.Message)); } finally { if (drop.m_itemData.m_stack != stack) { try { drop.Save(); if (drop.m_itemData.m_stack == 0) { ZNetScene.instance.Destroy(((Component)drop).gameObject); } } catch (Exception ex2) { Plugin.Log.LogError((object)("Production storage: cannot save/remove output remainder: " + ex2.Message)); } } } } private static HashSet<string> OutputNames(Component machine) { Smelter val = (Smelter)(object)((machine is Smelter) ? machine : null); IEnumerable<ItemDrop> source; if (val != null) { source = val.m_conversion.Select((ItemConversion c) => c.m_to); } else { Fermenter val2 = (Fermenter)(object)((machine is Fermenter) ? machine : null); if (val2 != null) { source = val2.m_conversion.Select((ItemConversion c) => c.m_to); } else { CookingStation val3 = (CookingStation)(object)((machine is CookingStation) ? machine : null); if (val3 != null) { source = val3.m_conversion.Select((ItemConversion c) => c.m_to).Concat((IEnumerable<ItemDrop>)(object)new ItemDrop[1] { val3.m_overCookedItem }); } else { SapCollector val4 = (SapCollector)(object)((machine is SapCollector) ? machine : null); source = (IEnumerable<ItemDrop>)((val4 == null) ? ((Array)Array.Empty<ItemDrop>()) : ((Array)new ItemDrop[1] { val4.m_spawnItem })); } } } return new HashSet<string>(from i in source where (Object)(object)i != (Object)null select ((Object)((Component)i).gameObject).name, StringComparer.Ordinal); } } internal static class ProductionTransfer { internal static long Capacity(Inventory inventory, ItemData item) { if (item?.m_shared == null || item.m_shared.m_maxStackSize < 1) { return 0L; } long num = (long)Math.Max(0, inventory.GetEmptySlots()) * (long)item.m_shared.m_maxStackSize; if (item.m_shared.m_maxStackSize > 1) { foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_worldLevel == item.m_worldLevel) { num += Math.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack); } } } return num; } internal static int Deposit(Inventory inventory, ItemData source) { int stack = source.m_stack; while (source.m_stack > 0) { int num = (int)Math.Min(Math.Min((long)source.m_stack, (long)source.m_shared.m_maxStackSize), Capacity(inventory, source)); if (num <= 0) { break; } ItemData val = source.Clone(); val.m_stack = num; long num2 = Count(inventory); bool flag = false; try { inventory.AddItem(val); } catch (Exception ex) { flag = true; Plugin.Log.LogWarning((object)("Production storage: inventory callback failed: " + ex.Message)); } int num3 = (int)Math.Max(0L, Math.Min(num, Count(inventory) - num2)); source.m_stack -= num3; if (flag || num3 < num) { break; } } return stack - source.m_stack; } private static long Count(Inventory inventory) { return ((IEnumerable<ItemData>)inventory.GetAllItems()).Sum((Func<ItemData, long>)((ItemData item) => item.m_stack)); } } [HarmonyPatch] internal static class QuestCameraInput { private static IEnumerable<MethodBase> TargetMethods() { yield return typeof(GameCamera).GetMethod("UpdateCamera", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); yield return typeof(GameCamera).GetMethod("UpdateFreeFly", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } internal static float ReadScroll() { if (!QuestUi.Open) { return ZInput.GetMouseScrollWheel(); } return 0f; } internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = typeof(ZInput).GetMethod("GetMouseScrollWheel", BindingFlags.Static | BindingFlags.Public); MethodInfo replacement = typeof(QuestCameraInput).GetMethod("ReadScroll", BindingFlags.Static | BindingFlags.NonPublic); bool found = false; foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Call && object.Equals(instruction.operand, original)) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; found = true; } yield return instruction; } if (!found) { throw new InvalidOperationException("GearAndStorage: camera scroll call not found in this game version."); } } } internal static class QuestFiles { internal static readonly JsonSerializerSettings Json = new JsonSerializerSettings { TypeNameHandling = (TypeNameHandling)0, MaxDepth = 32 }; internal static QuestList Parse(string json) { QuestList questList = JsonConvert.DeserializeObject<QuestList>(json, Json); if (questList?.Quests == null || questList.Quests.Count > 10000) { throw new InvalidDataException("Invalid quest list"); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (QuestDefinition quest in questList.Quests) { if (quest == null || string.IsNullOrWhiteSpace(quest.ID) || !hashSet.Add(quest.ID)) { throw new InvalidDataException("Missing/duplicate quest ID"); } QuestDefinition questDefinition = quest; if (questDefinition.KillReqs == null) { questDefinition.KillReqs = new List<QuestObjective>(); } questDefinition = quest; if (questDefinition.GatherReqs == null) { questDefinition.GatherReqs = new List<QuestObjective>(); } questDefinition = quest; if (questDefinition.RewardItems == null) { questDefinition.RewardItems = new List<QuestReward>(); } questDefinition = quest; if (questDefinition.SkillRewards == null) { questDefinition.SkillRewards = new List<QuestSkill>(); } if (quest.KillReqs.Count + quest.GatherReqs.Count + quest.RewardItems.Count + quest.SkillRewards.Count > 100) { throw new InvalidDataException("Too many objectives/rewards: " + quest.ID); } foreach (QuestObjective item in quest.KillReqs.Concat(quest.GatherReqs)) { if (item == null || string.IsNullOrWhiteSpace(item.Prefab) || item.Amount <= 0 || item.Amount > 1000000) { throw new InvalidDataException("Invalid objective: " + quest.ID); } } if (quest.KillReqs.Select((QuestObjective o) => o.Prefab).Distinct<string>(StringComparer.OrdinalIgnoreCase).Count() != quest.KillReqs.Count || quest.GatherReqs.Select((QuestObjective o) => o.Prefab).Distinct<string>(StringComparer.OrdinalIgnoreCase).Count() != quest.GatherReqs.Count) { throw new InvalidDataException("Duplicate objective: " + quest.ID); } foreach (QuestReward rewardItem in quest.RewardItems) { if (rewardItem == null || string.IsNullOrWhiteSpace(rewardItem.Prefab) || rewardItem.Amount <= 0 || rewardItem.Amount > 100000) { throw new InvalidDataException("Invalid reward: " + quest.ID); } } foreach (QuestSkill skillReward in quest.SkillRewards) { if (skillReward == null || string.IsNullOrWhiteSpace(skillReward.Skill) || float.IsNaN(skillReward.Amount) || float.IsInfinity(skillReward.Amount) || skillReward.Amount <= 0f) { throw new InvalidDataException("Invalid skill reward: " + quest.ID); } } quest.Title = (string.IsNullOrWhiteSpace(quest.Title) ? quest.ID : quest.Title); } return questList; } internal static QuestList Load(string folder, Action<string> warn) { QuestList questList = new QuestList(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (!Directory.Exists(folder)) { return questList; } foreach (string item in Directory.GetFiles(folder, "quest*.json").OrderBy<string, string>((string f) => f, StringComparer.Ordinal)) { try { foreach (QuestDefinition quest in Parse(File.ReadAllText(item)).Quests) { if (!hashSet.Add(quest.ID)) { warn("Duplicate quest ID ignored: " + quest.ID + " in " + Path.GetFileName(item)); continue; } quest.SourceFile = Path.GetFileNameWithoutExtension(item); questList.Quests.Add(quest); } } catch (Exception ex) { warn(Path.GetFileName(item) + ": " + ex.Message); } } return questList; } } internal static class QuestNetwork { [HarmonyPatch(typeof(Character), "OnDeath")] private static class Death { private static void Prefix(Character __instance) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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) if (!QuestSystem.Enabled.Value || __instance is Player || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner()) { return; } HitData lastHit = __instance.m_lastHit; Character obj = ((lastHit != null) ? lastHit.GetAttacker() : null); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)(object)val == (Object)null) { return; } ZDO zDO = __instance.m_nview.GetZDO(); string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); if (ServerSettings.IsServer) { Relay(zDO.m_uid, ((Character)val).GetZDOID(), prefabName, ZNet.GetUID()); return; } ZPackage val2 = new ZPackage(); val2.Write(zDO.m_uid); val2.Write(((Character)val).GetZDOID()); val2.Write(prefabName); ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC != null) { serverRPC.Invoke("GearAndStorage.QuestKill", new object[1] { val2 }); } } } private const string Catalog = "GearAndStorage.Quests"; private const string Kill = "GearAndStorage.QuestKill"; private const string Credit = "GearAndStorage.QuestCredit"; private static string stamp; private static string catalog = "{\"Quests\":[]}"; private static float nextScan; private static readonly HashSet<ZDOID> kills = new HashSet<ZDOID>(); internal static void Reset() { stamp = null; nextScan = 0f; catalog = "{\"Quests\":[]}"; kills.Clear(); QuestSystem.Reset(); } internal static void Tick() { if (!ServerSettings.IsServer || Time.realtimeSinceStartup < nextScan) { return; } nextScan = Time.realtimeSinceStartup + 5f; try { string text = (Directory.Exists(QuestSystem.Folder) ? string.Join("|", from f in Directory.GetFiles(QuestSystem.Folder, "quest*.json").OrderBy<string, string>((string f) => f, StringComparer.Ordinal) select f + File.GetLastWriteTimeUtc(f).Ticks + new FileInfo(f).Length) : "missing"); if (text == stamp) { return; } stamp = text; QuestList questList = QuestFiles.Load(QuestSystem.Folder, delegate(string s) { Plugin.Log.LogWarning((object)s); }); catalog = JsonConvert.SerializeObject((object)questList, QuestFiles.Json); QuestSystem.SetDefinitions(questList); Plugin.Log.LogInfo((object)("Loaded " + questList.Quests.Count + " GearAndStorage quests from " + QuestSystem.Folder)); foreach (ZNetPeer item in ServerSettings.Clients()) { item.m_rpc.Invoke("GearAndStorage.Quests", new object[1] { catalog }); } } catch (Exception ex) { Plugin.Log.LogError((object)("Quest catalog: " + ex.Message)); } } internal static void Send(ZRpc rpc) { nextScan = 0f; Tick(); rpc.Invoke("GearAndStorage.Quests", new object[1] { catalog }); } internal static void Register(ZRpc rpc) { rpc.Register<string>("GearAndStorage.Quests", (Action<ZRpc, string>)delegate(ZRpc sender, string json) { if (!ServerSettings.FromServer(sender)) { return; } try { if (json.Length > 4000000) { throw new InvalidDataException("Quest catalog too large"); } QuestSystem.SetDefinitions(QuestFiles.Parse(json)); } catch (Exception ex) { Plugin.Log.LogError((object)("Server quest catalog rejected: " + ex.Message)); } }); rpc.Register<ZPackage>("GearAndStorage.QuestKill", (Action<ZRpc, ZPackage>)delegate(ZRpc sender, ZPackage pkg) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (ServerSettings.IsServer) { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null && peer.IsReady()) { try { Relay(pkg.ReadZDOID(), pkg.ReadZDOID(), pkg.ReadString(), peer.m_uid); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Quest kill packet: " + ex.Message)); } } } }); rpc.Register<string>("GearAndStorage.QuestCredit", (Action<ZRpc, string>)delegate(ZRpc sender, string prefab) { if (ServerSettings.FromServer(sender)) { QuestSystem.Credit(prefab, 1, kill: true); } }); } private static void Relay(ZDOID victim, ZDOID attacker, string prefab, long owner) { //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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_0072: Unknown result type (might be due to invalid IL or missing references) if (!QuestSystem.Enabled.Value || kills.Contains(victim)) { return; } ZDO zDO = ZDOMan.instance.GetZDO(victim); if (zDO == null || zDO.GetOwner() != owner || zDO.GetPrefab() != StringExtensionMethods.GetStableHashCode(prefab)) { return; } kills.Add(victim); if ((Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).GetZDOID() == attacker) { QuestSystem.Credit(prefab, 1, kill: true); return; } ZNetPeer? obj = ServerSettings.Clients().FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_characterID == attacker)); if (obj != null) { obj.m_rpc.Invoke("GearAndStorage.QuestCredit", new object[1] { prefab }); } } } internal static class QuestSystem { [HarmonyPatch] private static class Gather { private sealed class Scope { internal Dictionary<string, int> Before; } private static IEnumerable<MethodBase> TargetMethods() { return from m in typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "AddItem" select m; } private static void Prefix(Inventory __instance, out Scope __state) { __state = null; if (Enabled.Value && Book?.Active != null && !((Object)(object)loadedPlayer == (Object)null) && !loadedPlayer.m_isLoading && __instance == ((Humanoid)loadedPlayer).GetInventory()) { __state = new Scope(); if (gatherDepth++ == 0) { __state.Before = Counts(__instance); } } } private static void Finalizer(Inventory __instance, Scope __state) { if (__state == null) { return; } gatherDepth--; if (__state.Before == null) { return; } foreach (KeyValuePair<string, int> item in Counts(__instance)) { __state.Before.TryGetValue(item.Key, out var value); if (item.Value > value) { Credit(item.Key, item.Value - value, kill: false); } } } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class Spawn { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && Enabled.Value) { Load(__instance); } } } [HarmonyPatch(typeof(Player), "Save")] private static class Persist { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)loadedPlayer) { Save(); } } } private const string ProgressKey = "GearAndStorage.questProgress"; internal static ConfigEntry<bool> Enabled; internal static QuestBook Book; internal static string Notice = ""; internal static QuestList Definitions = new QuestList(); private static Player loadedPlayer; private static float rewardCheck; private static int gatherDepth; internal static string Folder => Plugin.Data.Quests; internal static void Initialize(ConfigFile config) { Enabled = config.Bind<bool>("Quests", "Enabled", true, "Read quest JSON files from GearAndStorage/Quests. One accepted quest, cumulative gather objectives and automatic rewards."); } internal static void SetDefinitions(QuestList list) { Definitions = list; if (Book != null) { Book = new QuestBook(list.Quests, Book.Progress); } } internal static void Reset() { Book = null; loadedPlayer = null; Definitions = new QuestList(); Notice = ""; QuestUi.Close(); } private static void Load(Player player) { if ((Object)(object)loadedPlayer == (Object)(object)player) { return; } loadedPlayer = player; Notice = ""; try { QuestProgress questProgress = null; if (player.m_customData.TryGetValue("GearAndStorage.questProgress", out var value)) { questProgress = JsonConvert.DeserializeObject<QuestProgress>(value, QuestFiles.Json); } else { string text = player.GetPlayerName(); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } string path = Path.Combine(Plugin.Data.Progress, "progress_" + text + ".json"); if (File.Exists(path)) { questProgress = JsonConvert.DeserializeObject<QuestProgress>(File.ReadAllText(path), QuestFiles.Json); Plugin.Log.LogInfo((object)("Imported quest progress for " + player.GetPlayerName() + " from GearAndStorage/Progress.")); } } if (questProgress != null && questProgress.AcceptedQuestIDs?.Count > 1) { Notice = "Převzat jeden aktivní quest; ostatní lze přijmout později."; } Book = new QuestBook(Definitions.Quests, questProgress); Save(); } catch (Exception ex) { Book = null; Notice = "Postup questů nelze načíst. Podrobnosti jsou v logu."; Plugin.Log.LogError((object)("Quest progress not modified: " + ex.Message)); } } internal static void Save() { if ((Object)(object)loadedPlayer != (Object)null && Book != null) { loadedPlayer.m_customData["GearAndStorage.questProgress"] = JsonConvert.SerializeObject((object)Book.Progress, QuestFiles.Json); } } internal static void Credit(string prefab, int amount, bool kill) { if (Enabled.Value && Book != null && Book.Credit(prefab, amount, kill)) { Save(); } } internal static void Accept(QuestDefinition q) { if (Book == null || !Enabled.Value) { return; } string text = RewardProblem(q); if (text == null) { foreach (QuestObjective gatherReq in q.GatherReqs) { if ((Object)(object)ObjectDB.instance.GetItemPrefab(gatherReq.Prefab) == (Object)null) { text = "Chybí předmět úkolu: " + gatherReq.Prefab; break; } } } if (text == null) { foreach (QuestObjective killReq in q.KillReqs) { ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab = instance.GetPrefab(killReq.Prefab); obj = ((prefab != null) ? prefab.GetComponent<Character>() : null); } if ((Object)obj == (Object)null) { text = "Chybí tvor úkolu: " + killReq.Prefab; break; } } } if (text != null) { Notice = text; } else if (Book.Accept(q.ID)) { Notice = "Přijato: " + q.Title; Save(); } } internal static void Abandon() { if (Book != null) { Book.Abandon(); Notice = "Quest zrušen."; Save(); } } internal static void Tick(Player player) { if (!Enabled.Value || (Object)(object)player == (Object)null || player.m_isLoading || ((Character)player).IsDead()) { return; } Load(player); if (Book != null && Book.Ready && !(Time.realtimeSinceStartup < rewardCheck)) { rewardCheck = Time.realtimeSinceStartup + 1f; if (!((Object)(object)InventoryGui.instance != (Object)null) || InventoryGui.instance.m_dragItem == null) { Reward(); } } } internal static string RewardProblem(QuestDefinition q) { //IL_00ae: 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)ObjectDB.instance == (Object)null) { return "Předměty ještě nejsou načtené."; } foreach (QuestReward rewardItem in q.RewardItems) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(rewardItem.Prefab); if ((Object)(object)((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null) == (Object)null) { return "Chybí předmět odměny: " + rewardItem.Prefab; } } foreach (QuestSkill skillReward in q.SkillRewards) { if (!Enum.TryParse<SkillType>(skillReward.Skill, ignoreCase: true, out SkillType result) || !Enum.IsDefined(typeof(SkillType), result) || (int)result == 0) { return "Neznámá dovednost: " + skillReward.Skill; } } return null; } private static void Reward() { //IL_01fc: 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_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) QuestDefinition active = Book.Active; string text = RewardProblem(active); if (text != null) { Notice = text; return; } Inventory inventory = ((Humanoid)loadedPlayer).GetInventory(); List<Vector2i> list = new List<Vector2i>(); PlayerSlots.State state = PlayerSlots.Track(loadedPlayer); for (int i = 0; i < state.NormalRows; i++) { for (int j = 0; j < inventory.GetWidth(); j++) { if (inventory.GetItemAt(j, i) == null) { list.Add(new Vector2i(j, i)); } } } List<ItemData> list2 = new List<ItemData>(); foreach (QuestReward rewardItem in active.RewardItems) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(rewardItem.Prefab); ItemData itemData = itemPrefab.GetComponent<ItemDrop>().m_itemData; int num = rewardItem.Amount; while (num > 0) { ItemData val = itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_stack = Math.Min(num, Math.Max(1, val.m_shared.m_maxStackSize)); val.m_worldLevel = Game.m_worldLevel; val.m_durability = val.GetMaxDurability(); num -= val.m_stack; list2.Add(val); if (list2.Count > list.Count) { Notice = "Splněno — uvolni místo v běžném inventáři pro odměnu."; return; } } } for (int k = 0; k < list2.Count; k++) { list2[k].m_gridPos = list[k]; inventory.m_inventory.Add(list2[k]); } Book.Complete(); Save(); inventory.Changed(false, false); foreach (QuestSkill skillReward in active.SkillRewards) { try { ((Character)loadedPlayer).RaiseSkill((SkillType)Enum.Parse(typeof(SkillType), skillReward.Skill, ignoreCase: true), skillReward.Amount); } catch (Exception ex) { Plugin.Log.LogError((object)("Quest skill reward failed: " + ex.Message)); } } Notice = "Splněno: " + active.Title; ((Character)loadedPlayer).Message((MessageType)2, Notice + " — odměna přidána.", 0, (Sprite)null, false); } private static Dictionary<string, int> Counts(Inventory inventory) { return (from i in inventory.GetAllItems() where (Object)(object)i.m_dropPrefab != (Object)null select i).GroupBy<ItemData, string>((ItemData i) => ((Object)i.m_dropPrefab).name, StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, ItemData>, string, int>((IGrouping<string, ItemData> g) => g.Key, (IGrouping<string, ItemData> g) => g.Sum((ItemData i) => i.m_stack), StringComparer.OrdinalIgnoreCase); } } internal static class QuestUi { [HarmonyPatch(typeof(Player), "TakeInput")] private static class BlockInput { private static void Postfix(ref bool __result) { if (Open) { __result = false; } } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] private static class BlockController { private static void Postfix(ref bool __result) { if (Open) { __result = false; } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] private static class Mouse { private static bool Prefix() { if (!Open) { return true; } ZCursor.LockState = (CursorLockMode)0; ZCursor.Show(); return false; } } [HarmonyPatch(typeof(Menu), "Update")] private static class Escape { private static bool Prefix() { return !Open; } } internal static bool Open; private static ConfigEntry<KeyboardShortcut> key; private static ConfigEntry<Vector2> offset; private static ConfigEntry<float> width; private static Vector2 scroll; private static Vector2 hudScroll; private static Vector2 completedScroll; private static string search = ""; private static int page; private static bool editingSearch; private static List<QuestDefinition> navigationSource; private static List<QuestLine> lines = new List<QuestLine>(); private static string selectedLine; private static GUIStyle body; private static GUIStyle title; private static GUIStyle small; private static GUIStyle button; private static GUIStyle panel; internal static void Initialize(ConfigFile config) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown key = config.Bind<KeyboardShortcut>("Controls", "QuestJournal", new KeyboardShortcut((KeyCode)108, Array.Empty<KeyCode>()), "Open the quest journal."); offset = config.Bind<Vector2>("QuestsUI", "Offset", Vector2.zero, "Quest tracker offset from its position to the left of the minimap: X right, Y down, in screen pixels."); width = config.Bind<float>("QuestsUI", "Width", 280f, new ConfigDescription("Compact quest tracker width in pixels. Height follows its content.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(220f, 600f), Array.Empty<object>())); } internal static void Close() { if (Open) { Open = false; if ((Object)(object)GameCamera.instance != (Object)null) { GameCamera.instance.UpdateMouseCapture(); } } } internal static void Tick(bool canOpen) { //IL_004b: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //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) if (!QuestSystem.Enabled.Value) { Close(); return; } KeyboardShortcut value; if (Open) { if (Input.GetKeyDown((KeyCode)27)) { goto IL_003d; } if (!editingSearch) { value = key.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { goto IL_003d; } } } if (canOpen) { value = key.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !Open) { Open = true; scroll = Vector2.zero; editingSearch = false; FocusActive(); } } if (Open) { ZCursor.LockState = (CursorLockMode)0; ZCursor.Show(); } return; IL_003d: Close(); } private static void Styles() { //IL_0012: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0041: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_010c: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0133: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) if (body == null) { body = new GUIStyle(GUI.skin.label) { fontSize = 15, wordWrap = true, richText = false, padding = new RectOffset(4, 4, 3, 3) }; body.normal.textColor = new Color(0.91f, 0.93f, 0.95f); title = new GUIStyle(body) { fontSize = 19, fontStyle = (FontStyle)1 }; title.normal.textColor = new Color(1f, 0.78f, 0.36f); small = new GUIStyle(body) { fontSize = 13 }; small.normal.textColor = new Color(0.71f, 0.77f, 0.81f); button = new GUIStyle(GUI.skin.button) { fontSize = 15, padding = new RectOffset(10, 10, 8, 8) }; panel = new GUIStyle(GUI.skin.box) { padding = new RectOffset(14, 14, 12, 12) }; Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, new Color(0.045f, 0.055f, 0.07f, 0.93f)); val.Apply(); panel.normal.background = val; } } internal static string ItemName(string prefab) { ObjectDB instance = ObjectDB.instance; object obj = ((instance != null) ? instance.GetItemPrefab(prefab) : null); if (obj == null) { ZNetScene instance2 = ZNetScene.instance; obj = ((instance2 != null) ? instance2.GetPrefab(prefab) : null); } GameObject val = (GameObject)obj; string text = ((val == null) ? null : val.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? ((val == null) ? null : val.GetComponent<Character>()?.m_name) ?? prefab; if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } private static string Objectives(QuestDefinition q, bool progress) { StringBuilder stringBuilder = new StringBuilder(); bool[] array = new bool[2] { true, false }; foreach (bool flag in array) { foreach (QuestObjective item in flag ? q.KillReqs : q.GatherReqs) { int value = (progress ? QuestSystem.Book.Count(q, item, flag) : 0); stringBuilder.Append(flag ? "Zabít: " : "Získat: ").Append(ItemName(item.Prefab)).Append(" "); if (progress) { stringBuilder.Append(value).Append(" / "); } stringBuilder.Append(item.Amount).AppendLine(); } } return stringBuilder.ToString().TrimEnd(); } private static string Rewards(QuestDefinition q) { StringBuilder stringBuilder = new StringBuilder("Odměna: "); stringBuilder.Append(string.Join(", ", q.RewardItems.Select((QuestReward r) => r.Amount + "× " + ItemName(r.Prefab)).Concat(q.SkillRewards.Select((QuestSkill s) => s.Skill + " +" + s.Amount + " XP")))); if (q.RewardItems.Count == 0 && q.SkillRewards.Count == 0) { stringBuilder.Append("žádná"); } return stringBuilder.ToString(); } internal static void Draw() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) if (!QuestSystem.Enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead() || (Object)(object)Hud.instance == (Object)null) { return; } Styles(); if (Open) { Journal(); } else if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !((Object)(object)Minimap.instance == (Object)null) && Minimap.instance.m_smallRoot.activeInHierarchy) { Rect val = SlotUi.ScreenRect((RectTransform)Minimap.instance.m_smallRoot.transform); float num = Math.Min(width.Value, Screen.width - 16); float num2 = Mathf.Clamp(((Rect)(ref val)).xMin - num - 12f + offset.Value.x, 8f, (float)Screen.width - num - 8f); float num3 = Mathf.Clamp((float)Screen.height - ((Rect)(ref val)).yMax + offset.Value.y, 8f, (float)(Screen.height - 100)); List<(string, GUIStyle)> list = new List<(string, GUIStyle)> { ("AKTIVNÍ QUEST", small) }; QuestDefinition questDefinition = QuestSystem.Book?.Active; if (questDefinition == null) { list.Add(("Žádný aktivní quest. Deník: " + ((object)key.Value/*cast due to .constrained prefix*/).ToString(), body)); } else { list.Add((questDefinition.Title, title)); list.Add((Objectives(questDefinition, progress: true), body)); list.Add((Rewards(questDefinition), small)); } if (!string.IsNullOrEmpty(QuestSystem.Notice) && QuestSystem.Notice != "Přijato: " + questDefinition?.Title) { list.Add((QuestSystem.Notice, small)); } float innerWidth = num - (float)panel.padding.horizontal - 16f; float[] array = list.Select<(string, GUIStyle), float>(((string Text, GUIStyle Style) c) => c.Style.CalcHeight(new GUIContent(c.Text), innerWidth)).ToArray(); float num4 = array.Sum() + (float)(3 * (list.Count - 1)); float num5 = Mathf.Min(num4 + (float)panel.padding.vertical, (float)Screen.height - num3 - 8f); GUI.Box(new Rect(num2, num3, num, num5), GUIContent.none, panel); hudScroll = GUI.BeginScrollView(new Rect(num2 + (float)panel.padding.left, num3 + (float)panel.padding.top, num - (float)panel.padding.horizontal, num5 - (float)panel.padding.vertical), hudScroll, new Rect(0f, 0f, innerWidth, num4)); float num6 = 0f; for (int num7 = 0; num7 < list.Count; num7++) { GUI.Label(new Rect(0f, num6, innerWidth, array[num7]), list[num7].Item1, list[num7].Item2); num6 += array[num7] + 3f; } GUI.EndScrollView(); } } private static void UpdateNavigation() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) List<QuestDefinition> list = QuestSystem.Book?.Definitions; if (list != navigationSource) { navigationSource = list; lines = ((list == null) ? new List<QuestLine>() : QuestNavigation.ForJournal(list)); if (!lines.Any((QuestLine l) => l.ID == selectedLine)) { selectedLine = lines.FirstOrDefault()?.ID; } page = 0; scroll = (completedScroll = Vector2.zero); } } private static void SelectLine(QuestLine line) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0026: Unknown result type (might be due to invalid IL or missing references) selectedLine = line.ID; page = 0; search = ""; scroll = (completedScroll = Vector2.zero); } private static void FocusActive() { UpdateNavigation(); QuestDefinition active = QuestSystem.Book?.Active; if (active != null) { QuestLine questLine = lines.FirstOrDefault((QuestLine l) => l.Quests.Contains(active)); if (questLine != null) { SelectLine(questLine); page = new QuestLineProgress(questLine, QuestSystem.Book.Progress).PageOf(active); } } } private static