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 list = new List(); list.Add(__instance); list.AddRange(from c in sources where Accessible(c, Player.m_localPlayer) select c.m_inventory); List> list2 = new List>(); 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(item2, allItem)); } } } int[] array = PaymentPlanner.Allocate(list2.Select((Tuple entry) => entry.Item2.m_stack).ToArray(), amount); if (array == null) { throw new InvalidOperationException("GearAndStorage: crafting materials changed during native crafting; payment aborted."); } HashSet hashSet = new HashSet(); 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 Enabled; private static ConfigEntry Radius; private static readonly HashSet Containers = new HashSet(); private static List nearby = new List(); private static float nextScan; private static Vector3 scanPosition; private static Player scanningPlayer; private static int queryDepth; private static int craftingDepth; private static List sources; internal static IEnumerable 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("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("Crafting", "ChestRange", 20f, new ConfigDescription("Maximum distance from the player in metres.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); } 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() == (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 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 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 hashSet = new HashSet(); 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 States = new ConditionalWeakTable(); 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() ?? ((Component)__instance).GetComponentInParent(); 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 Enabled; internal static void Initialize(ConfigFile config) { Enabled = config.Bind("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() == (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 States = new ConditionalWeakTable(); 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)((Humanoid)player).GetInventory().GetAllItems()).FirstOrDefault((Func)((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 allItems = inventory.GetAllItems(); List list = new List(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 Rows; internal static ConfigEntry QuickCount; internal static ConfigEntry ProtectSlots; internal static ConfigEntry AnchorAboveGuardianPower; internal static ConfigEntry HudPosition; internal static ConfigEntry PanelPosition; internal static ConfigEntry GuardianPowerOffset; internal static readonly ConfigEntry[] Keys = new ConfigEntry[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), "k__BackingField") ?? throw new MissingFieldException("BepInEx BaseUnityPlugin.Config backing field")).SetValue(this, value); Rows = ((BaseUnityPlugin)this).Config.Bind("Player", "InventoryRows", 6, new ConfigDescription("Minimum normal inventory rows; purchased rows and recovery space are preserved.", (AcceptableValueBase)(object)new AcceptableValueRange(4, 9), Array.Empty())); QuickCount = ((BaseUnityPlugin)this).Config.Bind("QuickSlots", "Count", 3, new ConfigDescription("Quick slots. Removed slots return items to normal inventory.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 6), Array.Empty())); HudPosition = ((BaseUnityPlugin)this).Config.Bind("QuickSlots", "HudPosition", new Vector2(20f, 160f), "HUD position in pixels from the lower left corner."); AnchorAboveGuardianPower = ((BaseUnityPlugin)this).Config.Bind("QuickSlots", "AnchorAboveGuardianPower", true, "Center the quick bar above the guardian power (Eikthyr) icon. Disable to use HudPosition."); GuardianPowerOffset = ((BaseUnityPlugin)this).Config.Bind("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("Equipment", "PanelPosition", new Vector2(12f, -20f), "Slot panel offset from the upper right corner of the player inventory."); ProtectSlots = ((BaseUnityPlugin)this).Config.Bind("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("QuickSlots", "Hotkey" + (i + 1), (i < 3) ? new KeyboardShortcut(array2[i], (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }) : new KeyboardShortcut(array2[i], Array.Empty()), "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 val = default(ConfigEntry); if ((Object)(object)instance == (Object)null || !instance.Config.TryGetEntry(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 Manual; internal ConfigEntry Items; internal ConfigEntry 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 Rules = new List(); private static ConfigEntry AutoUnlock; internal static void Initialize(ConfigFile config) { AutoUnlock = config.Bind("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("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("Portals.Items", biome, items, "Comma-separated item prefab names assigned to this biome. Unknown items remain blocked; extend for modded items."), Trophy = config.Bind("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> Stations = new Dictionary>(StringComparer.Ordinal); internal static readonly Dictionary Keys = new Dictionary(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 val = config.Bind("Production", "StoreOutputInChests", false, (ConfigDescription)null); bool value = val.Value; foreach (string value2 in Keys.Values) { Stations[value2] = config.Bind("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 Drops = new HashSet(); internal HashSet Prefabs; } [HarmonyPatch] private static class CaptureOutput { private static IEnumerable 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()) }; 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() : 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 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("Production", "ChestRange", 10f, new ConfigDescription("Maximum distance from the producing machine to a chest in metres. Server controlled.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); } 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(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return false; } Piece val = machine.GetComponent() ?? machine.GetComponentInParent(); 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 OutputNames(Component machine) { Smelter val = (Smelter)(object)((machine is Smelter) ? machine : null); IEnumerable 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)(object)new ItemDrop[1] { val3.m_overCookedItem }); } else { SapCollector val4 = (SapCollector)(object)((machine is SapCollector) ? machine : null); source = (IEnumerable)((val4 == null) ? ((Array)Array.Empty()) : ((Array)new ItemDrop[1] { val4.m_spawnItem })); } } } return new HashSet(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)inventory.GetAllItems()).Sum((Func)((ItemData item) => item.m_stack)); } } [HarmonyPatch] internal static class QuestCameraInput { private static IEnumerable 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 Transpiler(IEnumerable 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(json, Json); if (questList?.Quests == null || questList.Quests.Count > 10000) { throw new InvalidDataException("Invalid quest list"); } HashSet hashSet = new HashSet(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(); } questDefinition = quest; if (questDefinition.GatherReqs == null) { questDefinition.GatherReqs = new List(); } questDefinition = quest; if (questDefinition.RewardItems == null) { questDefinition.RewardItems = new List(); } questDefinition = quest; if (questDefinition.SkillRewards == null) { questDefinition.SkillRewards = new List(); } 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(StringComparer.OrdinalIgnoreCase).Count() != quest.KillReqs.Count || quest.GatherReqs.Select((QuestObjective o) => o.Prefab).Distinct(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 warn) { QuestList questList = new QuestList(); HashSet hashSet = new HashSet(StringComparer.Ordinal); if (!Directory.Exists(folder)) { return questList; } foreach (string item in Directory.GetFiles(folder, "quest*.json").OrderBy((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 kills = new HashSet(); 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 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("GearAndStorage.Quests", (Action)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("GearAndStorage.QuestKill", (Action)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("GearAndStorage.QuestCredit", (Action)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 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 Before; } private static IEnumerable 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 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 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("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(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(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() : 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() : null) == (Object)null) { return "Chybí předmět odměny: " + rewardItem.Prefab; } } foreach (QuestSkill skillReward in q.SkillRewards) { if (!Enum.TryParse(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 list = new List(); 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 list2 = new List(); foreach (QuestReward rewardItem in active.RewardItems) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(rewardItem.Prefab); ItemData itemData = itemPrefab.GetComponent().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 Counts(Inventory inventory) { return (from i in inventory.GetAllItems() where (Object)(object)i.m_dropPrefab != (Object)null select i).GroupBy((ItemData i) => ((Object)i.m_dropPrefab).name, StringComparer.OrdinalIgnoreCase).ToDictionary, string, int>((IGrouping g) => g.Key, (IGrouping 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 key; private static ConfigEntry offset; private static ConfigEntry 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 navigationSource; private static List lines = new List(); 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("Controls", "QuestJournal", new KeyboardShortcut((KeyCode)108, Array.Empty()), "Open the quest journal."); offset = config.Bind("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("QuestsUI", "Width", 280f, new ConfigDescription("Compact quest tracker width in pixels. Height follows its content.", (AcceptableValueBase)(object)new AcceptableValueRange(220f, 600f), Array.Empty())); } 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()?.m_itemData.m_shared.m_name) ?? ((val == null) ? null : val.GetComponent()?.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 list = QuestSystem.Book?.Definitions; if (list != navigationSource) { navigationSource = list; lines = ((list == null) ? new List() : 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 void Journal() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_044b: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Unknown result type (might be due to invalid IL or missing references) //IL_051e: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Unknown result type (might be due to invalid IL or missing references) //IL_04fa: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) QuestBook book = QuestSystem.Book; UpdateNavigation(); QuestLine questLine = lines.FirstOrDefault((QuestLine l) => l.ID == selectedLine); QuestLineProgress questLineProgress = new QuestLineProgress(questLine, book?.Progress); float num = Mathf.Min(1040, Screen.width - 32); float num2 = Mathf.Min(760, Screen.height - 32); float num3 = Mathf.Min(248f, num * 0.24f); float num4 = 12f; float num5 = num - num3 - num4; float num6 = ((float)Screen.width - num) / 2f; float num7 = ((float)Screen.height - num2) / 2f; CompletedPanel(new Rect(num6, num7, num3, num2), questLine, questLineProgress, book == null); GUILayout.BeginArea(new Rect(num6 + num3 + num4, num7, num5, num2), panel); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("DENÍK QUESTŮ", title, Array.Empty()); if (GUILayout.Button("Zavřít [" + ((object)key.Value/*cast due to .constrained prefix*/).ToString() + "]", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(155f, num5 * 0.36f)) })) { Close(); } GUILayout.EndHorizontal(); GUILayout.Label("Současně můžeš mít přijatý jeden quest. Sběr se počítá od přijetí; předměty se neodevzdávají.", small, Array.Empty()); if (questLine != null) { int num8 = lines.IndexOf(questLine); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = num8 > 0; if (GUILayout.Button("← Předchozí linka", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(160f, (num5 - 148f) / 2f)) })) { SelectLine(lines[num8 - 1]); GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.Label("LINKA " + (num8 + 1) + " / " + lines.Count, small, Array.Empty()); GUI.enabled = num8 + 1 < lines.Count; if (GUILayout.Button("Další linka →", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(160f, (num5 - 148f) / 2f)) })) { SelectLine(lines[num8 + 1]); GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.EndHorizontal(); questLine = lines.First((QuestLine l) => l.ID == selectedLine); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(questLine.Title, title, Array.Empty()); GUI.enabled = book.Active != null; if (GUILayout.Button("Aktivní quest", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { FocusActive(); GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.EndHorizontal(); } GUILayout.Label("Hledat v nedokončených úkolech této linky:", small, Array.Empty()); GUI.SetNextControlName("GearAndStorage.QuestSearch"); string text = GUILayout.TextField(search ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); if (text != search) { page = 0; scroll = Vector2.zero; search = text; } editingSearch = GUI.GetNameOfFocusedControl() == "GearAndStorage.QuestSearch"; if (!string.IsNullOrEmpty(QuestSystem.Notice)) { GUILayout.Label(QuestSystem.Notice, small, Array.Empty()); } QuestDefinition[] array = questLineProgress.Matches(search); int num9 = Math.Max(1, (array.Length + 8 - 1) / 8); page = Math.Min(page, num9 - 1); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = page > 0; if (GUILayout.Button("← Strana", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { page--; scroll = Vector2.zero; GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.Label(page + 1 + " / " + num9 + " · " + array.Length + " questů", small, Array.Empty()); GUI.enabled = page + 1 < num9; if (GUILayout.Button("Strana →", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { page++; scroll = Vector2.zero; GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.EndHorizontal(); scroll = GUILayout.BeginScrollView(scroll, Array.Empty()); if (book == null) { GUILayout.Label("Načítání postupu…", body, Array.Empty()); } else { if (book.Progress.AcceptedQuestIDs.Count > 0 && book.Active == null) { GUILayout.Label("Aktivní quest chybí v serverových souborech.", body, Array.Empty()); if (GUILayout.Button("Zrušit nedostupný quest", button, Array.Empty())) { QuestSystem.Abandon(); } } foreach (QuestDefinition q in array.Skip(page * 8).Take(8)) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.Label(questLine.Quests.IndexOf(q) + 1 + ". " + q.Title, title, Array.Empty()); GUILayout.Label(q.Goal ?? "", body, Array.Empty()); GUILayout.Label(Objectives(q, book.Active == q), body, Array.Empty()); GUILayout.Label(Rewards(q), small, Array.Empty()); if (book.Active == q) { if (GUILayout.Button("Zrušit quest", button, Array.Empty())) { QuestSystem.Abandon(); GUIUtility.ExitGUI(); } } else if (!book.Available(q)) { GUILayout.Label("Nejprve dokonči: " + (book.Definitions.FirstOrDefault((QuestDefinition p) => p.ID == q.PreReqID)?.Title ?? q.PreReqID), small, Array.Empty()); } else { GUI.enabled = book.Progress.AcceptedQuestIDs.Count == 0; if (GUILayout.Button("Přijmout", button, Array.Empty())) { QuestSystem.Accept(q); GUIUtility.ExitGUI(); } GUI.enabled = true; } GUILayout.EndVertical(); GUILayout.Space(8f); } if (book.Definitions.Count == 0) { GUILayout.Label("Server nemá quest*.json v BepInEx/config/GearAndStorage/Quests.", body, Array.Empty()); } else if (questLine != null && questLineProgress.Pending.Count == 0) { GUILayout.Label("Všechny úkoly této linky jsou dokončené.", body, Array.Empty()); } else if (array.Length == 0) { GUILayout.Label("Hledání neodpovídá žádný nedokončený úkol.", body, Array.Empty()); } } GUILayout.EndScrollView(); GUILayout.EndArea(); } private static void CompletedPanel(Rect rect, QuestLine line, QuestLineProgress progress, bool loading) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginArea(rect, panel); GUILayout.Label("DOKONČENO", title, Array.Empty()); if (line != null) { GUILayout.Label(line.Title, small, Array.Empty()); } GUILayout.Space(8f); completedScroll = GUILayout.BeginScrollView(completedScroll, Array.Empty()); if (loading) { GUILayout.Label("Načítání postupu…", small, Array.Empty()); } else if (progress.Completed.Count == 0) { GUILayout.Label("Zatím žádné dokončené úkoly.", small, Array.Empty()); } else { foreach (QuestDefinition item in progress.Completed) { GUILayout.Label(item.Title, body, Array.Empty()); GUILayout.Space(6f); } } GUILayout.EndScrollView(); GUILayout.EndArea(); } } internal static class QuickSlotInput { [HarmonyPatch(typeof(ZInput), "GetButtonDown")] private static class NativeActions { private static bool Prefix(string name, ref bool __result) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((name != "Sit" && name != "ToggleWalk") || !Plugin.CanUseQuickSlots || ZInput.instance == null || !ZInput.instance.m_buttons.TryGetValue(name, out var value)) { return true; } for (int i = 0; i < Plugin.QuickCount.Value; i++) { if (SharesPressedKey(Plugin.Keys[i].Value, value)) { __result = false; return false; } } return true; } } internal static bool IsDown(KeyboardShortcut shortcut) { //IL_0002: 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_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Read(((KeyboardShortcut)(ref shortcut)).MainKey, down: true)) { return ModifiersHeld(shortcut); } return false; } private static bool ModifiersHeld(KeyboardShortcut shortcut) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!Read(modifier, down: false)) { return false; } } return true; } private unsafe static KeyControl Letter(KeyCode key) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; if (current == null || (int)key < 97 || (int)key > 122) { return null; } KeyControl obj = current.FindKeyOnCurrentKeyboardLayout(((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString()); if (obj == null) { if (!Enum.TryParse(((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(), out Key result)) { return null; } obj = current[result]; } return obj; } private static bool Read(KeyCode key, bool down) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if ((int)key == 0) { return false; } KeyControl val = Letter(key); if (val != null) { if (!down) { return ((ButtonControl)val).isPressed; } return ((ButtonControl)val).wasPressedThisFrame; } if (!down) { return ZInput.GetKey(key, false); } return ZInput.GetKeyDown(key, false); } internal static bool SharesPressedKey(KeyboardShortcut shortcut, ButtonDef button) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) KeyControl val = Letter(((KeyboardShortcut)(ref shortcut)).MainKey); if (val == null || !((ButtonControl)val).isPressed || !ModifiersHeld(shortcut)) { return false; } Enumerator enumerator = button.ButtonAction.controls.GetEnumerator(); try { while (enumerator.MoveNext()) { if ((object)enumerator.Current == val) { return true; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } return false; } } internal static class QuickStack { private sealed class Request { internal float Deadline; internal bool Granted; internal bool Local; internal uint Revision; internal long Owner; internal int Id; } [HarmonyPatch(typeof(Container), "Awake")] private static class Register { private static void Postfix(Container __instance) { if (!((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid()) { Container c = __instance; c.m_nview.Register("GearAndStorage.StackRequest", (Action)delegate(long sender, long playerId, int id) { Grant(c, sender, playerId, id); }); c.m_nview.Register("GearAndStorage.StackResponse", (Action)delegate(long sender, int id, bool granted, int revision) { Receive(c, sender, id, granted, revision); }); } } } private const string RequestRpc = "GearAndStorage.StackRequest"; private const string ResponseRpc = "GearAndStorage.StackResponse"; internal static ConfigEntry Enabled; internal static ConfigEntry ProtectHotbar; internal static ConfigEntry Range; internal static ConfigEntry Key; internal static bool Transferring; private static readonly Dictionary pending = new Dictionary(); private static int serial; private static int moved; private static int usedChests; private static int skipped; 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 //IL_0080: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind("QuickStack", "Enabled", true, "Stack matching items into accessible nearby chests. A single key press waits for ownership and the matching inventory revision."); Range = config.Bind("QuickStack", "Range", 20f, new ConfigDescription("Distance from player in metres.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); ProtectHotbar = config.Bind("QuickStack", "ProtectHotbar", true, "Keep the first inventory row. Equipment and quick slots are always protected."); Key = config.Bind("Controls", "QuickStack", new KeyboardShortcut((KeyCode)98, Array.Empty()), "Quick stack nearby chests."); } internal static bool Protected(ItemData item) { if (!item.m_equipped && PlayerSlots.Tag(item) < 0) { if (ProtectHotbar.Value) { return item.m_gridPos.y == 0; } return false; } return true; } private static bool Accessible(Container c, Player p) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && (Object)(object)p != (Object)null && c.m_inventory != null && (Object)(object)((Component)c).GetComponentInParent() == (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid() && !c.IsInUse() && c.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)c.m_wagon == (Object)null || !c.m_wagon.InUse()) && Vector3.Distance(((Component)c).transform.position, ((Component)p).transform.position) <= Range.Value && c.CheckAccess(p.GetPlayerID())) { if (c.m_checkGuardStone) { return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false); } return true; } return false; } internal static void Start(Player p) { if (!Enabled.Value || pending.Count > 0) { return; } moved = (usedChests = (skipped = 0)); Container[] array = (from c in ChestCrafting.LoadedContainers where Accessible(c, p) orderby Vector3.SqrMagnitude(((Component)c).transform.position - ((Component)p).transform.position) select c).ToArray(); foreach (Container val in array) { bool flag = val.m_nview.IsOwner(); Request request = new Request { Deadline = Time.realtimeSinceStartup + 10f, Id = ++serial, Owner = val.m_nview.GetZDO().GetOwner(), Local = flag, Granted = flag }; pending[val] = request; if (!flag) { val.m_nview.InvokeRPC("GearAndStorage.StackRequest", new object[2] { p.GetPlayerID(), request.Id }); } } if (pending.Count == 0) { ((Character)p).Message((MessageType)2, "Žádná přístupná truhla v dosahu.", 0, (Sprite)null, false); } else { Tick(p, canAct: true); } } internal static void Tick(Player player, bool canAct) { if (pending.Count == 0) { return; } KeyValuePair[] array = pending.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; Container key = keyValuePair.Key; Request value = keyValuePair.Value; if (!canAct || !Enabled.Value || !Accessible(key, player) || Time.realtimeSinceStartup > value.Deadline) { pending.Remove(key); skipped++; } else { if (!value.Granted || !key.m_nview.IsOwner() || (!value.Local && key.m_nview.GetZDO().DataRevision < value.Revision)) { continue; } pending.Remove(key); key.Load(); Transferring = true; try { int num = ((Humanoid)player).GetInventory().GetAllItems().Sum((ItemData val) => val.m_stack); key.m_inventory.StackAll(((Humanoid)player).GetInventory(), false); int num2 = num - ((Humanoid)player).GetInventory().GetAllItems().Sum((ItemData val) => val.m_stack); moved += num2; if (num2 > 0) { usedChests++; } } finally { Transferring = false; } } } if (pending.Count == 0 && (Object)(object)player != (Object)null) { ((Character)player).Message((MessageType)2, ((moved > 0) ? $"Uloženo {moved} ks do {usedChests} truhel." : "Žádné odpovídající předměty k uložení nebo volné místo.") + ((skipped > 0) ? $" Nedostupné truhly: {skipped}." : ""), 0, (Sprite)null, false); } } internal static void Reset() { pending.Clear(); Transferring = false; } internal static void Receive(Container c, long sender, int id, bool granted, int revision) { if (pending.TryGetValue(c, out var value) && id == value.Id && sender == value.Owner) { value.Granted = granted; value.Revision = (uint)revision; if (!granted) { value.Deadline = -1f; } } } internal static void Grant(Container c, long sender, long playerId, int id) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)c.m_nview == (Object)null) && c.m_nview.IsValid() && c.m_nview.IsOwner()) { bool flag = Enabled.Value && (Object)(object)((Component)c).GetComponentInParent() == (Object)null && !c.IsInUse() && c.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)c.m_wagon == (Object)null || !c.m_wagon.InUse()) && c.CheckAccess(playerId) && (!c.m_checkGuardStone || ChestCrafting.MachineAccess(((Component)c).transform.position, playerId)); uint num = 0u; if (flag) { c.Load(); c.Save(); num = c.m_nview.GetZDO().DataRevision; ZDOMan.instance.ForceSendZDO(sender, c.m_nview.GetZDO().m_uid); c.m_nview.GetZDO().SetOwner(sender); } c.m_nview.InvokeRPC(sender, "GearAndStorage.StackResponse", new object[3] { id, flag, (int)num }); } } } internal static class ServerSettings { [HarmonyPatch(typeof(ConfigFile), "Save")] private static class PreventRemoteSave { private static bool Prefix(ConfigFile __instance) { if (__instance == config) { return !Locked; } return true; } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class Register { private static void Prefix(ZNetPeer peer) { if (!IsServer && peer.m_server) { server = peer.m_rpc; } peer.m_rpc.Register("GearAndStorage.Hello", (Action)delegate(ZRpc rpc, string version) { if (IsServer) { versions[rpc] = version; } }); peer.m_rpc.Register("GearAndStorage.Settings", (Action)Receive); WorkbenchNetwork.Register(peer.m_rpc); QuestNetwork.Register(peer.m_rpc); } } [HarmonyPatch(typeof(ZNet), "SendPeerInfo")] private static class Send { private static void Prefix(ZRpc rpc) { if (IsServer) { rpc.Invoke("GearAndStorage.Settings", new object[1] { Package() }); WorkbenchNetwork.Send(rpc); QuestNetwork.Send(rpc); } else { rpc.Invoke("GearAndStorage.Hello", new object[1] { "0.5.7" }); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class RequireMod { private static bool Prefix(ZRpc rpc) { int num; if (!IsServer) { if (rpc == server) { num = (Locked ? 1 : 0); goto IL_0036; } num = 0; } else { if (versions.TryGetValue(rpc, out var value)) { num = ((value == "0.5.7") ? 1 : 0); goto IL_0036; } num = 0; } goto IL_0039; IL_0036: if (num == 0) { goto IL_0039; } goto IL_003f; IL_003f: return (byte)num != 0; IL_0039: Reject(rpc); goto IL_003f; } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static class Leave { private static void Postfix() { Restore(); } } private const string Hello = "GearAndStorage.Hello"; private const string Settings = "GearAndStorage.Settings"; private static ConfigFile config; private static ConfigEntryBase[] entries; private static readonly Dictionary versions = new Dictionary(); private static readonly Dictionary local = new Dictionary(); private static readonly Dictionary remote = new Dictionary(); private static ZRpc server; private static bool applying; private static bool saveOnSet; private static float nextCheck; private static DateTime lastWrite; internal static bool Locked => remote.Count > 0; internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void Initialize(ConfigFile file) { config = file; entries = (from kv in (IEnumerable>)config select kv.Value into e where !(e.Definition.Section == "Equipment") && !(e.Definition.Section == "Controls") && !(e.Definition.Section == "QuestsUI") && (!(e.Definition.Section == "QuickSlots") || !(e.Definition.Key != "Count")) select e).OrderBy((ConfigEntryBase e) => e.Definition.Section, StringComparer.Ordinal).ThenBy((ConfigEntryBase e) => e.Definition.Key, StringComparer.Ordinal).ToArray(); config.SettingChanged += delegate(object _, SettingChangedEventArgs args) { if (!applying) { if (remote.TryGetValue(args.ChangedSetting, out var value)) { applying = true; try { args.ChangedSetting.SetSerializedValue(value); } finally { applying = false; } } else if (IsServer) { Broadcast(); } WorkbenchNetwork.Invalidate(); } }; lastWrite = File.GetLastWriteTimeUtc(config.ConfigFilePath); } internal static void Tick() { if (Time.realtimeSinceStartup < nextCheck) { return; } nextCheck = Time.realtimeSinceStartup + 2f; if (Locked && ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.GetPeers().Any((ZNetPeer p) => p.m_rpc == server))) { Restore(); } if (!IsServer) { return; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(config.ConfigFilePath); if (lastWriteTimeUtc == lastWrite) { return; } lastWrite = lastWriteTimeUtc; applying = true; try { config.Reload(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cannot reload server configuration: " + ex.Message)); } finally { applying = false; } Broadcast(); WorkbenchNetwork.Invalidate(); } private static ZPackage Package() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write("0.5.7"); val.Write(entries.Length); ConfigEntryBase[] array = entries; foreach (ConfigEntryBase val2 in array) { val.Write(val2.Definition.Section); val.Write(val2.Definition.Key); val.Write(val2.GetSerializedValue()); } return val; } private static void Broadcast() { if (!IsServer) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady() && versions.TryGetValue(peer.m_rpc, out var value) && value == "0.5.7") { peer.m_rpc.Invoke("GearAndStorage.Settings", new object[1] { Package() }); } } } private static void Receive(ZRpc rpc, ZPackage pkg) { if (IsServer || rpc != server) { return; } try { if (pkg.ReadString() != "0.5.7" || pkg.ReadInt() != entries.Length) { throw new Exception("Configuration version mismatch"); } Dictionary dictionary = new Dictionary(); ConfigEntryBase[] array = entries; foreach (ConfigEntryBase val in array) { if (pkg.ReadString() != val.Definition.Section || pkg.ReadString() != val.Definition.Key) { throw new Exception("Configuration schema mismatch"); } string text = pkg.ReadString(); object obj = TomlTypeConverter.ConvertToValue(text, val.SettingType); if (val.Description.AcceptableValues != null && !val.Description.AcceptableValues.IsValid(obj)) { throw new Exception("Invalid server setting"); } dictionary.Add(val, text); } if (!Locked) { saveOnSet = config.SaveOnConfigSet; array = entries; foreach (ConfigEntryBase val2 in array) { local[val2] = val2.GetSerializedValue(); } } config.SaveOnConfigSet = false; applying = true; try { foreach (KeyValuePair item in dictionary) { remote[item.Key] = item.Value; item.Key.SetSerializedValue(item.Value); } } finally { applying = false; } WorkbenchNetwork.Invalidate(); Plugin.Log.LogInfo((object)"Server configuration applied and locked for this session."); } catch (Exception ex) { Plugin.Log.LogError((object)ex.Message); Reject(rpc); } } private static void Reject(ZRpc rpc) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogError((object)"GearAndStorage 0.5.7 is required on the server and every client."); if (IsServer) { rpc.Invoke("Error", new object[1] { 3 }); } else { ZNet.m_connectionStatus = (ConnectionStatus)3; } ZNetPeer peer = ZNet.instance.GetPeer(rpc); if (peer != null) { ZNet.instance.Disconnect(peer); } } internal static void Restore() { bool flag = local.Count > 0 && saveOnSet; applying = true; try { foreach (KeyValuePair item in local) { item.Key.SetSerializedValue(item.Value); } if (local.Count > 0) { config.SaveOnConfigSet = saveOnSet; } local.Clear(); remote.Clear(); versions.Clear(); server = null; WorkbenchNetwork.Reset(); QuestNetwork.Reset(); QuickStack.Reset(); } finally { applying = false; } if (flag) { config.Save(); } } internal static bool FromServer(ZRpc rpc) { if (!IsServer && rpc == server) { return Locked; } return false; } internal static IEnumerable Clients() { string value; return from p in ZNet.instance.GetPeers() where p.IsReady() && versions.TryGetValue(p.m_rpc, out value) && value == "0.5.7" select p; } } internal static class SlotUi { [HarmonyPatch(typeof(InventoryGui), "UpdateInventory")] private static class ResizePanel { private static void Prefix(InventoryGui __instance, Player player) { if (PlayerSlots.TryState(((Humanoid)player).GetInventory(), out var state)) { __instance.SetInventorySize(state.NormalRows); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateInventory")] private static class Decorate { private static void Postfix(InventoryGrid __instance, Inventory inventory, Player player) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !PlayerSlots.TryState(inventory, out var state)) { return; } RectTransform val = EnsurePanel(__instance); int width = inventory.GetWidth(); Vector2 val5 = default(Vector2); for (int i = 0; i < __instance.m_elements.Count; i++) { int num = i % width; int num2 = i / width; InventoryElement val2 = __instance.m_elements[i]; bool flag = num2 >= state.NormalRows; int num3 = PlayerSlots.SlotAt(state, num, num2); ((Component)val2).gameObject.SetActive(!flag || num3 >= 0 || inventory.GetItemAt(num, num2) != null); Transform obj = ((Component)val2).transform.Find("binding"); TMP_Text val3 = ((obj != null) ? ((Component)obj).GetComponent() : null); if ((Object)(object)val3 == (Object)null) { continue; } ((Behaviour)val3).enabled = num2 == 0 || num3 >= 0; if (num3 >= 0) { RectTransform val4 = (RectTransform)((Component)val2).transform; if ((Object)(object)((Transform)val4).parent != (Object)(object)val) { ((Transform)val4).SetParent((Transform)(object)val, false); } ((Vector2)(ref val5))..ctor(0f, 1f); val4.anchorMax = val5; val4.anchorMin = val5; val4.pivot = new Vector2(0.5f, 0.5f); int num4 = num3 - 5; int num5 = num3 switch { 11 => 5, 12 => 6, _ => num3, }; Vector2 val6 = (Vector2)(LayoutPlanner.IsEquipment(num3) ? GearPositions[num5] : new Vector2((float)(num4 % 3), 4.6f + (float)(num4 / 3))); val4.anchoredPosition = new Vector2(8f + __instance.m_elementSpace * (0.5f + val6.x), -8f - __instance.m_elementSpace * (0.5f + val6.y)); val3.text = (LayoutPlanner.IsEquipment(num3) ? Labels[num5] : KeyLabel(Plugin.Keys[num3 - 5].Value)); RectTransform rectTransform = val3.rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, -3f); rectTransform.sizeDelta = new Vector2(-8f, 16f); val3.textWrappingMode = (TextWrappingModes)0; val3.enableAutoSizing = true; val3.fontSizeMin = 8f; val3.fontSizeMax = 11f; val3.overflowMode = (TextOverflowModes)1; val3.alignment = (TextAlignmentOptions)258; ((Graphic)val3).raycastTarget = false; } else if (num2 == 0) { val3.text = (num + 1).ToString(); } } __instance.m_gridRoot.SetSizeWithCurrentAnchors((Axis)1, (float)state.NormalRows * __instance.m_elementSpace); } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class ValidateSelection { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (__instance.m_dragItem == null) { return true; } if (!CanPlace(grid.GetInventory(), __instance.m_dragItem, pos)) { return false; } if (item != null && item != __instance.m_dragItem && !CanPlace(__instance.m_dragInventory, item, __instance.m_dragItem.m_gridPos)) { return false; } return true; } } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] private static class Drop { private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, Vector2i pos, ref bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = __instance.GetInventory(); ItemData itemAt = inventory.GetItemAt(pos.x, pos.y); if (!CanPlace(inventory, item, pos) || (itemAt != null && itemAt != item && !CanPlace(fromInventory, itemAt, item.m_gridPos))) { __result = false; return false; } return true; } private static void Postfix(InventoryGrid __instance, Inventory fromInventory, bool __result) { if (__result) { Retag(__instance.GetInventory()); if (fromInventory != __instance.GetInventory()) { Retag(fromInventory); } } } } private static readonly string[] Labels = new string[7] { "HEAD", "CHEST", "LEGS", "CAPE", "UTILITY 1", "UTILITY 2", "TRINKET" }; private static RectTransform panel; private static readonly Vector3[] corners = (Vector3[])(object)new Vector3[4]; private static readonly Vector2[] GearPositions = (Vector2[])(object)new Vector2[7] { new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 3f), new Vector2(2f, 1f), new Vector2(0.5f, 2f), new Vector2(1.5f, 2f), new Vector2(0f, 1f) }; private unsafe static string KeyLabel(KeyboardShortcut key) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (((KeyboardShortcut)(ref key)).Modifiers.Count() != 1 || !((KeyboardShortcut)(ref key)).Modifiers.Contains((KeyCode)308)) { return ((object)(*(KeyboardShortcut*)(&key))/*cast due to .constrained prefix*/).ToString(); } return "LAlt+" + ((object)((KeyboardShortcut)(ref key)).MainKey/*cast due to .constrained prefix*/).ToString(); } private static RectTransform EnsurePanel(InventoryGrid grid) { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel == (Object)null) { GameObject val = new GameObject("GearAndStorage.Panel", new Type[2] { typeof(RectTransform), typeof(Image) }); panel = (RectTransform)val.transform; ((Transform)panel).SetParent((Transform)(object)InventoryGui.instance.m_player, false); RectTransform obj = panel; RectTransform obj2 = panel; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(1f, 1f); obj2.anchorMax = val2; obj.anchorMin = val2; panel.pivot = new Vector2(0f, 1f); Image component = val.GetComponent(); ((Graphic)component).color = new Color(0.08f, 0.09f, 0.1f, 0.95f); ((Graphic)component).raycastTarget = false; } panel.anchoredPosition = Plugin.PanelPosition.Value; float num = ((Plugin.QuickCount.Value == 0) ? 4f : ((Plugin.QuickCount.Value > 3) ? 6.6f : 5.6f)); panel.sizeDelta = new Vector2(grid.m_elementSpace * 3f + 16f, grid.m_elementSpace * num + 16f); return panel; } internal static Rect ScreenRect(RectTransform rect) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) Canvas componentInParent = ((Component)rect).GetComponentInParent(); Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); rect.GetWorldCorners(corners); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(float.PositiveInfinity, float.PositiveInfinity); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(float.NegativeInfinity, float.NegativeInfinity); Vector3[] array = corners; foreach (Vector3 val4 in array) { Vector2 val5 = RectTransformUtility.WorldToScreenPoint(val, val4); val2 = Vector2.Min(val2, val5); val3 = Vector2.Max(val3, val5); } return Rect.MinMaxRect(val2.x, val2.y, val3.x, val3.y); } private static Vector2 HudOrigin(float barWidth, float size) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) Vector2 value = Plugin.HudPosition.Value; Hud instance = Hud.instance; if (Plugin.AnchorAboveGuardianPower.Value && (Object)(object)instance.m_gpIcon != (Object)null && ((Component)instance.m_gpIcon).gameObject.activeInHierarchy) { Rect val = ScreenRect(((Graphic)instance.m_gpIcon).rectTransform); float num = ((Rect)(ref val)).yMax; if ((Object)(object)instance.m_gpName != (Object)null && ((Component)instance.m_gpName).gameObject.activeInHierarchy) { float num2 = num; Rect val2 = ScreenRect(instance.m_gpName.rectTransform); num = Mathf.Max(num2, ((Rect)(ref val2)).yMax); } Vector2 value2 = Plugin.GuardianPowerOffset.Value; ((Vector2)(ref value))..ctor(((Rect)(ref val)).center.x - barWidth / 2f + value2.x, num + value2.y); } value.x = Mathf.Clamp(value.x, 4f, Mathf.Max(4f, (float)Screen.width - barWidth - 4f)); value.y = Mathf.Clamp(value.y, 4f, Mathf.Max(4f, (float)Screen.height - size - 24f)); return value; } internal static void DrawHud() { //IL_004f: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: 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) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)Hud.instance == (Object)null || InventoryGui.IsVisible() || Menu.IsVisible()) { return; } Vector2 val = HudOrigin((float)Plugin.QuickCount.Value * 58f - 4f, 54f); Rect val2 = default(Rect); for (int i = 0; i < Plugin.QuickCount.Value; i++) { ((Rect)(ref val2))..ctor(val.x + (float)i * 58f, (float)Screen.height - val.y - 54f, 54f, 54f); GUI.Box(val2, GUIContent.none); ItemData slot = PlayerSlots.GetSlot(localPlayer, 5 + i); if (slot != null && (Object)(object)slot.GetIcon() != (Object)null) { Sprite icon = slot.GetIcon(); Rect textureRect = icon.textureRect; Texture2D texture = icon.texture; GUI.DrawTextureWithTexCoords(new Rect(((Rect)(ref val2)).x + 5f, ((Rect)(ref val2)).y + 5f, 44f, 44f), (Texture)(object)texture, new Rect(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height)); if (slot.m_stack > 1) { GUI.Label(new Rect(((Rect)(ref val2)).x + 30f, ((Rect)(ref val2)).y + 32f, 30f, 20f), slot.m_stack.ToString()); } } GUI.Label(new Rect(((Rect)(ref val2)).x + 3f, ((Rect)(ref val2)).y - 17f, 62f, 22f), KeyLabel(Plugin.Keys[i].Value)); } } internal static bool CanPlace(Inventory inventory, ItemData item, Vector2i position) { //IL_000c: 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_0024: Unknown result type (might be due to invalid IL or missing references) if (!PlayerSlots.TryState(inventory, out var state)) { return true; } if (position.y < state.NormalRows) { return true; } return PlayerSlots.ValidSlot(item, PlayerSlots.SlotAt(state, position.x, position.y)); } private static void Retag(Inventory inventory) { if (!PlayerSlots.TryState(inventory, out var state)) { return; } foreach (ItemData allItem in inventory.GetAllItems()) { int num = PlayerSlots.SlotAt(state, allItem.m_gridPos.x, allItem.m_gridPos.y); PlayerSlots.SetTag(allItem, PlayerSlots.ValidSlot(allItem, num) ? num : (-1)); if (LayoutPlanner.IsEquipment(num) && PlayerSlots.ValidSlot(allItem, num) && !allItem.m_equipped) { ((Humanoid)state.Player).EquipItem(allItem, true); } } } } internal static class StackSettings { private sealed class State { internal WeakReference Shared; internal StackBaseline Baseline; } [HarmonyPatch] private static class Database { private static IEnumerable TargetMethods() { return new string[3] { "Awake", "CopyOtherDB", "UpdateRegisters" }.Select((string name) => AccessTools.Method(typeof(ObjectDB), name, (Type[])null, (Type[])null)); } [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { Prefabs(__instance); } } [HarmonyPatch(typeof(ItemDrop), "Awake")] private static class Spawn { [HarmonyPriority(0)] private static void Postfix(ItemDrop __instance) { Apply(__instance.m_itemData); } } [HarmonyPatch(typeof(ItemData), "Clone")] private static class Clone { private static void Prefix(ItemData __instance) { Apply(__instance); } } [HarmonyPatch] private static class InventoryItems { private static IEnumerable TargetMethods() { return from m in typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "Load" || (m.Name == "AddItem" && m.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(ItemData))) select m; } [HarmonyPriority(800)] private static void Prefix(Inventory __instance, object[] __args) { TrackInventory(__instance); foreach (object obj in __args) { ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { Apply(val); } } } } [HarmonyPatch] private static class PreserveLoadedStack { private static MethodBase TargetMethod() { return typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).Single((MethodInfo m) => m.Name == "AddItem" && m.GetParameters().Length == 14 && m.GetParameters()[0].ParameterType == typeof(int)); } private static IEnumerable Transpiler(IEnumerable instructions) { return RewriteLoadClamp(instructions); } } [HarmonyPatch(typeof(ItemDrop), "SetStack")] private static class PreserveGroundRemainder { private static bool Prefix(ItemDrop __instance, int stack) { Apply(__instance.m_itemData); if (stack <= __instance.m_itemData.m_shared.m_maxStackSize || stack > __instance.m_itemData.m_stack || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner()) { return true; } __instance.m_itemData.m_stack = stack; __instance.Save(); return false; } } internal static ConfigEntry Increase; private static readonly ConditionalWeakTable known = new ConditionalWeakTable(); private static readonly List states = new List(); private static readonly ConditionalWeakTable inventories = new ConditionalWeakTable(); private static readonly List> inventoryRefs = new List>(); private static bool refreshing; private static float nextRefresh; private static int Percent => Increase?.Value ?? 0; internal static void Initialize(ConfigFile config) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown Increase = config.Bind("Stacks", "IncreasePercent", 0, new ConfigDescription("Extra stack capacity: 0 disables; 50 = 1.5x, 100 = 2x, 500 = 6x. Stackable items only. Fractions round up. Existing larger stacks are preserved when lowering the limit.", (AcceptableValueBase)(object)new AcceptableValueList((from n in Enumerable.Range(0, 11) select n * 50).ToArray()), Array.Empty())); Increase.SettingChanged += delegate { Refresh(); }; } private static State Track(SharedData shared, int original) { if (known.TryGetValue(shared, out var value)) { return value; } value = new State { Shared = new WeakReference(shared), Baseline = new StackBaseline(original) }; shared.m_maxStackSize = original; known.Add(shared, value); states.Add(value); return value; } private static void Update(State state, SharedData shared) { shared.m_maxStackSize = state.Baseline.Update(shared.m_maxStackSize, Percent); } internal static void Apply(ItemData item) { SharedData val = item?.m_shared; if (val == null) { return; } if (!known.TryGetValue(val, out var value)) { int num = val.m_maxStackSize; GameObject dropPrefab = item.m_dropPrefab; SharedData val2 = ((dropPrefab == null) ? null : dropPrefab.GetComponent()?.m_itemData?.m_shared); if (val2 != null && val2 != val) { State state = Track(val2, val2.m_maxStackSize); Update(state, val2); num = state.Baseline.OriginalForCopy(num); } value = Track(val, num); } Update(value, val); } private static void TrackInventory(Inventory inventory) { if (inventory != null && !inventories.TryGetValue(inventory, out var _)) { inventories.Add(inventory, new object()); inventoryRefs.Add(new WeakReference(inventory)); } } private static void Prefabs(ObjectDB db) { if ((Object)(object)db == (Object)null) { return; } foreach (GameObject item in db.m_items) { if ((Object)(object)item != (Object)null) { Apply(item.GetComponent()?.m_itemData); } } } internal static void Refresh() { if (refreshing) { return; } refreshing = true; try { Prefabs(ObjectDB.instance); for (int num = inventoryRefs.Count - 1; num >= 0; num--) { if (!inventoryRefs[num].TryGetTarget(out var target)) { inventoryRefs.RemoveAt(num); } else { foreach (ItemData item in target.m_inventory) { Apply(item); } } } foreach (ItemDrop s_instance in ItemDrop.s_instances) { if ((Object)(object)s_instance != (Object)null) { Apply(s_instance.m_itemData); } } for (int num2 = states.Count - 1; num2 >= 0; num2--) { if (states[num2].Shared.TryGetTarget(out var target2)) { Update(states[num2], target2); } else { states.RemoveAt(num2); } } } finally { refreshing = false; } } internal static void Tick() { if (!(Time.realtimeSinceStartup < nextRefresh)) { nextRefresh = Time.realtimeSinceStartup + 2f; Refresh(); } } internal static IEnumerable RewriteLoadClamp(IEnumerable instructions) { MethodInfo min = typeof(Mathf).GetMethod("Min", new Type[2] { typeof(int), typeof(int) }); MethodInfo preserve = typeof(StackPolicy).GetMethod("LoadedAmount", BindingFlags.Static | BindingFlags.NonPublic); int count = 0; foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Call && object.Equals(instruction.operand, min)) { instruction.operand = preserve; count++; } yield return instruction; } if (count != 1) { throw new InvalidOperationException("Inventory stack load clamp changed; expected one call, found " + count); } } } internal static class Storage { private sealed class Dimensions { internal ConfigEntry Columns; internal ConfigEntry Rows; } private sealed class Context { internal Container Container; internal Dimensions Size; internal int Loading; } [HarmonyPatch(typeof(Container), "Awake")] private static class Create { private static void Prefix(Container __instance) { Dimensions dimensions = Identify(__instance); if (dimensions != null) { __instance.m_width = dimensions.Columns.Value; __instance.m_height = dimensions.Rows.Value; } } private static void Postfix(Container __instance) { Dimensions size = Identify(__instance); if (size != null && __instance.m_inventory != null) { Inventories.GetValue(__instance.m_inventory, (Inventory _) => new Context { Container = __instance, Size = size }); } } } [HarmonyPatch(typeof(Container), "Load")] private static class Load { private static void Prefix(Container __instance, out Context __state) { __state = null; if (__instance.m_inventory != null && Inventories.TryGetValue(__instance.m_inventory, out __state)) { __state.Loading++; } } private static void Finalizer(Context __state) { if (__state != null) { __state.Loading--; Refresh(__state); } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] private static class LoadCell { [HarmonyPriority(800)] private static void Prefix(Inventory __instance, int x, int y) { if (Inventories.TryGetValue(__instance, out var value) && value.Loading != 0) { __instance.m_width = Math.Max(__instance.m_width, x + 1); __instance.m_height = Math.Max(__instance.m_height, y + 1); } } } [HarmonyPatch(typeof(Container), "CheckForChanges")] private static class Tick { private static void Postfix(Container __instance) { if (__instance.m_inventory != null && Inventories.TryGetValue(__instance.m_inventory, out var value)) { Refresh(value); } } } private static readonly Dictionary Types = new Dictionary(); private static readonly ConditionalWeakTable Inventories = new ConditionalWeakTable(); internal static void Initialize(ConfigFile config) { Bind(config, "piece_chest_wood", "WoodChest", 5, 3); Bind(config, "piece_chest_private", "PersonalChest", 3, 3); Bind(config, "piece_chest", "ReinforcedChest", 6, 5); Bind(config, "piece_chest_blackmetal", "BlackMetalChest", 8, 5); Bind(config, "Cart", "Cart", 8, 4); Bind(config, "Karve", "Karve", 2, 3); Bind(config, "VikingShip", "Longship", 8, 4); } private static void Bind(ConfigFile config, string prefab, string section, int columns, int rows) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown Types[prefab] = new Dimensions { Columns = config.Bind("Storage." + section, "Columns", columns, new ConfigDescription("Requested columns. Occupied cells are preserved when shrinking.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 8), Array.Empty())), Rows = config.Bind("Storage." + section, "Rows", rows, new ConfigDescription("Requested rows. Occupied cells are preserved when shrinking.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), Array.Empty())) }; } private static Dimensions Identify(Container container) { if ((Object)(object)((Component)container).GetComponentInParent() != (Object)null) { return null; } if (!Types.TryGetValue(((Object)((Component)container).gameObject).name.Replace("(Clone)", "").Trim(), out var value)) { return null; } return value; } private static void Refresh(Context context) { if (context.Loading > 0 || (Object)(object)context.Container == (Object)null) { return; } Inventory inventory = context.Container.m_inventory; int num = context.Size.Columns.Value; int num2 = context.Size.Rows.Value; foreach (ItemData allItem in inventory.GetAllItems()) { num = Math.Max(num, allItem.m_gridPos.x + 1); num2 = Math.Max(num2, allItem.m_gridPos.y + 1); } inventory.m_width = (context.Container.m_width = num); inventory.m_height = (context.Container.m_height = num2); } } internal static class WeightSettings { [HarmonyPatch(typeof(ItemData), "GetWeight")] private static class Weight { private static void Postfix(ref float __result) { __result *= 1f - (float)Reduction.Value / 100f; } } [HarmonyPatch(typeof(ItemData), "GetNonStackedWeight")] private static class UnitWeight { private static void Postfix(ref float __result) { __result *= 1f - (float)Reduction.Value / 100f; } } [HarmonyPatch(typeof(Inventory), "GetTotalWeight")] private static class TotalWeight { private static void Postfix(Inventory __instance, ref float __result) { __result = __instance.GetAllItems().Sum((ItemData item) => item.GetWeight(-1)); } } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] private static class Capacity { private static void Postfix(ref float __result) { __result += (float)(BaseCapacity.Value - 300) * Game.m_carryWeightRate; } } internal static ConfigEntry Reduction; internal static ConfigEntry BaseCapacity; internal static void Initialize(ConfigFile config) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown Reduction = config.Bind("Weight", "ReductionPercent", 0, new ConfigDescription("Reduce all item weights by this percentage. 100 means zero weight.", (AcceptableValueBase)(object)new AcceptableValueList(new int[5] { 0, 25, 50, 70, 100 }), Array.Empty())); BaseCapacity = config.Bind("Weight", "BaseCarryCapacity", 300, new ConfigDescription("Base carry capacity in steps of 50 (vanilla 300). Equipment bonuses and the world carry modifier remain separate.", (AcceptableValueBase)(object)new AcceptableValueList((from x in Enumerable.Range(6, 35) select x * 50).ToArray()), Array.Empty())); } } internal static class WorkbenchNetwork { [HarmonyPatch(typeof(CraftingStation), "GetStationBuildRange")] private static class BuildRange { private static void Postfix(CraftingStation __instance, ref float __result) { if (enabled.Value) { __result = Radius(__instance); if ((Object)(object)__instance.m_areaMarkerCircle != (Object)null) { __instance.m_areaMarkerCircle.m_radius = __result; } } } } [HarmonyPatch(typeof(CraftingStation), "ShowAreaMarker")] private static class Marker { private static void Prefix(CraftingStation __instance) { if (enabled.Value) { __instance.GetStationBuildRange(); } } } [HarmonyPatch(typeof(CraftingStation), "HaveBuildStationInRange")] private static class BuildPermission { private static void Postfix(string name, Vector3 point, ref CraftingStation __result) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__result != (Object)null || !enabled.Value) { return; } DiscoverPrefabs(); if (!stationNames.TryGetValue(name, out var value) || !Graph().Allows(value, point.x, point.z)) { return; } foreach (CraftingStation allStation in CraftingStation.m_allStations) { if (!((Object)(object)allStation == (Object)null) && ((Object)((Component)allStation).gameObject).name.StartsWith("piece_workbench", StringComparison.Ordinal)) { Vector3 position = ((Component)allStation).transform.position; float num = position.x - point.x; float num2 = position.z - point.z; float num3 = Radius(allStation); if (num * num + num2 * num2 <= num3 * num3) { __result = allStation; break; } } } } } private const string RpcName = "GearAndStorage.Stations"; private static ConfigEntry enabled; private static ConfigEntry basic; private static ConfigEntry expanded; private static readonly Dictionary stationPrefabs = new Dictionary(); private static readonly Dictionary stationNames = new Dictionary(); private static StationNode[] nodes = Array.Empty(); private static StationGraph graph; private static byte[] previous; private static float nextScan; 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 //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown enabled = config.Bind("Workstations", "SharedBuildArea", false, "Share building station requirements through connected workbenches. A new workbench centre must be inside another workbench radius. Crafting, repair and upgrade levels remain local."); basic = config.Bind("Workstations", "BaseBuildRange", 20f, new ConfigDescription("Building radius in metres for standalone stations when SharedBuildArea is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), Array.Empty())); expanded = config.Bind("Workstations", "ConnectedBuildRange", 40f, new ConfigDescription("Workbench radius after a stonecutter is connected. Cannot effectively be smaller than BaseBuildRange.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), Array.Empty())); } internal static void Invalidate() { graph = null; nextScan = 0f; } internal static void Reset() { nodes = Array.Empty(); previous = null; stationPrefabs.Clear(); stationNames.Clear(); Invalidate(); } private static void DiscoverPrefabs() { if (stationPrefabs.Count > 0 || (Object)(object)ZNetScene.instance == (Object)null) { return; } foreach (KeyValuePair namedPrefab in ZNetScene.instance.m_namedPrefabs) { CraftingStation component = namedPrefab.Value.GetComponent(); if (!((Object)(object)component == (Object)null)) { stationPrefabs[namedPrefab.Key] = ((Object)namedPrefab.Value).name; stationNames[component.m_name] = ((Object)namedPrefab.Value).name; } } } private static StationGraph Graph() { return graph ?? (graph = new StationGraph(nodes, basic.Value, Math.Max(basic.Value, expanded.Value))); } private static ZPackage Package() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(nodes.Length); StationNode[] array = nodes; foreach (StationNode stationNode in array) { val.Write(stationNode.Type); val.Write(stationNode.X); val.Write(stationNode.Z); } return val; } internal static void Tick() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (!ServerSettings.IsServer || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || Time.realtimeSinceStartup < nextScan) { return; } nextScan = Time.realtimeSinceStartup + 2f; DiscoverPrefabs(); List list = new List(); if (enabled.Value) { foreach (ZDO value2 in ZDOMan.instance.m_objectsByID.Values) { if (stationPrefabs.TryGetValue(value2.GetPrefab(), out var value)) { Vector3 position = value2.GetPosition(); list.Add(new StationNode { Type = value, X = position.x, Z = position.z }); } } } nodes = list.ToArray(); ZPackage val = Package(); byte[] array = val.GetArray(); if (previous != null && previous.SequenceEqual(array)) { return; } previous = array; graph = null; foreach (ZNetPeer item in ServerSettings.Clients()) { item.m_rpc.Invoke("GearAndStorage.Stations", new object[1] { val }); } } internal static void Send(ZRpc rpc) { nextScan = 0f; Tick(); rpc.Invoke("GearAndStorage.Stations", new object[1] { Package() }); } internal static void Register(ZRpc rpc) { rpc.Register("GearAndStorage.Stations", (Action)delegate(ZRpc sender, ZPackage pkg) { if (!ServerSettings.FromServer(sender)) { return; } try { int num = pkg.ReadInt(); if (num < 0 || num > 100000) { throw new Exception("Invalid station count"); } StationNode[] array = new StationNode[num]; for (int i = 0; i < num; i++) { string type = pkg.ReadString(); float num2 = pkg.ReadSingle(); float num3 = pkg.ReadSingle(); if (float.IsNaN(num2) || float.IsInfinity(num2) || float.IsNaN(num3) || float.IsInfinity(num3)) { throw new Exception("Invalid station coordinates"); } array[i] = new StationNode { Type = type, X = num2, Z = num3 }; } nodes = array; graph = null; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Invalid station snapshot: " + ex.Message)); } }); } private static float Radius(CraftingStation station) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) DiscoverPrefabs(); StationGraph stationGraph = Graph(); Vector3 position = ((Component)station).transform.position; string text = ((Object)((Component)station).gameObject).name.Replace("(Clone)", ""); for (int i = 0; i < nodes.Length; i++) { if (nodes[i].Type == text && Math.Abs(nodes[i].X - position.x) < 0.05f && Math.Abs(nodes[i].Z - position.z) < 0.05f) { return stationGraph.Radius[i]; } } return basic.Value; } } } namespace GearAndStorage.Core { public static class FurnacePolicy { private static readonly HashSet Ores = new HashSet(StringComparer.Ordinal) { "CopperOre", "TinOre", "IronScrap", "SilverOre", "BlackMetalScrap", "CopperScrap", "FlametalOre", "FlametalOreNew" }; public static bool IsRawOre(string prefab) { if (prefab != null) { return Ores.Contains(prefab); } return false; } public static int FuelSpace(float current, int maximum) { return Math.Max(0, (int)Math.Floor((float)maximum - current)); } } public sealed class LayoutItem { public int Id; public int X; public int Y; public int Slot = -1; public bool Equipped; } public sealed class LayoutResult { public int NormalRows; public List Items = new List(); } public static class LayoutPlanner { public const int EquipmentCount = 5; public const int SecondUtility = 11; public const int Trinket = 12; public static bool IsEquipment(int slot) { if ((slot < 0 || slot >= 5) && slot != 11) { return slot == 12; } return true; } public static LayoutResult Arrange(IReadOnlyList items, int width, int requestedRows, int quickCount) { if (width < 7 || requestedRows < 1 || quickCount < 0 || quickCount > 6) { throw new ArgumentOutOfRangeException(); } LayoutResult layoutResult = new LayoutResult { NormalRows = requestedRows }; HashSet hashSet = new HashSet(); List list = new List(); foreach (LayoutItem item in items.OrderByDescending((LayoutItem x) => x.Equipped)) { LayoutItem layoutItem = new LayoutItem { Id = item.Id, X = item.X, Y = item.Y, Slot = item.Slot, Equipped = item.Equipped }; if (((layoutItem.Slot >= 0 && layoutItem.Slot < 5 + quickCount) || IsEquipment(layoutItem.Slot)) && hashSet.Add(layoutItem.Slot)) { layoutResult.Items.Add(layoutItem); continue; } layoutItem.Slot = -1; list.Add(layoutItem); } layoutResult.NormalRows = Math.Max(layoutResult.NormalRows, (list.Count + width - 1) / width); HashSet hashSet2 = new HashSet(); List list2 = new List(); foreach (LayoutItem item2 in list) { if (item2.X >= 0 && item2.X < width && item2.Y >= 0 && item2.Y < layoutResult.NormalRows && hashSet2.Add((long)item2.Y * (long)width + item2.X)) { layoutResult.Items.Add(item2); } else { list2.Add(item2); } } long num = 0L; foreach (LayoutItem item3 in list2) { for (; hashSet2.Contains(num); num++) { } hashSet2.Add(num); item3.X = (int)(num % width); item3.Y = (int)(num / width); layoutResult.Items.Add(item3); } foreach (LayoutItem item4 in layoutResult.Items.Where((LayoutItem x) => x.Slot >= 0)) { item4.X = ((item4.Slot == 12) ? 6 : ((item4.Slot == 11) ? 5 : ((item4.Slot < 5) ? item4.Slot : (item4.Slot - 5)))); item4.Y = layoutResult.NormalRows + ((!IsEquipment(item4.Slot)) ? 1 : 0); } return layoutResult; } } internal sealed class ModDataLayout { internal const string ExampleQuest = "{\n \"Quests\": [\n {\n \"ID\": \"gearandstorage_first_supplies\",\n \"Title\": \"První zásoby\",\n \"Goal\": \"Po přijetí questu získej dřevo a kámen na začátek výpravy.\",\n \"Rarity\": \"Common\",\n \"PreReqID\": null,\n \"KillReqs\": [],\n \"GatherReqs\": [\n { \"Prefab\": \"Wood\", \"Amount\": 10 },\n { \"Prefab\": \"Stone\", \"Amount\": 5 }\n ],\n \"RewardItems\": [{ \"Prefab\": \"Coins\", \"Amount\": 10 }],\n \"SkillRewards\": []\n }\n ]\n}"; internal string Root { get; } internal string SettingsPath => Path.Combine(Root, "GearAndStorage.cfg"); internal string Quests => Path.Combine(Root, "Quests"); internal string Progress => Path.Combine(Root, "Progress"); internal ModDataLayout(string configRoot) { Root = Path.Combine(configRoot, "GearAndStorage"); } internal void Ensure() { bool num = !Directory.Exists(Quests); Directory.CreateDirectory(Root); Directory.CreateDirectory(Quests); Directory.CreateDirectory(Progress); if (num) { File.WriteAllText(Path.Combine(Quests, "quest_example.json"), "{\n \"Quests\": [\n {\n \"ID\": \"gearandstorage_first_supplies\",\n \"Title\": \"První zásoby\",\n \"Goal\": \"Po přijetí questu získej dřevo a kámen na začátek výpravy.\",\n \"Rarity\": \"Common\",\n \"PreReqID\": null,\n \"KillReqs\": [],\n \"GatherReqs\": [\n { \"Prefab\": \"Wood\", \"Amount\": 10 },\n { \"Prefab\": \"Stone\", \"Amount\": 5 }\n ],\n \"RewardItems\": [{ \"Prefab\": \"Coins\", \"Amount\": 10 }],\n \"SkillRewards\": []\n }\n ]\n}"); } } } public static class PaymentPlanner { public static int[] Allocate(IReadOnlyList available, int amount) { if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } int[] array = new int[available.Count]; for (int i = 0; i < available.Count; i++) { if (available[i] < 0) { throw new ArgumentOutOfRangeException("available"); } amount -= (array[i] = Math.Min(amount, available[i])); } if (amount != 0) { return null; } return array; } } internal sealed class QuestLineProgress { internal const int PageSize = 8; internal readonly List Pending = new List(); internal readonly List Completed = new List(); internal QuestLineProgress(QuestLine line, QuestProgress progress) { if (line == null) { return; } foreach (QuestDefinition quest in line.Quests) { ((progress != null && progress.CompletedQuestIDs?.Contains(quest.ID) == true) ? Completed : Pending).Add(quest); } } internal QuestDefinition[] Matches(string search) { return Pending.Where((QuestDefinition q) => string.IsNullOrEmpty(search) || (q.Title + " " + q.ID).IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0).ToArray(); } internal int PageOf(QuestDefinition quest) { return Math.Max(0, Pending.IndexOf(quest)) / 8; } } public sealed class QuestList { public List Quests = new List(); } public sealed class QuestDefinition { public string ID; public string Title; public string Goal; public string PreReqID; public string SourceFile; public string Rarity = "Common"; public List KillReqs = new List(); public List GatherReqs = new List(); public List RewardItems = new List(); public List SkillRewards = new List(); } public sealed class QuestObjective { public string Prefab; public int Amount; } public sealed class QuestReward { public string Prefab; public int Amount = 1; } public sealed class QuestSkill { public string Skill; public float Amount; } public sealed class QuestProgress { public HashSet CompletedQuestIDs = new HashSet(); public HashSet AcceptedQuestIDs = new HashSet(); public HashSet TrackedQuestIDs = new HashSet(); public Dictionary KillCounts = new Dictionary(); public Dictionary GatherCounts = new Dictionary(); } public sealed class QuestBook { public readonly QuestProgress Progress; public readonly List Definitions; public QuestDefinition Active => Definitions.FirstOrDefault((QuestDefinition q) => Progress.AcceptedQuestIDs.Contains(q.ID)); public bool Ready { get { if (Active != null && Active.KillReqs.All((QuestObjective o) => Count(Active, o, kill: true) >= o.Amount)) { return Active.GatherReqs.All((QuestObjective o) => Count(Active, o, kill: false) >= o.Amount); } return false; } } public QuestBook(IEnumerable definitions, QuestProgress progress) { Definitions = definitions.ToList(); Progress = progress ?? new QuestProgress(); QuestProgress progress2 = Progress; if (progress2.CompletedQuestIDs == null) { progress2.CompletedQuestIDs = new HashSet(); } progress2 = Progress; if (progress2.AcceptedQuestIDs == null) { progress2.AcceptedQuestIDs = new HashSet(); } progress2 = Progress; if (progress2.TrackedQuestIDs == null) { progress2.TrackedQuestIDs = new HashSet(); } Progress.KillCounts = NormalizeCounts(Progress.KillCounts); Progress.GatherCounts = NormalizeCounts(Progress.GatherCounts); string text = (from id in Progress.AcceptedQuestIDs where !string.IsNullOrWhiteSpace(id) && !Progress.CompletedQuestIDs.Contains(id) orderby Progress.TrackedQuestIDs.Contains(id) descending select id).ThenBy((string id) => id, StringComparer.Ordinal).FirstOrDefault(); Progress.AcceptedQuestIDs.Clear(); Progress.TrackedQuestIDs.Clear(); if (text != null) { Progress.AcceptedQuestIDs.Add(text); Progress.TrackedQuestIDs.Add(text); } } private static Dictionary NormalizeCounts(Dictionary counts) { return (counts ?? new Dictionary()).GroupBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase).ToDictionary>, string, int>((IGrouping> g) => g.Key, (IGrouping> g) => Math.Max(0, g.Max((KeyValuePair p) => p.Value)), StringComparer.OrdinalIgnoreCase); } public bool Available(QuestDefinition q) { if (!Progress.CompletedQuestIDs.Contains(q.ID)) { if (!string.IsNullOrEmpty(q.PreReqID)) { return Progress.CompletedQuestIDs.Contains(q.PreReqID); } return true; } return false; } public bool Accept(string id) { QuestDefinition questDefinition = Definitions.FirstOrDefault((QuestDefinition x) => x.ID == id); if (questDefinition == null || Progress.AcceptedQuestIDs.Count != 0 || !Available(questDefinition)) { return false; } Progress.AcceptedQuestIDs.Add(id); Progress.TrackedQuestIDs.Add(id); return true; } public void Abandon() { foreach (string id in Progress.AcceptedQuestIDs) { string[] array = Progress.KillCounts.Keys.Where((string k) => k.StartsWith(id + ":", StringComparison.Ordinal)).ToArray(); foreach (string key in array) { Progress.KillCounts.Remove(key); } array = Progress.GatherCounts.Keys.Where((string k) => k.StartsWith(id + ":", StringComparison.Ordinal)).ToArray(); foreach (string key2 in array) { Progress.GatherCounts.Remove(key2); } } Progress.AcceptedQuestIDs.Clear(); Progress.TrackedQuestIDs.Clear(); } public int Count(QuestDefinition q, QuestObjective objective, bool kill) { if (!(kill ? Progress.KillCounts : Progress.GatherCounts).TryGetValue(q.ID + ":" + objective.Prefab, out var value)) { return 0; } return Math.Max(0, Math.Min(objective.Amount, value)); } public bool Credit(string prefab, int amount, bool kill) { QuestDefinition active = Active; if (active == null || amount <= 0) { return false; } QuestObjective questObjective = (kill ? active.KillReqs : active.GatherReqs).FirstOrDefault((QuestObjective o) => string.Equals(o.Prefab, prefab, StringComparison.OrdinalIgnoreCase)); if (questObjective == null) { return false; } Dictionary obj = (kill ? Progress.KillCounts : Progress.GatherCounts); int num = Count(active, questObjective, kill); int num2 = (int)Math.Min(questObjective.Amount, (long)num + (long)amount); obj[active.ID + ":" + questObjective.Prefab] = num2; return num2 != num; } public bool Complete() { if (!Ready) { return false; } Progress.CompletedQuestIDs.Add(Active.ID); Progress.AcceptedQuestIDs.Clear(); Progress.TrackedQuestIDs.Clear(); return true; } } internal sealed class QuestLine { internal string ID; internal string Title; internal List Quests; } internal static class QuestNavigation { internal static List ForJournal(IReadOnlyList quests) { return (from @group in Build(quests).SelectMany((QuestLine line) => line.Quests.Select((QuestDefinition q) => new { Quest = q, Key = (string.IsNullOrEmpty(q.SourceFile) ? ("line:" + line.ID) : ("file:" + q.SourceFile)), Title = (string.IsNullOrEmpty(q.SourceFile) ? line.Title : FileTitle(q.SourceFile)) })).GroupBy(entry => entry.Key, StringComparer.Ordinal) select new QuestLine { ID = @group.Key, Title = @group.First().Title, Quests = @group.Select(entry => entry.Quest).ToList() }).ToList(); } private static string FileTitle(string source) { string text = (source.StartsWith("quest_", StringComparison.OrdinalIgnoreCase) ? source.Substring(6) : (source.StartsWith("quests_", StringComparison.OrdinalIgnoreCase) ? source.Substring(7) : source)); if (text.IndexOf('_') == 1) { text = text.Substring(2); } return text.ToLowerInvariant() switch { "tutorial_cz" => "Začátky", "default" => "Další výpravy", "meadows" => "Louky", "blackforest" => "Černý les", "swamp" => "Bažiny", "mountain" => "Hory", "plains" => "Pláně", "mistlands" => "Mlžné země", "ashlands" => "Popelavé země", "deepnorth" => "Hluboký sever", _ => text.Replace('_', ' '), }; } internal static List Build(IReadOnlyList quests) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); for (int i = 0; i < quests.Count; i++) { dictionary[quests[i].ID] = i; } int[] parents = Enumerable.Range(0, quests.Count).ToArray(); for (int j = 0; j < quests.Count; j++) { if (!string.IsNullOrEmpty(quests[j].PreReqID) && dictionary.TryGetValue(quests[j].PreReqID, out var value)) { parents[Root(j)] = Root(value); } } List list = new List(); foreach (IGrouping item in from g in Enumerable.Range(0, quests.Count).GroupBy(Root) orderby g.Min() select g) { int[] array = item.ToArray(); Dictionary> dictionary2 = array.ToDictionary((int result) => result, (int _) => new List()); Dictionary degree = array.ToDictionary((int result) => result, (int _) => 0); int[] array2 = array; foreach (int num2 in array2) { if (!string.IsNullOrEmpty(quests[num2].PreReqID) && dictionary.TryGetValue(quests[num2].PreReqID, out var value2)) { degree[num2]++; dictionary2[value2].Add(num2); } } SortedSet sortedSet = new SortedSet(array.Where((int key) => degree[key] == 0)); List list2 = new List(); while (sortedSet.Count > 0) { int min = sortedSet.Min; sortedSet.Remove(min); list2.Add(min); foreach (int item2 in dictionary2[min]) { if (--degree[item2] == 0) { sortedSet.Add(item2); } } } HashSet visited = new HashSet(list2); list2.AddRange(array.Where((int item) => !visited.Contains(item))); QuestDefinition questDefinition = quests[list2[0]]; list.Add(new QuestLine { ID = questDefinition.ID, Title = questDefinition.Title, Quests = list2.Select((int index) => quests[index]).ToList() }); } return list; int Root(int num3) { while (parents[num3] != num3) { parents[num3] = parents[parents[num3]]; num3 = parents[num3]; } return num3; } } } internal static class StackPolicy { internal static int Limit(int original, int increasePercent) { if (original <= 1) { return original; } int num = Math.Max(0, Math.Min(500, increasePercent)); return (int)Math.Min(65535L, ((long)original * (long)(100 + num) + 99) / 100); } internal static int LoadedAmount(int saved, int limit) { if (limit <= 1) { return Math.Min(saved, limit); } return saved; } } internal sealed class StackBaseline { private readonly HashSet appliedValues = new HashSet(); internal int Original { get; private set; } internal int Applied { get; private set; } internal StackBaseline(int original) { Original = (Applied = original); appliedValues.Add(original); } internal int Update(int current, int percent) { if (current != Applied) { Original = current; appliedValues.Clear(); appliedValues.Add(current); } Applied = StackPolicy.Limit(Original, percent); appliedValues.Add(Applied); return Applied; } internal int OriginalForCopy(int current) { if (!appliedValues.Contains(current)) { return current; } return Original; } } internal sealed class StationNode { internal float X; internal float Z; internal string Type; } internal sealed class StationGraph { internal readonly StationNode[] Nodes; internal readonly float[] Radius; private readonly int[] parent; private readonly Dictionary> types = new Dictionary>(); private int Root(int i) { while (parent[i] != i) { parent[i] = parent[parent[i]]; i = parent[i]; } return i; } private bool Join(int a, int b) { a = Root(a); b = Root(b); if (a == b) { return false; } parent[b] = a; return true; } private static float Distance2(StationNode a, StationNode b) { float num = a.X - b.X; float num2 = a.Z - b.Z; return num * num + num2 * num2; } internal StationGraph(StationNode[] nodes, float basic, float expanded) { Nodes = nodes; Radius = new float[nodes.Length]; parent = new int[nodes.Length]; for (int i = 0; i < nodes.Length; i++) { parent[i] = i; Radius[i] = basic; } float num = Math.Max(basic, expanded); Dictionary<(int, int), List> dictionary = new Dictionary<(int, int), List>(); for (int j = 0; j < nodes.Length; j++) { (int, int) key = ((int)Math.Floor(nodes[j].X / num), (int)Math.Floor(nodes[j].Z / num)); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List()); } value.Add(j); } List[] array = new List[nodes.Length]; for (int k = 0; k < nodes.Length; k++) { array[k] = new List(); if (nodes[k].Type != "piece_workbench") { continue; } int num2 = (int)Math.Floor(nodes[k].X / num); int num3 = (int)Math.Floor(nodes[k].Z / num); for (int l = -1; l <= 1; l++) { for (int m = -1; m <= 1; m++) { if (!dictionary.TryGetValue((num2 + l, num3 + m), out var value2)) { continue; } foreach (int item in value2) { if (Distance2(nodes[k], nodes[item]) <= num * num) { array[k].Add(item); } } } } } bool flag; do { flag = false; for (int n = 0; n < nodes.Length; n++) { if (!(nodes[n].Type == "piece_workbench")) { continue; } foreach (int item2 in array[n]) { if (item2 > n && nodes[item2].Type == "piece_workbench" && Distance2(nodes[n], nodes[item2]) <= Math.Max(Radius[n], Radius[item2]) * Math.Max(Radius[n], Radius[item2])) { flag |= Join(n, item2); } } } HashSet hashSet = new HashSet(); for (int num4 = 0; num4 < nodes.Length; num4++) { if (!(nodes[num4].Type == "piece_workbench")) { continue; } foreach (int item3 in array[num4]) { if (nodes[item3].Type == "piece_stonecutter" && Distance2(nodes[num4], nodes[item3]) <= Radius[num4] * Radius[num4]) { hashSet.Add(Root(num4)); break; } } } for (int num5 = 0; num5 < nodes.Length; num5++) { if (nodes[num5].Type == "piece_workbench" && hashSet.Contains(Root(num5)) && Radius[num5] < expanded) { Radius[num5] = expanded; flag = true; } } } while (flag); for (int num6 = 0; num6 < nodes.Length; num6++) { if (!(nodes[num6].Type == "piece_workbench")) { continue; } int key2 = Root(num6); if (!types.TryGetValue(key2, out var value3)) { value3 = (types[key2] = new HashSet()); } value3.Add("piece_workbench"); foreach (int item4 in array[num6]) { if (Distance2(nodes[num6], nodes[item4]) <= Radius[num6] * Radius[num6]) { value3.Add(nodes[item4].Type); } } } } internal bool Allows(string type, float x, float z) { for (int i = 0; i < Nodes.Length; i++) { float num = Nodes[i].X - x; float num2 = Nodes[i].Z - z; if (!(num * num + num2 * num2 > Radius[i] * Radius[i]) && (Nodes[i].Type == type || (Nodes[i].Type == "piece_workbench" && types[Root(i)].Contains(type)))) { return true; } } return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }