Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Safe Pouch v0.7.2
BepInEx/plugins/SafePouch/SafePouch.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SafePouch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.7.2.0")] [assembly: AssemblyInformationalVersion("0.7.2")] [assembly: AssemblyProduct("SafePouch")] [assembly: AssemblyTitle("SafePouch")] [assembly: AssemblyVersion("0.7.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SafePouch { [BepInPlugin("local.valheim.safepouch", "Safe Pouch", "0.7.2")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "Load")] private static class LoadPatch { private static void Postfix(Player __instance) { Instance._states.Remove(__instance); } } [HarmonyPatch(typeof(Player), "Save")] private static class SavePatch { private static void Prefix(Player __instance) { if (Instance._states.TryGetValue(__instance, out PouchState value)) { value.Store(); } } } [HarmonyPatch(typeof(Inventory), "GetTotalWeight")] private static class WeightPatch { private static void Postfix(Inventory __instance, ref float __result) { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer) && __instance == ((Humanoid)localPlayer).GetInventory()) { __result += Instance.State(localPlayer).Inventory.GetAllItems().Sum((ItemData i) => i.GetWeight(-1)) * ((float)Instance.EffectiveWeightPercent / 100f); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class SelectedPatch { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, Vector2i pos) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) PouchState current = Current; if (current == null || Drag.Inventory(__instance) != current.Inventory) { return true; } Inventory inventory = grid.GetInventory(); if (inventory != ((Humanoid)Player.m_localPlayer).GetInventory()) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Move pouch items into your inventory first.", 0, (Sprite)null, false); return false; } ItemData val = Drag.Item(__instance); if (val != null) { current.Transfer(current.Inventory, inventory, val, Drag.Amount(__instance), pos.x, pos.y); } Drag.Clear(__instance); return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] private static class ContainerUpdatePatch { private static void Prefix(ref Inventory? ___m_dragInventory, out Inventory? __state) { __state = null; PouchState current = Current; if (current != null && ___m_dragInventory == current.Inventory) { __state = ___m_dragInventory; ___m_dragInventory = null; } } private static void Postfix(ref Inventory? ___m_dragInventory, ItemData? ___m_dragItem, Inventory? __state) { if (__state != null && ___m_dragItem != null && ___m_dragInventory == null) { ___m_dragInventory = __state; } } } [HarmonyPatch(typeof(InventoryGui), "OnDropOutside")] private static class OutsidePatch { private static bool Prepare() { return AccessTools.Method(typeof(InventoryGui), "OnDropOutside", (Type[])null, (Type[])null) != null; } private static bool Prefix(InventoryGui __instance) { PouchState current = Current; if (current == null || Drag.Inventory(__instance) != current.Inventory) { return true; } Drag.Clear(__instance); return false; } } public const string Id = "local.valheim.safepouch"; internal static Plugin Instance; private Harmony _harmony; private PouchUi? _ui; private bool _uiFailed; internal ConfigEntry<float> PouchGap; internal ConfigEntry<float> VerticalOffset; internal ConfigEntry<int> WeightPercent; internal ConfigEntry<int> SlotCount; internal ConfigEntry<int> RegularStackLimit; internal ConfigEntry<int> PortalRestrictedStackLimit; internal ConfigEntry<bool> BlockPortalWithRestrictedItems; internal PouchConfigSync Sync; private readonly ConditionalWeakTable<Player, PouchState> _states = new ConditionalWeakTable<Player, PouchState>(); internal int EffectiveSlotCount => Sync.Effective.Slots; internal int EffectiveWeightPercent => Sync.Effective.WeightPercent; internal static PouchState? Current { get { if (!Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return null; } return Instance.State(Player.m_localPlayer); } } private void Awake() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown Instance = this; SlotCount = ((BaseUnityPlugin)this).Config.Bind<int>("Pouch", "SlotCount", 4, new ConfigDescription("Number of pouch slots (1-28), wrapping after seven per row. Servers with this mod override this setting while connected. Occupied slots beyond a reduced count remain withdrawal-only until emptied.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 28), Array.Empty<object>())); WeightPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Pouch", "WeightPercent", 100, new ConfigDescription("Percentage of normal item weight counted while inside the pouch: 0 = weightless, 50 = half weight, 100 = normal weight. Servers with this mod override this setting while connected. Withdrawn items always use normal weight.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); RegularStackLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Pouch", "RegularStackLimit", 99999, new ConfigDescription("Maximum items per pouch slot for teleportable items that normally stack (1-99999). Non-stackable gear stays one per slot. Servers override this setting while connected. Existing larger stacks are preserved for withdrawal.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 99999), Array.Empty<object>())); PortalRestrictedStackLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Pouch", "PortalRestrictedStackLimit", 1, new ConfigDescription("Maximum items per pouch slot for portal-restricted items that normally stack (1-99999). Non-stackable gear stays one per slot. Servers override this setting while connected. Existing larger stacks are preserved for withdrawal.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 99999), Array.Empty<object>())); BlockPortalWithRestrictedItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Pouch", "BlockPortalWithRestrictedItems", false, "Apply normal portal restrictions to pouch contents. False lets pouch items pass through portals; true blocks ordinary portals when restricted items are in the pouch. Portals and world settings that allow all items still work. Servers override this setting while connected."); PouchGap = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "Gap", 8f, new ConfigDescription("Space below the visible inventory panel in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>())); VerticalOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "VerticalOffset", 0f, new ConfigDescription("Additional pouch offset in UI units. Positive moves down; negative moves up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), Array.Empty<object>())); Sync = new PouchConfigSync(() => new PouchPolicy(SlotCount.Value, WeightPercent.Value, RegularStackLimit.Value, PortalRestrictedStackLimit.Value, BlockPortalWithRestrictedItems.Value), delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }); SlotCount.SettingChanged += OnPouchSettingsChanged; WeightPercent.SettingChanged += OnPouchSettingsChanged; RegularStackLimit.SettingChanged += OnPouchSettingsChanged; PortalRestrictedStackLimit.SettingChanged += OnPouchSettingsChanged; BlockPortalWithRestrictedItems.SettingChanged += OnPouchSettingsChanged; _harmony = new Harmony("local.valheim.safepouch"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Safe Pouch 0.7.2 loaded: personal pouch with optional server-authoritative settings."); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Pouch config: {((BaseUnityPlugin)this).Config.ConfigFilePath} ({SlotCount.Value} slots, {WeightPercent.Value}% weight, regular stack limit {RegularStackLimit.Value}, portal-restricted stack limit {PortalRestrictedStackLimit.Value}, block portals with restricted items {BlockPortalWithRestrictedItems.Value})."); } internal PouchState State(Player player) { PouchPolicy policy = Sync.Effective; PouchState value = _states.GetValue(player, (Player p) => new PouchState(p, policy.Slots, policy.RegularStackLimit, policy.PortalRestrictedStackLimit)); value.SetSlotCount(policy.Slots); value.SetStackLimits(policy.RegularStackLimit, policy.PortalRestrictedStackLimit); return value; } private void OnPouchSettingsChanged(object sender, EventArgs args) { Sync.SettingsChanged(); } private void Update() { Sync.Tick(ZNet.instance); } internal void Report(Exception error) { ((BaseUnityPlugin)this).Logger.LogError((object)error); } internal static void InventoryChanged(Inventory inventory) { AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null).Invoke(inventory, new object[2] { false, false }); } private void LateUpdate() { if (_uiFailed || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)InventoryGui.instance)) { return; } try { PouchState pouchState = State(Player.m_localPlayer); pouchState.RestoreEquipment(); if (_ui == null || !_ui.Alive || _ui.SlotCount != pouchState.VisibleSlots) { _ui?.Destroy(); _ui = new PouchUi(InventoryGui.instance, pouchState.VisibleSlots); } _ui.Refresh(pouchState); } catch (Exception error) { _uiFailed = true; _ui?.Destroy(); Report(error); ((BaseUnityPlugin)this).Logger.LogError((object)"Pouch UI disabled for this session. Saved contents are retained."); } } private void OnDestroy() { SlotCount.SettingChanged -= OnPouchSettingsChanged; WeightPercent.SettingChanged -= OnPouchSettingsChanged; RegularStackLimit.SettingChanged -= OnPouchSettingsChanged; PortalRestrictedStackLimit.SettingChanged -= OnPouchSettingsChanged; BlockPortalWithRestrictedItems.SettingChanged -= OnPouchSettingsChanged; Sync?.Reset(); _ui?.Destroy(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class Drag { internal static ItemData? Item(InventoryGui gui) { return Traverse.Create((object)gui).Field("m_dragItem").GetValue<ItemData>(); } internal static Inventory? Inventory(InventoryGui gui) { return Traverse.Create((object)gui).Field("m_dragInventory").GetValue<Inventory>(); } internal static int Amount(InventoryGui gui) { return Traverse.Create((object)gui).Field("m_dragAmount").GetValue<int>(); } internal static void Set(InventoryGui gui, ItemData? item, Inventory? inventory, int amount) { AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null).Invoke(gui, new object[3] { item, inventory, amount }); } internal static void Clear(InventoryGui gui) { Set(gui, null, null, 1); } } internal sealed class PouchConfigSync { [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class NewConnectionPatch { private static void Prefix(ZNet __instance, ZNetPeer peer) { Plugin.Instance.Sync.RegisterPeer(__instance, peer); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class PeerReadyPatch { private static void Postfix(ZNet __instance, ZRpc rpc) { Plugin.Instance.Sync.PeerReady(__instance, rpc); } } [HarmonyPatch(typeof(ZNet), "Disconnect")] private static class DisconnectPatch { private static void Prefix(ZNet __instance, ZNetPeer peer) { Plugin.Instance.Sync.Disconnect(__instance, peer); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] private static class ShutdownPatch { private static void Prefix(ZNet __instance) { Plugin.Instance.Sync.Shutdown(__instance); } } private const string ConfigRpc = "local.valheim.safepouch.Config.v1"; private const string AckRpc = "local.valheim.safepouch.ConfigAck.v1"; private readonly Func<PouchPolicy> _localPolicy; private readonly Action<string> _log; private readonly HashSet<ZRpc> _subscribers = new HashSet<ZRpc>(); private ZNet? _session; private ZRpc? _authority; private PouchPolicy? _remotePolicy; private ZRpc? _pendingServer; private PouchPolicy? _pendingPolicy; private volatile bool _broadcastPending; internal PouchPolicy Effective => _remotePolicy ?? _localPolicy(); internal bool ServerControlled => _remotePolicy.HasValue; internal PouchConfigSync(Func<PouchPolicy> localPolicy, Action<string> log) { _localPolicy = localPolicy; _log = log; } internal void SettingsChanged() { _broadcastPending = true; } internal void RegisterPeer(ZNet net, ZNetPeer peer) { if (_session != net) { Reset(); _session = net; } if (net.IsServer()) { peer.m_rpc.Register<int>("local.valheim.safepouch.ConfigAck.v1", (Action<ZRpc, int>)ReceiveAck); return; } if (peer.m_server) { ClearRemote(); } peer.m_rpc.Register<ZPackage>("local.valheim.safepouch.Config.v1", (Action<ZRpc, ZPackage>)ReceiveConfig); } internal void PeerReady(ZNet net, ZRpc rpc) { if (net == _session && net.IsServer() && ReadyPeer(net, rpc) != null) { PouchPolicy policy = _localPolicy(); _log("Offering server pouch settings: " + Describe(policy) + "."); SendPolicy(rpc); } } private static ZNetPeer? ReadyPeer(ZNet net, ZRpc rpc) { return net.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc && p.IsReady()); } private void SendPolicy(ZRpc rpc) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown PouchPolicy pouchPolicy = _localPolicy(); ZPackage val = new ZPackage(); val.Write(3); val.Write(pouchPolicy.Slots); val.Write(pouchPolicy.WeightPercent); val.Write(pouchPolicy.RegularStackLimit); val.Write(pouchPolicy.PortalRestrictedStackLimit); val.Write(pouchPolicy.BlockPortalWithRestrictedItems ? 1 : 0); rpc.Invoke("local.valheim.safepouch.Config.v1", new object[1] { val }); } private void ReceiveAck(ZRpc rpc, int protocol) { if (Object.op_Implicit((Object)(object)_session) && _session.IsServer() && protocol == 3 && ReadyPeer(_session, rpc) != null && _subscribers.Add(rpc)) { _log("Client acknowledged server pouch settings."); } } private void ReceiveConfig(ZRpc rpc, ZPackage package) { if (!Object.op_Implicit((Object)(object)_session) || _session.IsServer() || !_session.GetPeers().Exists((ZNetPeer p) => p.m_rpc == rpc && p.m_server)) { return; } try { if (package.Size() != 24 || package.ReadInt() != 3) { throw new FormatException("Unsupported pouch config packet."); } int slots = package.ReadInt(); int weightPercent = package.ReadInt(); int regularStackLimit = package.ReadInt(); int portalRestrictedStackLimit = package.ReadInt(); int num = package.ReadInt(); if (num != 0 && num != 1) { throw new FormatException("Invalid portal restriction flag."); } PouchPolicy pouchPolicy = new PouchPolicy(slots, weightPercent, regularStackLimit, portalRestrictedStackLimit, num == 1); if (rpc == _session.GetServerRPC() && ReadyPeer(_session, rpc) != null) { ApplyPolicy(rpc, pouchPolicy); return; } _pendingServer = rpc; _pendingPolicy = pouchPolicy; _log("Received server pouch settings; waiting for login to complete."); } catch (Exception ex) { _log("Ignored invalid server pouch settings: " + ex.Message); } } private void ApplyPolicy(ZRpc rpc, PouchPolicy policy) { bool num = !_remotePolicy.HasValue || _remotePolicy.Value.Slots != policy.Slots || _remotePolicy.Value.WeightPercent != policy.WeightPercent || _remotePolicy.Value.RegularStackLimit != policy.RegularStackLimit || _remotePolicy.Value.PortalRestrictedStackLimit != policy.PortalRestrictedStackLimit || _remotePolicy.Value.BlockPortalWithRestrictedItems != policy.BlockPortalWithRestrictedItems; _pendingServer = null; _pendingPolicy = null; _authority = rpc; _remotePolicy = policy; if (num) { _log("Server pouch settings applied: " + Describe(policy) + ". Local config file preserved."); } rpc.Invoke("local.valheim.safepouch.ConfigAck.v1", new object[1] { 3 }); } private static string Describe(PouchPolicy policy) { return $"{policy.Slots} slots, {policy.WeightPercent}% weight, regular stack limit {policy.RegularStackLimit}, portal-restricted stack limit {policy.PortalRestrictedStackLimit}, block portals with restricted items {policy.BlockPortalWithRestrictedItems}"; } internal void Tick(ZNet? net) { if (!Object.op_Implicit((Object)(object)net) || net != _session) { Reset(); _session = net; } if (!Object.op_Implicit((Object)(object)net)) { return; } if (_authority != null && _authority != net.GetServerRPC()) { ClearRemote(); } if (_pendingServer != null && _pendingPolicy.HasValue) { if (!net.GetPeers().Exists((ZNetPeer p) => p.m_rpc == _pendingServer && p.m_server)) { _pendingServer = null; _pendingPolicy = null; } else if (!net.IsServer() && _pendingServer == net.GetServerRPC() && ReadyPeer(net, _pendingServer) != null) { ApplyPolicy(_pendingServer, _pendingPolicy.Value); } } if (!_broadcastPending) { return; } _broadcastPending = false; if (!net.IsServer()) { return; } foreach (ZNetPeer peer in net.GetPeers()) { if (peer.IsReady() && _subscribers.Contains(peer.m_rpc)) { SendPolicy(peer.m_rpc); } } } internal void Disconnect(ZNet net, ZNetPeer peer) { if (net == _session) { _subscribers.Remove(peer.m_rpc); if (peer.m_rpc == _authority || peer.m_rpc == _pendingServer) { ClearRemote(); } } } internal void Shutdown(ZNet net) { if (net == _session) { Reset(); } } private void ClearRemote() { if (_remotePolicy.HasValue) { _log("Server pouch settings released; local settings restored."); } _authority = null; _remotePolicy = null; _pendingServer = null; _pendingPolicy = null; } internal void Reset() { ClearRemote(); _session = null; _subscribers.Clear(); _broadcastPending = false; } } internal static class PouchEquipment { [HarmonyPatch(typeof(FejdStartup), "SetupCharacterPreview")] private static class PreviewEquipmentPatch { private static void Postfix(FejdStartup __instance) { Player previewPlayer = __instance.GetPreviewPlayer(); if (!Object.op_Implicit((Object)(object)previewPlayer)) { return; } Inventory previewInventory = _previewInventory; PouchState previewState = _previewState; try { _previewInventory = ((Humanoid)previewPlayer).GetInventory(); _previewState = new PouchState(previewPlayer, Plugin.Instance.SlotCount.Value); _previewState.RestoreEquipment(); } catch (Exception error) { Plugin.Instance.Report(error); } finally { _previewInventory = previewInventory; _previewState = previewState; } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] private static class EquipOwnershipPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(Inventory), "ContainsItem", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(PouchEquipment), "OwnsEquipment", (Type[])null, (Type[])null); } yield return instruction; } } } [HarmonyPatch] private static class EquipmentEffectsPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Humanoid), "UpdateEquipment", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Player), "AddAdrenaline", (Type[])null, (Type[])null); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(Inventory), "GetAllItems", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(PouchEquipment), "EquipmentItems", (Type[])null, (Type[])null); } yield return instruction; } } } [HarmonyPatch] private static class AttackAmmoOwnershipPatch { private static IEnumerable<MethodBase> TargetMethods() { string[] array = new string[4] { "FindAmmo", "EquipAmmoItem", "HaveAmmo", "UseAmmo" }; foreach (string text in array) { yield return AccessTools.Method(typeof(Attack), text, (Type[])null, (Type[])null); } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(Inventory), "ContainsItem", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(PouchInventory), "ContainsForUse", (Type[])null, (Type[])null); } yield return instruction; } } } private static Inventory? _previewInventory; private static PouchState? _previewState; private static PouchState? For(Inventory inventory) { if (inventory == _previewInventory) { PouchState previewState = _previewState; if (previewState == null || previewState.Locked) { return null; } return _previewState; } Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || inventory != ((Humanoid)localPlayer).GetInventory()) { return null; } PouchState current = Plugin.Current; if (current == null || current.Locked) { return null; } return current; } private static bool OwnsEquipment(Inventory inventory, ItemData item) { if (!inventory.ContainsItem(item)) { if (PouchState.CanSelectEquipment(item)) { PouchState? pouchState = For(inventory); if (pouchState == null) { return false; } return pouchState.Inventory.ContainsItem(item); } return false; } return true; } private static List<ItemData> EquipmentItems(Inventory inventory) { List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); PouchState pouchState = For(inventory); if (pouchState != null) { list.AddRange(pouchState.Inventory.GetAllItems().FindAll((ItemData i) => i.m_equipped && PouchState.CanEquip(i))); } return list; } } internal static class PouchInventory { [HarmonyPatch(typeof(Inventory), "GetWornItems")] internal static class WornItemsPatch { internal static void Postfix(Inventory __instance, List<ItemData> worn) { PouchState pouchState = For(__instance); if (pouchState == null) { return; } List<ItemData> list = new List<ItemData>(); pouchState.Inventory.GetWornItems(list); foreach (ItemData item in list) { if (!worn.Contains(item)) { worn.Add(item); } } } } [HarmonyPatch(typeof(Inventory), "CountItems")] internal static class CountPatch { internal static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { PouchState pouchState = For(__instance); if (pouchState != null) { __result += pouchState.Inventory.CountItems(name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "CountItemsByName")] internal static class CountNamesPatch { internal static void Postfix(Inventory __instance, string[] names, int quality, bool matchWorldLevel, bool stacksOnly, ref int __result) { PouchState pouchState = For(__instance); if (pouchState != null) { __result += pouchState.Inventory.CountItemsByName(names, quality, matchWorldLevel, stacksOnly); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] internal static class HaveNamePatch { internal static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result) { PouchState pouchState = For(__instance); if (pouchState != null) { __result = pouchState.Inventory.HaveItem(name, matchWorldLevel); } } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(ItemType), typeof(bool) })] internal static class HaveTypePatch { internal static void Postfix(Inventory __instance, ItemType type, bool matchWorldLevel, ref bool __result) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!__result) { PouchState pouchState = For(__instance); if (pouchState != null) { __result = pouchState.Inventory.HaveItem(type, matchWorldLevel); } } } } [HarmonyPatch(typeof(Inventory), "GetItem", new Type[] { typeof(string), typeof(int), typeof(bool) })] internal static class ItemPatch { internal static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData? __result) { if (__result == null) { PouchState pouchState = For(__instance); if (pouchState != null) { __result = pouchState.Inventory.GetItem(name, quality, isPrefabName); } } } } [HarmonyPatch(typeof(Inventory), "GetAmmoItem")] internal static class AmmoPatch { internal static void Postfix(Inventory __instance, string ammoName, string matchPrefabName, ref ItemData? __result) { if (__result == null) { PouchState pouchState = For(__instance); if (pouchState != null) { __result = pouchState.Inventory.GetAmmoItem(ammoName, matchPrefabName); } } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class RemoveNamePatch { internal static void Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased, out (PouchState? Pouch, int Remaining) __state) { PouchState pouchState = For(__instance); __state = (Pouch: pouchState, Remaining: (pouchState != null) ? RemainingAfterMain(__instance, name, amount, itemQuality, worldLevelBased) : 0); } internal static void Postfix(string name, int itemQuality, bool worldLevelBased, (PouchState? Pouch, int Remaining) __state) { if (__state.Remaining > 0) { __state.Pouch?.ConsumeByName(name, __state.Remaining, itemQuality, worldLevelBased); } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class RemoveAmountPatch { internal static bool Prefix(Inventory __instance, ItemData item, int amount, ref bool __result) { PouchState pouchState = For(__instance); if (pouchState == null || !pouchState.Inventory.ContainsItem(item)) { return true; } __result = pouchState.Consume(item, amount); return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData) })] internal static class RemoveItemPatch { internal static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { return RemoveAmountPatch.Prefix(__instance, item, item?.m_stack ?? 0, ref __result); } } [HarmonyPatch(typeof(Inventory), "RemoveOneItem")] internal static class RemoveOnePatch { internal static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { return RemoveAmountPatch.Prefix(__instance, item, 1, ref __result); } } [HarmonyPatch(typeof(Inventory), "IsTeleportable")] internal static class PortalPatch { internal static void Postfix(Inventory __instance, bool allowAllItems, ref bool __result) { if (__result && Plugin.Instance.Sync.Effective.BlockPortalWithRestrictedItems) { PouchState pouchState = For(__instance); if (pouchState != null) { __result = pouchState.Inventory.IsTeleportable(allowAllItems); } } } } internal static PouchState? For(Inventory inventory) { Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || inventory != ((Humanoid)localPlayer).GetInventory()) { return null; } PouchState current = Plugin.Current; if (current == null || current.Locked) { return null; } return current; } internal static bool ContainsForUse(Inventory inventory, ItemData item) { if (!inventory.ContainsItem(item)) { PouchState? pouchState = For(inventory); if (pouchState == null) { return false; } return pouchState.Inventory.ContainsItem(item); } return true; } internal static int RemainingAfterMain(Inventory inventory, string name, int amount, int quality, bool worldLevelBased) { int num = (from i in inventory.GetAllItems() where i.m_shared.m_name == name && (quality < 0 || i.m_quality == quality) && (!worldLevelBased || i.m_worldLevel >= Game.m_worldLevel) select i).Sum((ItemData i) => i.m_stack); return Math.Max(0, amount - num); } } internal readonly struct PouchPolicy { internal const int Protocol = 3; internal int Slots { get; } internal int WeightPercent { get; } internal int RegularStackLimit { get; } internal int PortalRestrictedStackLimit { get; } internal bool BlockPortalWithRestrictedItems { get; } internal PouchPolicy(int slots, int weightPercent, int regularStackLimit = 99999, int portalRestrictedStackLimit = 1, bool blockPortalWithRestrictedItems = false) { if (slots < 1 || slots > 28 || weightPercent < 0 || weightPercent > 100) { throw new ArgumentOutOfRangeException("slots", "Invalid pouch settings."); } if (regularStackLimit < 1 || regularStackLimit > 99999 || portalRestrictedStackLimit < 1 || portalRestrictedStackLimit > 99999) { throw new ArgumentOutOfRangeException("regularStackLimit", "Invalid pouch stack limits."); } Slots = slots; WeightPercent = weightPercent; RegularStackLimit = regularStackLimit; PortalRestrictedStackLimit = portalRestrictedStackLimit; BlockPortalWithRestrictedItems = blockPortalWithRestrictedItems; } } [HarmonyPatch(typeof(InventoryGui), "RepairOneItem")] internal static class PouchRepair { internal static void Postfix() { Plugin.Current?.Store(); } } internal static class PouchSave { internal static string Encode(int[] counts, string payload) { Validate(counts); return "v3|" + string.Join(",", counts) + "|" + payload; } internal static (int[] Counts, string Payload) Decode(string data) { string[] array = data.Split('|'); if (array.Length != 3 || (array[0] != "v2" && array[0] != "v3")) { throw new FormatException("Unknown pouch save format."); } int[] array2 = array[1].Split(',').Select(int.Parse).ToArray(); if (array[0] == "v2" && array2.Length != 4) { throw new FormatException("Invalid legacy slot count."); } Validate(array2); return (Counts: array2, Payload: array[2]); } private static void Validate(int[] counts) { if (counts.Length < 1 || counts.Length > 28 || counts.Any((int c) => c < 0 || c > 99999)) { throw new FormatException("Invalid pouch stack counts."); } } } internal sealed class PouchState { private const string Key = "local.valheim.safepouch.inventory.v1"; internal readonly Inventory Inventory = new Inventory("Safe Pouch", (Sprite)null, 28, 1); private readonly Player _player; private bool _equipmentRestored; internal int ConfiguredSlots { get; private set; } internal int RegularStackLimit { get; private set; } internal int PortalRestrictedStackLimit { get; private set; } internal int VisibleSlots => Math.Min(28, Math.Max(ConfiguredSlots, (from i in Inventory.GetAllItems() select i.m_gridPos.x + 1).DefaultIfEmpty(0).Max())); internal bool Locked { get; private set; } internal void SetSlotCount(int slots) { ConfiguredSlots = Math.Max(1, Math.Min(28, slots)); } internal void SetStackLimits(int regularLimit, int restrictedLimit) { RegularStackLimit = Math.Max(1, Math.Min(99999, regularLimit)); PortalRestrictedStackLimit = Math.Max(1, Math.Min(99999, restrictedLimit)); } internal int Capacity(ItemData item) { return SlotRules.PouchCapacity(item.m_shared.m_teleportable, item.m_shared.m_maxStackSize, RegularStackLimit, PortalRestrictedStackLimit); } internal static bool CanEquip(ItemData item) { if (item.IsEquipable()) { return item.m_shared.m_maxStackSize == 1; } return false; } internal static bool CanSelectEquipment(ItemData item) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if (!CanEquip(item) && (int)item.m_shared.m_itemType != 9) { return (int)item.m_shared.m_itemType == 23; } return true; } internal void RestoreEquipment() { if (_equipmentRestored || Locked || ((Character)_player).IsDead()) { return; } _equipmentRestored = true; ItemData[] array = (from i in Inventory.GetAllItems() where i.m_equipped select i).ToArray(); foreach (ItemData val in array) { val.m_equipped = false; if (CanSelectEquipment(val)) { ((Humanoid)_player).EquipItem(val, false); } } } internal void ToggleEquipment(ItemData item) { if (!Locked && !((Character)_player).IsDead() && !((Character)_player).IsTeleporting() && Inventory.ContainsItem(item) && CanSelectEquipment(item)) { ((Humanoid)_player).RemoveEquipAction(item); if (((Humanoid)_player).IsItemEquiped(item)) { ((Humanoid)_player).UnequipItem(item, true); } else { ((Humanoid)_player).EquipItem(item, true); } Store(); } } internal bool Consume(ItemData item, int amount) { if (Locked || amount <= 0 || !Inventory.ContainsItem(item)) { return false; } amount = Math.Min(amount, item.m_stack); if (amount == item.m_stack) { ((Humanoid)_player).UnequipItem(item, false); } bool num = Inventory.RemoveItem(item, amount); if (num) { Store(); } return num; } internal void UseConsumable(ItemData item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 if (!Locked && !((Character)_player).IsDead() && !((Character)_player).IsTeleporting() && Inventory.ContainsItem(item) && (int)item.m_shared.m_itemType == 2) { ((Humanoid)_player).UseItem(Inventory, item, true); Store(); } } internal void ConsumeByName(string name, int amount, int quality, bool matchWorldLevel) { ItemData[] array = Inventory.GetAllItems().ToArray(); foreach (ItemData val in array) { if (amount <= 0) { break; } if (!(val.m_shared.m_name != name) && (quality < 0 || val.m_quality == quality) && (!matchWorldLevel || val.m_worldLevel >= Game.m_worldLevel)) { int num = Math.Min(amount, val.m_stack); if (Consume(val, num)) { amount -= num; } } } } internal PouchState(Player player, int slotCount = 4, int regularLimit = 99999, int restrictedLimit = 1) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown ConfiguredSlots = Math.Max(1, Math.Min(28, slotCount)); SetStackLimits(regularLimit, restrictedLimit); _player = player; if (!player.m_customData.TryGetValue("local.valheim.safepouch.inventory.v1", out var value)) { return; } try { int[] array = null; int num; int num2; if (value.StartsWith("v2|", StringComparison.Ordinal) || value.StartsWith("v3|", StringComparison.Ordinal)) { (int[], string) tuple = PouchSave.Decode(value); array = tuple.Item1; num = array.Count((int c) => c > 0); num2 = array.Sum(); Inventory.Load(new ZPackage(tuple.Item2)); } else { int num3 = value.IndexOf(':'); string[] array2 = value.Substring(0, num3).Split(','); num = int.Parse(array2[0]); num2 = int.Parse(array2[1]); Inventory.Load(new ZPackage(value.Substring(num3 + 1))); } List<ItemData> allItems = Inventory.GetAllItems(); int savedSlots = ((array != null) ? array.Length : 4); if (allItems.Count != num || allItems.Count > savedSlots || allItems.Select((ItemData i) => i.m_gridPos.x).Distinct().Count() != allItems.Count || allItems.Any((ItemData i) => i.m_gridPos.x < 0 || i.m_gridPos.x >= savedSlots || i.m_gridPos.y != 0)) { throw new InvalidOperationException("Pouch data could not be restored completely. Original data retained; restore missing item mods before using this pouch."); } if (array != null) { foreach (ItemData item in allItems) { if (item.m_stack != 1 || array[item.m_gridPos.x] < 1) { throw new FormatException("Invalid pouch representative item."); } item.m_stack = array[item.m_gridPos.x]; } } if (allItems.Sum((ItemData i) => i.m_stack) != num2 || allItems.Any((ItemData i) => i.m_stack < 1 || i.m_stack > SlotRules.PouchCapacity(i.m_shared.m_teleportable, i.m_shared.m_maxStackSize, 99999, 99999))) { throw new FormatException("Invalid pouch item counts. Original data retained."); } } catch (Exception error) { Locked = true; Plugin.Instance.Report(error); } } internal void Store() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown if (Locked) { return; } int[] array = new int[VisibleSlots]; Inventory val = new Inventory("Safe Pouch save", (Sprite)null, 28, 1); foreach (ItemData allItem in Inventory.GetAllItems()) { array[allItem.m_gridPos.x] = allItem.m_stack; ItemData val2 = allItem.Clone(); val2.m_stack = 1; val.GetAllItems().Add(val2); } ZPackage val3 = new ZPackage(); val.Save(val3); _player.m_customData["local.valheim.safepouch.inventory.v1"] = PouchSave.Encode(array, val3.GetBase64()); } internal int Transfer(Inventory source, Inventory target, ItemData item, int requested, int x, int y) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (Locked || ((Character)_player).IsDead() || ((Character)_player).IsTeleporting() || !source.ContainsItem(item) || x < 0 || y < 0 || x >= target.GetWidth() || y >= target.GetHeight()) { return 0; } if (target == Inventory && x >= ConfiguredSlots) { return 0; } ItemData itemAt = target.GetItemAt(x, y); if (itemAt == item) { return 0; } if (source == Inventory && target == Inventory && itemAt != null && (!itemAt.IsSameType(item) || itemAt.m_stack >= Capacity(item))) { Vector2i gridPos = item.m_gridPos; if (requested < item.m_stack || item.m_stack <= 0 || itemAt.m_stack <= 0 || gridPos.x < 0 || gridPos.x >= ConfiguredSlots || gridPos.y != 0 || Inventory.GetItemAt(gridPos.x, gridPos.y) != item || item.m_stack > Capacity(item) || itemAt.m_stack > Capacity(itemAt)) { return 0; } item.m_gridPos = new Vector2i(x, y); itemAt.m_gridPos = gridPos; Plugin.InventoryChanged(Inventory); Store(); return item.m_stack; } int maxStack = ((target == Inventory) ? Capacity(item) : item.m_shared.m_maxStackSize); int num = SlotRules.TransferAmount(restricted: false, maxStack, itemAt?.m_stack ?? 0, itemAt == null || itemAt.IsSameType(item), item.m_stack, requested); if (num == 0) { return 0; } if (source == Inventory && target == Inventory && itemAt == null && num == item.m_stack) { item.m_gridPos = new Vector2i(x, y); Plugin.InventoryChanged(Inventory); Store(); return num; } ((Humanoid)_player).RemoveEquipAction(item); if (CanEquip(item) && itemAt == null && num == item.m_stack) { source.RemoveItem(item, num); item.m_gridPos = new Vector2i(x, y); target.GetAllItems().Add(item); Plugin.InventoryChanged(target); Store(); return num; } ((Humanoid)_player).UnequipItem(item, false); if (target == Inventory) { if (itemAt == null) { ItemData val = item.Clone(); val.m_stack = num; val.m_equipped = false; val.m_gridPos = new Vector2i(x, y); target.GetAllItems().Add(val); } else { itemAt.m_stack += num; } source.RemoveItem(item, num); Store(); return num; } int stack = item.m_stack; target.MoveItemToThis(source, item, num, x, y); int result = stack - item.m_stack; Store(); return result; } internal void Withdraw(ItemData item) { Inventory inventory = ((Humanoid)_player).GetInventory(); for (int i = 0; i < 2; i++) { if (!Inventory.ContainsItem(item)) { break; } for (int j = 0; j < inventory.GetHeight(); j++) { if (!Inventory.ContainsItem(item)) { break; } for (int k = 0; k < inventory.GetWidth(); k++) { if (!Inventory.ContainsItem(item)) { break; } if (inventory.GetItemAt(k, j) == null == (i == 1)) { Transfer(Inventory, inventory, item, item.m_stack, k, j); } } } } } } internal sealed class PouchUi { private readonly InventoryGui _gui; private readonly GameObject _panel; private readonly Image _background; private Image? _inventoryBackground; private readonly InventoryElement[] _slots; private readonly TMP_Text _status; private PouchState? _state; private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4]; private readonly List<Button> _inventoryButtons = new List<Button>(); private static readonly string[] BackgroundPaths = new string[4] { "Bkg", "Background", "Darken", "selected_frame/selected (1)" }; internal int SlotCount => _slots.Length; internal bool Alive { get { if (Object.op_Implicit((Object)(object)_panel)) { return Object.op_Implicit((Object)(object)_gui); } return false; } } internal PouchUi(InventoryGui gui, int slotCount) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_0308: 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_0316: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) _slots = (InventoryElement[])(object)new InventoryElement[slotCount]; _gui = gui; float elementSpace = gui.m_playerGrid.m_elementSpace; int num = Math.Min(7, slotCount); int num2 = (slotCount + 7 - 1) / 7; float num3 = ((num == 1) ? 60 : 38); _panel = new GameObject("SafePouch", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val = (RectTransform)_panel.transform; ((Transform)val).SetParent((Transform)(object)gui.m_player, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f, 0f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(0f, -8f); val.sizeDelta = new Vector2(elementSpace * (float)num + 24f, elementSpace * (float)num2 + num3 + 40f); _background = _panel.GetComponent<Image>(); Image val3 = ((Component)gui.m_player).GetComponent<Image>(); if (!Object.op_Implicit((Object)(object)val3) || !Object.op_Implicit((Object)(object)val3.sprite)) { val3 = (from Transform t in (IEnumerable)gui.m_player where string.Equals(((Object)t).name, "Bkg", StringComparison.OrdinalIgnoreCase) || string.Equals(((Object)t).name, "Background", StringComparison.OrdinalIgnoreCase) select ((Component)t).GetComponent<Image>()).FirstOrDefault((Func<Image, bool>)((Image i) => Object.op_Implicit((Object)(object)i) && Object.op_Implicit((Object)(object)i.sprite))); } _inventoryBackground = val3; ((Graphic)_background).color = new Color(0.16f, 0.14f, 0.15f, 0.97f); ((Graphic)_background).raycastTarget = true; SyncBackgroundStyle(); MakeText((num == 1) ? "Safe\nPouch" : "Safe Pouch", new Vector2(12f, -7f), 23f, new Color(1f, 0.77f, 0.38f), gui.m_containerName, num3 - 9f); _status = MakeText("Kept on death", new Vector2(12f, (0f - elementSpace) * (float)num2 - num3 - 5f), 16f, new Color(0.85f, 0.82f, 0.74f), gui.m_recipeDecription); for (int num4 = 0; num4 < slotCount; num4++) { int slotIndex = num4; GameObject val4 = Object.Instantiate<GameObject>(gui.m_playerGrid.m_elementPrefab, (Transform)(object)val); ((Object)val4).name = "SafePouchSlot" + num4; RectTransform val5 = (RectTransform)val4.transform; ((Vector2)(ref val2))..ctor(0f, 1f); val5.anchorMax = val2; val5.anchorMin = val2; val5.pivot = new Vector2(0f, 1f); val5.anchoredPosition = new Vector2(12f + elementSpace * (float)(num4 % 7), 0f - num3 - elementSpace * (float)(num4 / 7)); InventoryElement component = val4.GetComponent<InventoryElement>(); component.Initialize(num4, 0); _slots[num4] = component; UIInputHandler componentInChildren = val4.GetComponentInChildren<UIInputHandler>(); componentInChildren.m_onLeftDown = delegate { Click(slotIndex); }; componentInChildren.m_onRightDown = delegate { Withdraw(slotIndex); }; UIDragHandler[] componentsInChildren = val4.GetComponentsInChildren<UIDragHandler>(true); for (int num5 = 0; num5 < componentsInChildren.Length; num5++) { ((Behaviour)componentsInChildren[num5]).enabled = false; } Transform obj = val4.transform.Find("binding"); TMP_Text val6 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (Object.op_Implicit((Object)(object)val6)) { ((Behaviour)val6).enabled = false; } } } private TMP_Text MakeText(string text, Vector2 position, float size, Color color, TMP_Text style, float height = 32f) { //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) //IL_002c: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_005c: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(text, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(_panel.transform, false); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0f, 1f); val2.anchorMax = val3; val2.anchorMin = val3; val2.pivot = new Vector2(0f, 1f); val2.anchoredPosition = position; val2.sizeDelta = new Vector2(((RectTransform)_panel.transform).sizeDelta.x - 24f, height); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).font = style.font; ((TMP_Text)component).fontSharedMaterial = style.fontSharedMaterial; ((TMP_Text)component).fontSize = size; ((TMP_Text)component).enableAutoSizing = true; ((TMP_Text)component).fontSizeMin = 12f; ((TMP_Text)component).fontSizeMax = size; ((TMP_Text)component).enableWordWrapping = true; ((Graphic)component).color = color; ((TMP_Text)component).text = text; ((Graphic)component).raycastTarget = false; return (TMP_Text)(object)component; } internal void Refresh(PouchState state) { //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Invalid comparison between Unknown and I4 _state = state; _panel.SetActive(InventoryGui.IsVisible() && !((Character)Player.m_localPlayer).IsDead() && !_gui.IsContainerOpen()); if (!_panel.activeInHierarchy) { return; } SyncBackgroundStyle(); UpdatePosition(); _status.text = (state.Locked ? "Pouch locked — check log" : ((state.VisibleSlots > state.ConfiguredSlots) ? "Extra slots: withdraw only" : "Kept on death")); for (int i = 0; i < _slots.Length; i++) { InventoryElement val = _slots[i]; ItemData itemAt = state.Inventory.GetItemAt(i, 0); ((Behaviour)val.m_icon).enabled = itemAt != null; if (itemAt != null) { val.m_icon.sprite = itemAt.GetIcon(); } ((Behaviour)val.m_amount).enabled = itemAt != null && itemAt.m_stack > 1; val.m_amount.text = itemAt?.m_stack.ToString() ?? ""; ((Behaviour)val.m_quality).enabled = itemAt != null && itemAt.m_shared.m_maxQuality > 1; val.m_quality.text = itemAt?.m_quality.ToString() ?? ""; ((Behaviour)val.m_equiped).enabled = itemAt != null && ((Humanoid)Player.m_localPlayer).IsItemEquiped(itemAt); ((Behaviour)val.m_queued).enabled = false; ((Behaviour)val.m_food).enabled = false; ((Behaviour)val.m_noteleport).enabled = itemAt != null && !itemAt.m_shared.m_teleportable && Plugin.Instance.Sync.Effective.BlockPortalWithRestrictedItems; val.m_selected.SetActive(itemAt != null && Drag.Item(_gui) == itemAt); bool flag = itemAt != null && itemAt.m_shared.m_useDurability && itemAt.m_durability < itemAt.GetMaxDurability(); ((Component)val.m_durability).gameObject.SetActive(flag); if (flag) { val.m_durability.SetValue((itemAt.m_durability <= 0f) ? 1f : itemAt.GetDurabilityPercentage()); if (itemAt.m_durability <= 0f) { val.m_durability.SetColor((Mathf.Sin(Time.time * 10f) > 0f) ? Color.red : Color.clear); } else { val.m_durability.ResetColor(); } } val.m_tooltip.Set(itemAt?.m_shared.m_name ?? "Safe Pouch", (i >= state.ConfiguredSlots) ? "Withdraw this item to finish reducing the pouch size." : ((itemAt == null) ? "Items here survive death." : (itemAt.GetTooltip(-1) + "\n<color=orange>Kept on death</color>" + (PouchState.CanSelectEquipment(itemAt) ? "\nShift + right-click: equip / unequip" : "") + (((int)itemAt.m_shared.m_itemType == 2) ? "\nShift + right-click: use" : "") + ((itemAt.m_stack > state.Capacity(itemAt)) ? "\nAbove the current limit: withdraw to reduce this stack." : "") + ((!itemAt.m_shared.m_teleportable && !Plugin.Instance.Sync.Effective.BlockPortalWithRestrictedItems) ? "\nCan travel through portals here." : ""))), _gui.m_playerGrid.m_tooltipAnchor, default(Vector2)); } } private void UpdatePosition() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) RectTransform player = _gui.m_player; Rect rect = player.rect; float bottom = ((Rect)(ref rect)).yMin; string[] backgroundPaths = BackgroundPaths; foreach (string text in backgroundPaths) { Transform obj = ((Transform)player).Find(text); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if (Object.op_Implicit((Object)(object)val) && ((Component)val).gameObject.activeInHierarchy) { IncludeBottom(val, ref bottom); } } ((Component)player).GetComponentsInChildren<Button>(false, _inventoryButtons); foreach (Button inventoryButton in _inventoryButtons) { if (Object.op_Implicit((Object)(object)inventoryButton) && ((Behaviour)inventoryButton).isActiveAndEnabled && !((Component)inventoryButton).transform.IsChildOf(_panel.transform)) { Transform transform = ((Component)inventoryButton).transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (Object.op_Implicit((Object)(object)val2)) { IncludeBottom(val2, ref bottom); } } } RectTransform val3 = (RectTransform)_panel.transform; float num = bottom; rect = player.rect; val3.anchoredPosition = new Vector2(0f, num - ((Rect)(ref rect)).yMin - Plugin.Instance.PouchGap.Value - Plugin.Instance.VerticalOffset.Value); } private void SyncBackgroundStyle() { //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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_00d3: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_inventoryBackground)) { Image inventoryBackground = _inventoryBackground; if ((Object)(object)((Graphic)_background).material != (Object)(object)((Graphic)inventoryBackground).material) { ((Graphic)_background).material = ((Graphic)inventoryBackground).material; } if ((Object)(object)_background.sprite != (Object)(object)inventoryBackground.sprite) { _background.sprite = inventoryBackground.sprite; } if ((Object)(object)_background.overrideSprite != (Object)(object)inventoryBackground.overrideSprite) { _background.overrideSprite = inventoryBackground.overrideSprite; } if (((Graphic)_background).color != ((Graphic)inventoryBackground).color) { ((Graphic)_background).color = ((Graphic)inventoryBackground).color; } if (_background.type != inventoryBackground.type) { _background.type = inventoryBackground.type; } if (_background.pixelsPerUnitMultiplier != inventoryBackground.pixelsPerUnitMultiplier) { _background.pixelsPerUnitMultiplier = inventoryBackground.pixelsPerUnitMultiplier; } if (_background.fillCenter != inventoryBackground.fillCenter) { _background.fillCenter = inventoryBackground.fillCenter; } if (_background.preserveAspect != inventoryBackground.preserveAspect) { _background.preserveAspect = inventoryBackground.preserveAspect; } } } private void IncludeBottom(RectTransform edge, ref float bottom) { //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_003f: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) //IL_00a7: 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) edge.GetWorldCorners(_corners); float num = float.PositiveInfinity; float num2 = float.NegativeInfinity; float num3 = float.PositiveInfinity; Vector3[] corners = _corners; foreach (Vector3 val in corners) { Vector3 val2 = ((Transform)_gui.m_player).InverseTransformPoint(val); num = Mathf.Min(num, val2.x); num2 = Mathf.Max(num2, val2.x); num3 = Mathf.Min(num3, val2.y); } float num4 = num2; Rect rect = _gui.m_player.rect; if (num4 > ((Rect)(ref rect)).xMin) { float num5 = num; rect = _gui.m_player.rect; if (num5 < ((Rect)(ref rect)).xMax) { bottom = Mathf.Min(bottom, num3); } } } private void Click(int index) { if (_state == null || _state.Locked || ((Character)Player.m_localPlayer).IsDead() || ((Character)Player.m_localPlayer).IsTeleporting()) { return; } ItemData val = Drag.Item(_gui); Inventory val2 = Drag.Inventory(_gui); if (val != null && val2 != null) { if (index >= _state.ConfiguredSlots) { return; } if (val2 != ((Humanoid)Player.m_localPlayer).GetInventory() && val2 != _state.Inventory) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Move the item into your inventory first.", 0, (Sprite)null, false); return; } int num = Math.Min(val.m_stack, Drag.Amount(_gui)); if (!val.m_shared.m_teleportable && val2 != _state.Inventory) { for (int i = 0; i < _state.ConfiguredSlots; i++) { if (num <= 0) { break; } num -= _state.Transfer(val2, _state.Inventory, val, num, (index + i) % _state.ConfiguredSlots, 0); } } else { _state.Transfer(val2, _state.Inventory, val, num, index, 0); } Drag.Clear(_gui); } else { ItemData itemAt = _state.Inventory.GetItemAt(index, 0); if (itemAt != null) { Drag.Set(_gui, itemAt, _state.Inventory, itemAt.m_stack); } } } private void Withdraw(int index) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 if (_state == null || _state.Locked) { return; } Drag.Clear(_gui); ItemData itemAt = _state.Inventory.GetItemAt(index, 0); if (itemAt == null) { return; } if (Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) { if ((int)itemAt.m_shared.m_itemType == 2) { _state.UseConsumable(itemAt); } else { _state.ToggleEquipment(itemAt); } } else { _state.Withdraw(itemAt); } } internal void Destroy() { Object.Destroy((Object)(object)_panel); } } internal static class SlotRules { internal const int Slots = 4; internal const int MaxSlots = 28; internal const int Columns = 7; internal const int PouchStackLimit = 99999; internal static int PouchCapacity(bool teleportable, int normalMax, int regularLimit = 99999, int restrictedLimit = 1) { if (normalMax > 1) { return Math.Max(1, Math.Min(99999, teleportable ? regularLimit : restrictedLimit)); } return 1; } internal static int TransferAmount(bool restricted, int maxStack, int existing, bool compatible, int available, int requested) { if (!compatible || available <= 0 || requested <= 0 || existing < 0) { return 0; } int num = (restricted ? 1 : Math.Max(1, maxStack)); return Math.Max(0, Math.Min(Math.Min(available, requested), num - existing)); } } }