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 TidyChests v1.0.1
plugins/TidyChests/TidyChests.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using TidyChests.Compat; using TidyChests.Containers; using TidyChests.Find; using TidyChests.Hud; using TidyChests.Stash; using TidyChests.Ui; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Egor Fadeev")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Terraria-style quick stack for Valheim: a Stash button moves your materials into the nearby chests that already hold them, and a hotkey highlights the chests that contain the item under the cursor.")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+24a6f35d5636a7563caa38cb7a2d70a06d3d7fd5")] [assembly: AssemblyProduct("Tidy Chests")] [assembly: AssemblyTitle("TidyChests")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 TidyChests { public sealed class ModConfig { private static readonly string[] ItemTypeNames = Enum.GetNames(typeof(ItemType)); public const string DefaultItemTypes = "Material, Consumable, Ammo, AmmoNonEquipable, Trophy, Misc, Fish"; private readonly ConfigFile _config; public ConfigEntry<bool> Enabled { get; } public ConfigEntry<bool> IsDebug { get; } public ConfigEntry<float> Radius { get; } public ConfigEntry<bool> IncludeHotbar { get; } public ConfigEntry<string> ItemTypes { get; } public ConfigEntry<string> Blacklist { get; } public ConfigEntry<bool> ShowMessage { get; } public ConfigEntry<KeyboardShortcut> FindKey { get; } public ConfigEntry<float> HighlightDuration { get; } public ConfigEntry<bool> ScreenMarker { get; } public ConfigEntry<bool> Light { get; } public ConfigEntry<bool> Glow { get; } public ConfigEntry<bool> CloseInventory { get; } public ConfigEntry<bool> ShowButton { get; } public ConfigEntry<Vector2> ButtonOffset { get; } public ConfigEntry<Vector2> ButtonSize { get; } public ModConfig(ConfigFile config) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) _config = config; string text = string.Join(", ", ItemTypeNames); Enabled = config.Bind<bool>("General", "Enabled", true, "Enable this mod: the Stash button and the find key."); IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log every item decision and every chest that is considered when stashing."); Radius = config.Bind<float>("Stash", "Radius", 10f, new ConfigDescription("Chests within this many metres of the player are used, both by the Stash button and by the find key.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); IncludeHotbar = config.Bind<bool>("Stash", "IncludeHotbar", false, "Also stash items from the first inventory row (the hotbar)."); ItemTypes = config.Bind<string>("Stash", "ItemTypes", "Material, Consumable, Ammo, AmmoNonEquipable, Trophy, Misc, Fish", "Item types that may be stashed (comma-separated). Equipment, tools and weapons are left out by default. Valid types: " + text); Blacklist = config.Bind<string>("Stash", "Blacklist", "", "Items that are never stashed (comma-separated). Use prefab names or item names, for example: Wood, $item_coal, Resin"); ShowMessage = config.Bind<bool>("Stash", "ShowMessage", true, "Show a message with the result after stashing."); FindKey = config.Bind<KeyboardShortcut>("Find", "FindKey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "With the inventory open, point at an item (in the inventory, or an ingredient or recipe in the crafting panel) and press this key: the inventory closes and every chest in range that holds the item is highlighted. Modifiers are allowed, e.g. \"T + LeftControl\"."); HighlightDuration = config.Bind<float>("Find", "HighlightDuration", 8f, new ConfigDescription("Seconds the found chests stay highlighted.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>())); ScreenMarker = config.Bind<bool>("Find", "ScreenMarker", true, "Draw an arrow with the item count and the distance over each found chest (at the screen edge when the chest is out of view)."); Light = config.Bind<bool>("Find", "Light", true, "Light up each found chest with a pulsing light."); Glow = config.Bind<bool>("Find", "Glow", true, "Tint each found chest with an emissive glow. Only works for materials whose shader has an emission colour."); CloseInventory = config.Bind<bool>("Find", "CloseInventory", true, "Close the inventory when chests are found, so the highlights are visible right away. When nothing is found the inventory stays open."); ShowButton = config.Bind<bool>("Button", "ShowButton", true, "Show the Stash button in the inventory. The console command 'tidychests stash' works without it."); ButtonOffset = config.Bind<Vector2>("Button", "ButtonOffset", new Vector2(41f, -56f), "Position of the Stash button relative to the weight display of the inventory panel, in UI pixels (x right, y up)."); ButtonSize = config.Bind<Vector2>("Button", "ButtonSize", new Vector2(120f, 38f), "Width and height of the Stash button in UI pixels."); } public void Reload() { _config.Reload(); } public StashRules BuildRules() { return StashRules.Parse(new StashRuleSettings { IncludeHotbar = IncludeHotbar.Value, ItemTypes = ItemTypes.Value, Blacklist = Blacklist.Value }, ItemTypeNames, delegate(string warning) { Plugin.Log.LogWarning((object)warning); }); } public HighlightOptions ToHighlightOptions() { return new HighlightOptions(HighlightDuration.Value, Light.Value, Glow.Value); } public string Describe() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) CultureInfo invariantCulture = CultureInfo.InvariantCulture; return (Enabled.Value ? "enabled" : "disabled") + ", radius " + Radius.Value.ToString("0.#", invariantCulture) + " m, hotbar " + (IncludeHotbar.Value ? "included" : "excluded") + ", " + BuildRules().Describe() + ", " + string.Format("find key {0}, highlight {1} s, ", FindKey.Value, HighlightDuration.Value.ToString("0.#", invariantCulture)) + "button " + (ShowButton.Value ? "shown" : "hidden"); } } [BepInPlugin("muindor.TidyChests", "Tidy Chests", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private Harmony? _harmony; public static ManualLogSource Log { get; private set; } public static ModConfig Settings { get; private set; } public static ChestFinder? Finder { get; private set; } public static bool Enabled { get { if (Settings != null) { return Settings.Enabled.Value; } return false; } } private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); Finder = ((Component)this).gameObject.AddComponent<ChestFinder>(); _harmony = new Harmony("muindor.TidyChests"); _harmony.PatchAll(typeof(Plugin).Assembly); Translations.ApplyIfLoaded(); Log.LogInfo((object)("Tidy Chests 1.0.1 loaded: " + Settings.Describe())); SlotMods.Report(); ChestMods.Report(); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static void Debug(string message) { if (Settings != null && Settings.IsDebug.Value) { Log.LogInfo((object)message); } } } internal static class Translations { public const string Button = "tidychests_button"; public const string Stashed = "tidychests_stashed"; public const string Nothing = "tidychests_nothing"; public const string NoChests = "tidychests_no_chests"; public const string Found = "tidychests_found"; public const string NotFound = "tidychests_not_found"; public const string Hover = "tidychests_hover"; private const string DefaultLanguage = "English"; private static readonly Dictionary<string, Dictionary<string, string>> Words = new Dictionary<string, Dictionary<string, string>> { ["English"] = new Dictionary<string, string> { ["tidychests_button"] = "Stash", ["tidychests_stashed"] = "Stashed {0} items into {1} chests", ["tidychests_nothing"] = "Nothing to stash: no chest nearby holds these items", ["tidychests_no_chests"] = "No usable chest within {0} m", ["tidychests_found"] = "{0}: found in {1} chests", ["tidychests_not_found"] = "{0}: not in any chest within {1} m", ["tidychests_hover"] = "Point at an item in the inventory or the crafting panel and press {0} to find it in nearby chests" }, ["Russian"] = new Dictionary<string, string> { ["tidychests_button"] = "Убрать", ["tidychests_stashed"] = "Убрано предметов: {0}, в сундуков: {1}", ["tidychests_nothing"] = "Нечего убирать: поблизости нет сундуков с такими предметами", ["tidychests_no_chests"] = "В радиусе {0} м нет подходящих сундуков", ["tidychests_found"] = "{0}: есть в сундуках ({1})", ["tidychests_not_found"] = "{0}: нет ни в одном сундуке в радиусе {1} м", ["tidychests_hover"] = "Наведите курсор на предмет в инвентаре или в крафте и нажмите {0}, чтобы найти его в сундуках" } }; public static string Get(string key, params object[] args) { string text = Localization.instance.Localize("$" + key); if (args.Length != 0) { return string.Format(text, args); } return text; } public static void ApplyIfLoaded() { Localization instance = Localization.m_instance; if (instance != null) { Apply(instance, instance.GetSelectedLanguage()); } } public static void Apply(Localization localization, string language) { Dictionary<string, string> dictionary = Words["English"]; Words.TryGetValue(language ?? "", out Dictionary<string, string> value); foreach (KeyValuePair<string, string> item in dictionary) { string value2; string text = ((value != null && value.TryGetValue(item.Key, out value2)) ? value2 : item.Value); localization.AddWord(item.Key, text); } } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class Localization_SetupLanguage_Patch { private static void Postfix(Localization __instance, string language) { Translations.Apply(__instance, language); StashButton.RefreshLabel(); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "muindor.TidyChests"; public const string PLUGIN_NAME = "Tidy Chests"; public const string PLUGIN_VERSION = "1.0.1"; } } namespace TidyChests.Ui { internal static class StashButton { [CompilerGenerated] private static class <>O { public static UnityAction <0>__OnClick; } private const string ButtonName = "TidyChests Stash"; private static Button? _button; private static TMP_Text? _label; private static bool _warned; public static void Ensure(InventoryGui gui) { if (!Plugin.Enabled || !Plugin.Settings.ShowButton.Value) { if ((Object)(object)_button != (Object)null) { ((Component)_button).gameObject.SetActive(false); } return; } if ((Object)(object)_button != (Object)null) { ((Component)_button).gameObject.SetActive(true); return; } try { Create(gui); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not add the Stash button to the inventory: {arg}"); } } public static void RefreshLabel() { if ((Object)(object)_label != (Object)null) { _label.text = Translations.Get("tidychests_button"); } } private static void Create(InventoryGui gui) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //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_00f4: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) Button takeAllButton = gui.m_takeAllButton; RectTransform player = gui.m_player; if ((Object)(object)takeAllButton == (Object)null || (Object)(object)player == (Object)null) { if (!_warned) { _warned = true; Plugin.Log.LogWarning((object)"The inventory has no 'take all' button to copy (another UI mod?); the Stash button is not shown. Use the console command 'tidychests stash'."); } return; } UIGamePad component = ((Component)takeAllButton).GetComponent<UIGamePad>(); bool enabled = (Object)(object)component != (Object)null && ((Behaviour)component).enabled; if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } Button val; try { val = Object.Instantiate<Button>(takeAllButton, (Transform)(object)player); } finally { if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = enabled; } } ((Object)val).name = "TidyChests Stash"; UIGamePad component2 = ((Component)val).GetComponent<UIGamePad>(); if ((Object)(object)component2 != (Object)null) { if ((Object)(object)component2.m_hint != (Object)null) { component2.m_hint.SetActive(false); } Object.Destroy((Object)(object)component2); } ((UnityEventBase)val.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = val.onClick; object obj = <>O.<0>__OnClick; if (obj == null) { UnityAction val2 = OnClick; <>O.<0>__OnClick = val2; obj = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj); RectTransform val3 = (RectTransform)((Component)val).transform; Vector2 value = Plugin.Settings.ButtonSize.Value; val3.SetSizeWithCurrentAnchors((Axis)0, Mathf.Max(20f, value.x)); val3.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(16f, value.y)); Vector2 value2 = Plugin.Settings.ButtonOffset.Value; Transform val4 = ((Transform)player).Find("Weight"); if ((Object)(object)val4 != (Object)null) { ((Component)val).transform.localPosition = val4.localPosition + new Vector3(value2.x, value2.y, 0f); } else { val3.anchorMin = new Vector2(1f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(1f, 1f); val3.anchoredPosition = new Vector2(-16f, -16f) + value2; Plugin.Debug("Weight display not found in the inventory panel; the Stash button is anchored to the top-right corner"); } _label = ((Component)val).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)_label != (Object)null) { _label.enableAutoSizing = true; _label.fontSizeMax = _label.fontSize; _label.fontSizeMin = Mathf.Min(10f, _label.fontSize); } _button = val; RefreshLabel(); object arg = ((Component)val).transform.localPosition; Rect rect = val3.rect; object arg2 = ((Rect)(ref rect)).width; rect = val3.rect; Plugin.Debug($"Stash button created at {arg} ({arg2}x{((Rect)(ref rect)).height})"); } private static void OnClick() { try { Stasher.Stash(Player.m_localPlayer); } catch (Exception arg) { Plugin.Log.LogError((object)$"Stash failed: {arg}"); } } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class InventoryGui_Show_Patch { private static void Postfix(InventoryGui __instance) { StashButton.Ensure(__instance); } } } namespace TidyChests.Stash { public readonly struct ItemFacts { public string PrefabName { get; } public string SharedName { get; } public string TypeName { get; } public int MaxStack { get; } public bool Equipped { get; } public bool Hotbar { get; } public bool ModSlot { get; } public bool QuestItem { get; } public string DisplayName { get { if (PrefabName.Length <= 0) { return SharedName; } return PrefabName; } } public ItemFacts(string prefabName, string sharedName, string typeName, int maxStack, bool equipped, bool hotbar, bool modSlot, bool questItem) { PrefabName = prefabName ?? ""; SharedName = sharedName ?? ""; TypeName = typeName ?? ""; MaxStack = maxStack; Equipped = equipped; Hotbar = hotbar; ModSlot = modSlot; QuestItem = questItem; } } internal static class Stasher { public static string Stash(Player player) { //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled) { return "disabled"; } if ((Object)(object)player == (Object)null || ((Character)player).IsTeleporting()) { return "no player"; } ModConfig settings = Plugin.Settings; InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null) { instance.SetupDragItem((ItemData)null, (Inventory)null, 1); } List<ItemSnapshot> snapshots; List<ItemData> list = Candidates(player, settings, out snapshots); if (list.Count == 0) { return Report(player, settings, Translations.Get("tidychests_nothing"), "no stashable items"); } bool multiUserChestLoaded = ChestMods.MultiUserChestLoaded; long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); List<ContainerSnapshot> snapshots2; List<Container> list2 = UsableContainers(player, settings, playerID, multiUserChestLoaded, out snapshots2); if (list2.Count == 0) { string text = settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture); return Report(player, settings, Translations.Get("tidychests_no_chests", text), "no usable chest in range"); } StashPlan stashPlan = StashPlanner.Plan(snapshots, snapshots2); Plugin.Debug($"Plan: {stashPlan.Moves.Count} moves, {stashPlan.Units} units from {stashPlan.ItemsTouched} stacks into {stashPlan.ContainersUsed} chests"); if (stashPlan.Moves.Count == 0) { return Report(player, settings, Translations.Get("tidychests_nothing"), "no chest holds these items"); } Inventory inventory = ((Humanoid)player).GetInventory(); int num = 0; HashSet<Container> hashSet = new HashSet<Container>(); foreach (KeyValuePair<int, List<StashMove>> item in GroupByContainer(stashPlan.Moves)) { Container val = list2[item.Key]; if ((Object)(object)val == (Object)null || val.GetInventory() == null) { continue; } if (!ContainerAccess.PrepareForWrite(val, multiUserChestLoaded)) { Plugin.Log.LogWarning((object)("Could not take ownership of " + ContainerAccess.NameOf(val) + ", skipping it")); continue; } foreach (StashMove item2 in item.Value) { ItemData val2 = list[item2.Item]; int num2 = MoveInto(inventory, val2, val.GetInventory(), item2.Amount); Plugin.Debug(string.Format(" {0} x{1} -> {2} ({3} m): {4} moved", val2.m_shared.m_name, item2.Amount, ContainerAccess.NameOf(val), snapshots2[item.Key].Distance.ToString("0.#", CultureInfo.InvariantCulture), num2)); if (num2 > 0) { num += num2; hashSet.Add(val); } } } inventory.Changed(false, false); foreach (Container item3 in hashSet) { if ((Object)(object)instance != (Object)null && (Object)(object)item3 != (Object)null) { instance.m_moveItemEffects.Create(((Component)item3).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } string message = ((num > 0) ? Translations.Get("tidychests_stashed", num, hashSet.Count) : Translations.Get("tidychests_nothing")); return Report(player, settings, message, $"{num} units into {hashSet.Count} chests"); } private static List<ItemData> Candidates(Player player, ModConfig settings, out List<ItemSnapshot> snapshots) { StashRules stashRules = settings.BuildRules(); Func<ItemData, bool> classifier = SlotMods.GetClassifier(); Inventory inventory = ((Humanoid)player).GetInventory(); List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); list.Sort((ItemData a, ItemData b) => (a.m_gridPos.y == b.m_gridPos.y) ? a.m_gridPos.x.CompareTo(b.m_gridPos.x) : a.m_gridPos.y.CompareTo(b.m_gridPos.y)); Plugin.Debug($"Stash by {player.GetPlayerName()}: {list.Count} items, inventory {inventory.GetWidth()}x{inventory.GetHeight()}, {stashRules.Describe()}"); List<ItemData> list2 = new List<ItemData>(); snapshots = new List<ItemSnapshot>(); foreach (ItemData item in list) { ItemFacts facts = Describe(item, player, classifier); StashVerdict stashVerdict = stashRules.Judge(facts); Plugin.Debug($" {facts.DisplayName} x{item.m_stack} at [{item.m_gridPos.x},{item.m_gridPos.y}] ({facts.TypeName}) -> {stashVerdict}"); if (stashVerdict == StashVerdict.Stash) { snapshots.Add(new ItemSnapshot(list2.Count, item.m_shared.m_name, item.m_quality, item.m_worldLevel, item.m_stack, item.m_shared.m_maxStackSize)); list2.Add(item); } } return list2; } private static List<Container> UsableContainers(Player player, ModConfig settings, long playerId, bool multiUserChest, out List<ContainerSnapshot> snapshots) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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) List<Container> list = new List<Container>(); ContainerRegistry.CollectNearby(((Component)player).transform.position, settings.Radius.Value, list); Plugin.Debug(string.Format("{0} containers within {1} m ({2} loaded), MultiUserChest {3}", list.Count, settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture), ContainerRegistry.Count, multiUserChest ? "on" : "off")); List<Container> list2 = new List<Container>(); snapshots = new List<ContainerSnapshot>(); foreach (Container item in list) { float distance = Vector3.Distance(((Component)item).transform.position, ((Component)player).transform.position); if (!ContainerAccess.CanStashInto(item, playerId, multiUserChest, out string reason)) { Plugin.Debug(" skip " + ContainerAccess.NameOf(item) + " at " + distance.ToString("0.#", CultureInfo.InvariantCulture) + " m: " + reason); continue; } Inventory inventory = item.GetInventory(); List<StackSnapshot> list3 = new List<StackSnapshot>(); foreach (ItemData allItem in inventory.GetAllItems()) { list3.Add(new StackSnapshot(allItem.m_shared.m_name, allItem.m_quality, allItem.m_worldLevel, allItem.m_stack, allItem.m_shared.m_maxStackSize)); } snapshots.Add(new ContainerSnapshot(list2.Count, ContainerAccess.NameOf(item), distance, list3, inventory.GetEmptySlots())); list2.Add(item); } return list2; } private static int MoveInto(Inventory from, ItemData item, Inventory target, int amount) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) if (!from.ContainsItem(item)) { return 0; } amount = Math.Min(amount, item.m_stack); if (amount <= 0) { return 0; } if (amount == item.m_stack) { if (target.AddItem(item)) { from.RemoveItem(item); return amount; } if (!from.ContainsItem(item)) { return amount; } return Math.Max(0, amount - item.m_stack); } int num = 0; foreach (ItemData item2 in new List<ItemData>(target.GetAllItems())) { if (num >= amount || !from.ContainsItem(item)) { break; } if (!(item2.m_shared.m_name != item.m_shared.m_name) && item2.m_quality == item.m_quality && item2.m_worldLevel == item.m_worldLevel) { int num2 = item2.m_shared.m_maxStackSize - item2.m_stack; if (num2 > 0) { num += MoveTo(from, item, target, Math.Min(num2, amount - num), item2.m_gridPos); } } } for (int i = 0; i < target.GetHeight(); i++) { if (num >= amount) { break; } for (int j = 0; j < target.GetWidth(); j++) { if (num >= amount) { break; } if (from.ContainsItem(item) && target.GetItemAt(j, i) == null) { num += MoveTo(from, item, target, Math.Min(item.m_shared.m_maxStackSize, amount - num), new Vector2i(j, i)); } } } return num; } private static int MoveTo(Inventory from, ItemData item, Inventory target, int units, Vector2i cell) { //IL_000b: 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) int stack = item.m_stack; if (target.MoveItemToThis(from, item, units, cell.x, cell.y)) { return units; } if (!from.ContainsItem(item)) { return stack; } return Math.Max(0, stack - item.m_stack); } private static IEnumerable<KeyValuePair<int, List<StashMove>>> GroupByContainer(IReadOnlyList<StashMove> moves) { List<int> list = new List<int>(); Dictionary<int, List<StashMove>> groups = new Dictionary<int, List<StashMove>>(); foreach (StashMove move in moves) { if (!groups.TryGetValue(move.Container, out List<StashMove> value)) { value = new List<StashMove>(); groups[move.Container] = value; list.Add(move.Container); } value.Add(move); } foreach (int item in list) { yield return new KeyValuePair<int, List<StashMove>>(item, groups[item]); } } private static ItemFacts Describe(ItemData item, Player player, Func<ItemData, bool>? inModSlot) { SharedData shared = item.m_shared; return new ItemFacts(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "", shared.m_name, ((object)Unsafe.As<ItemType, ItemType>(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString(), shared.m_maxStackSize, item.m_equipped || ((Humanoid)player).IsItemEquiped(item), item.m_gridPos.y == 0, inModSlot?.Invoke(item) ?? false, shared.m_questItem); } private static string Report(Player player, ModConfig settings, string message, string summary) { Plugin.Debug("Stash result: " + summary); if (settings.ShowMessage.Value) { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false); } return summary; } } public sealed class ItemSnapshot { public int Index { get; } public string Name { get; } public int Quality { get; } public int WorldLevel { get; } public int Count { get; } public int MaxStack { get; } public ItemSnapshot(int index, string name, int quality, int worldLevel, int count, int maxStack) { Index = index; Name = name; Quality = quality; WorldLevel = worldLevel; Count = count; MaxStack = maxStack; } } public sealed class StackSnapshot { public string Name { get; } public int Quality { get; } public int WorldLevel { get; } public int Count { get; set; } public int MaxStack { get; } public int FreeSpace => MaxStack - Count; public StackSnapshot(string name, int quality, int worldLevel, int count, int maxStack) { Name = name; Quality = quality; WorldLevel = worldLevel; Count = count; MaxStack = maxStack; } public bool Accepts(ItemSnapshot item) { if (MaxStack > 1 && Name == item.Name && Quality == item.Quality) { return WorldLevel == item.WorldLevel; } return false; } } public sealed class ContainerSnapshot { public int Index { get; } public string Name { get; } public float Distance { get; } public List<StackSnapshot> Stacks { get; } public int EmptySlots { get; set; } public ContainerSnapshot(int index, string name, float distance, List<StackSnapshot> stacks, int emptySlots) { Index = index; Name = name; Distance = distance; Stacks = stacks; EmptySlots = emptySlots; } public bool Holds(string name) { foreach (StackSnapshot stack in Stacks) { if (stack.Name == name) { return true; } } return false; } } public readonly struct StashMove { public int Item { get; } public int Container { get; } public int Amount { get; } public StashMove(int item, int container, int amount) { Item = item; Container = container; Amount = amount; } } public sealed class StashPlan { public IReadOnlyList<StashMove> Moves { get; } public int Units { get; } public int ItemsTouched { get; } public int ContainersUsed { get; } public StashPlan(IReadOnlyList<StashMove> moves, int units, int itemsTouched, int containersUsed) { Moves = moves; Units = units; ItemsTouched = itemsTouched; ContainersUsed = containersUsed; } } public static class StashPlanner { public static StashPlan Plan(IReadOnlyList<ItemSnapshot> items, IReadOnlyList<ContainerSnapshot> containers) { List<ContainerSnapshot> list = containers.OrderBy((ContainerSnapshot container) => container.Distance).ToList(); List<StashMove> list2 = new List<StashMove>(); HashSet<int> hashSet = new HashSet<int>(); HashSet<int> hashSet2 = new HashSet<int>(); int num = 0; foreach (ItemSnapshot item in items) { int num2 = item.Count; foreach (ContainerSnapshot item2 in list) { if (num2 <= 0) { break; } if (item2.Holds(item.Name)) { int num3 = Fill(item2, item, num2); if (num3 > 0) { num2 -= num3; num += num3; hashSet.Add(item.Index); hashSet2.Add(item2.Index); list2.Add(new StashMove(item.Index, item2.Index, num3)); } } } } return new StashPlan(list2, num, hashSet.Count, hashSet2.Count); } private static int Fill(ContainerSnapshot container, ItemSnapshot item, int remaining) { int num = 0; foreach (StackSnapshot stack in container.Stacks) { if (remaining <= 0) { break; } if (stack.Accepts(item) && stack.FreeSpace > 0) { int num2 = Math.Min(stack.FreeSpace, remaining); stack.Count += num2; remaining -= num2; num += num2; } } while (remaining > 0 && container.EmptySlots > 0) { int num3 = Math.Min(Math.Max(1, item.MaxStack), remaining); container.Stacks.Add(new StackSnapshot(item.Name, item.Quality, item.WorldLevel, num3, item.MaxStack)); container.EmptySlots--; remaining -= num3; num += num3; } return num; } } public sealed class StashRules { private readonly bool _includeHotbar; private readonly HashSet<string> _types; private readonly HashSet<string> _blacklist; public IReadOnlyList<string> ItemTypes => _types.OrderBy<string, string>((string type) => type, StringComparer.OrdinalIgnoreCase).ToList(); public IReadOnlyList<string> Blacklist => _blacklist.OrderBy<string, string>((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); private StashRules(bool includeHotbar, HashSet<string> types, HashSet<string> blacklist) { _includeHotbar = includeHotbar; _types = types; _blacklist = blacklist; } public static StashRules Parse(StashRuleSettings settings, IEnumerable<string> knownTypeNames, Action<string>? warn = null) { HashSet<string> hashSet = new HashSet<string>(knownTypeNames, StringComparer.OrdinalIgnoreCase); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in Split(settings.ItemTypes)) { if (hashSet.Contains(item)) { hashSet2.Add(item); } else { warn?.Invoke("Unknown item type '" + item + "' in ItemTypes is ignored. Valid types: " + string.Join(", ", knownTypeNames)); } } HashSet<string> hashSet3 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item2 in Split(settings.Blacklist)) { hashSet3.Add(Normalize(item2)); } return new StashRules(settings.IncludeHotbar, hashSet2, hashSet3); } public StashVerdict Judge(ItemFacts facts) { if (facts.MaxStack <= 1) { return StashVerdict.NotStackable; } if (facts.QuestItem) { return StashVerdict.QuestItem; } if (facts.Equipped) { return StashVerdict.Equipped; } if (facts.ModSlot) { return StashVerdict.ModSlot; } if (facts.Hotbar && !_includeHotbar) { return StashVerdict.Hotbar; } if (!_types.Contains(facts.TypeName)) { return StashVerdict.TypeExcluded; } if (IsBlacklisted(facts.PrefabName) || IsBlacklisted(facts.SharedName)) { return StashVerdict.Blacklisted; } return StashVerdict.Stash; } public string Describe() { string text = ((_types.Count == 0) ? "none" : string.Join(", ", ItemTypes)); string text2 = ((_blacklist.Count == 0) ? "empty" : string.Join(", ", Blacklist)); return "types: " + text + "; blacklist: " + text2; } private bool IsBlacklisted(string name) { string text = Normalize(name); if (text.Length > 0) { return _blacklist.Contains(text); } return false; } private static IEnumerable<string> Split(string list) { if (string.IsNullOrWhiteSpace(list)) { yield break; } string[] array = list.Split(',', ';'); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { yield return text; } } } private static string Normalize(string name) { return (name ?? "").Trim().TrimStart(new char[1] { '$' }); } } public sealed class StashRuleSettings { public bool IncludeHotbar { get; set; } public string ItemTypes { get; set; } = ""; public string Blacklist { get; set; } = ""; } public enum StashVerdict { Stash, NotStackable, QuestItem, Equipped, ModSlot, Hotbar, TypeExcluded, Blacklisted } } namespace TidyChests.Patches { [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Terminal_InitTerminal_Patch { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Execute; } private const string Command = "tidychests"; private static void Postfix() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown object obj = <>O.<0>__Execute; if (obj == null) { ConsoleEvent val = Execute; <>O.<0>__Execute = val; obj = (object)val; } new ConsoleCommand("tidychests", "tidychests [stash|reload|status] - stash items into nearby chests, reload the Tidy Chests config or show the active settings", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Execute(ConsoleEventArgs args) { Terminal context = args.Context; switch ((args.Args.Length > 1) ? args.Args[1].ToLowerInvariant() : "status") { case "stash": if ((Object)(object)Player.m_localPlayer == (Object)null) { context.AddString("Tidy Chests: no player in the world"); } else { context.AddString("Tidy Chests: " + Stasher.Stash(Player.m_localPlayer)); } break; case "reload": Plugin.Settings.Reload(); context.AddString("Tidy Chests: config reloaded, " + Plugin.Settings.Describe()); break; case "status": context.AddString(string.Format("{0} {1}: {2}; {3} chests highlighted", "Tidy Chests", "1.0.1", Plugin.Settings.Describe(), Plugin.Finder?.HighlightCount ?? 0)); break; default: context.AddString("Usage: tidychests [stash|reload|status]"); break; } } } } namespace TidyChests.Hud { internal static class MarkerOverlay { private const int ArrowTextureSize = 48; private const float ArrowSize = 32f; private const float EdgeMargin = 56f; private static Texture2D? _arrow; private static GUIStyle? _label; public static void Draw(IReadOnlyList<ChestHighlight> highlights, Camera camera, Vector3 playerPosition) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_0082: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: 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: 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_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) if ((int)Event.current.type != 7) { return; } Texture2D arrow = _arrow ?? (_arrow = CreateArrow(48)); GUIStyle val = _label ?? (_label = CreateLabelStyle()); float num = Screen.width; float num2 = Screen.height; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(num / 2f, num2 / 2f); Vector2 val6 = default(Vector2); Vector2 val7 = default(Vector2); foreach (ChestHighlight highlight in highlights) { Vector3 val3 = camera.WorldToViewportPoint(highlight.Position); ScreenMarker screenMarker = ScreenMarkerMath.Compute(val3.x, val3.y, val3.z, num, num2, 56f); float alpha = highlight.Alpha; Color color = ChestHighlight.Color; color.a = alpha; float num3 = Vector3.Distance(playerPosition, highlight.Position); GUIContent val4 = new GUIContent($"{highlight.Label} {num3:0} m"); Vector2 val5 = val.CalcSize(val4); if (screenMarker.OnScreen) { ((Vector2)(ref val6))..ctor(screenMarker.X, screenMarker.Y - 10f); DrawArrow(arrow, val6, screenMarker.AngleDegrees, color); DrawLabel(val, val4, val5, new Vector2(val6.x, val6.y - 32f - 8f - val5.y / 2f), alpha); continue; } ((Vector2)(ref val7))..ctor(screenMarker.X, screenMarker.Y); DrawArrow(arrow, val7, screenMarker.AngleDegrees, color); Vector2 val8 = val2 - val7; Vector2 normalized = ((Vector2)(ref val8)).normalized; float num4 = 40f + Mathf.Abs(normalized.x) * val5.x / 2f + Mathf.Abs(normalized.y) * val5.y / 2f; DrawLabel(val, val4, val5, val7 + normalized * num4, alpha); } } private static void DrawArrow(Texture2D arrow, Vector2 tip, float angle, Color color) { //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_000b: 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_0032: 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) //IL_004a: 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_006b: 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_007d: 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) Matrix4x4 matrix = GUI.matrix; Color color2 = GUI.color; GUIUtility.RotateAroundPivot(angle, tip); float num = 40f; GUI.color = new Color(0f, 0f, 0f, 0.7f * color.a); GUI.DrawTexture(new Rect(tip.x - num / 2f, tip.y - (num - 32f) / 2f, num, num), (Texture)(object)arrow); GUI.color = color; GUI.DrawTexture(new Rect(tip.x - 16f, tip.y, 32f, 32f), (Texture)(object)arrow); GUI.color = color2; GUI.matrix = matrix; } private static void DrawLabel(GUIStyle style, GUIContent content, Vector2 size, Vector2 center, float alpha) { //IL_0002: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_002e: 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) //IL_004f: 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_009e: 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) Rect val = default(Rect); ((Rect)(ref val))..ctor(center.x - size.x / 2f, center.y - size.y / 2f, size.x, size.y); Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, alpha); GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), content, style); GUI.color = new Color(1f, 1f, 1f, alpha); GUI.Label(val, content, style); GUI.color = color; } private static GUIStyle CreateLabelStyle() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0025: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, wordWrap = false }; val.normal.textColor = Color.white; return val; } private static Texture2D CreateArrow(int size) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false) { hideFlags = (HideFlags)61, wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, name = "TidyChests arrow" }; float num = (float)size / 2f; float num2 = (float)size - 1f; float num3 = 1f; float num4 = (float)size / 2f - 1f; Color32[] array = (Color32[])(object)new Color32[size * size]; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { int num5 = 0; for (int k = 0; k < 2; k++) { for (int l = 0; l < 2; l++) { float num6 = (float)j + 0.25f + (float)l * 0.5f; float num7 = (float)i + 0.25f + (float)k * 0.5f; float num8 = (num7 - num3) / (num2 - num3); float num9 = num4 * (1f - num8); if (num7 >= num3 && num7 <= num2 && Mathf.Abs(num6 - num) <= num9) { num5++; } } } byte b = (byte)(num5 * 255 / 4); array[i * size + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b); } } val.SetPixels32(array); val.Apply(false, true); return val; } } public readonly struct ScreenMarker { public float X { get; } public float Y { get; } public float AngleDegrees { get; } public bool OnScreen { get; } public ScreenMarker(float x, float y, float angleDegrees, bool onScreen) { X = x; Y = y; AngleDegrees = angleDegrees; OnScreen = onScreen; } } public static class ScreenMarkerMath { private const float Epsilon = 0.0001f; public static ScreenMarker Compute(float vx, float vy, float vz, float width, float height, float margin) { bool flag = !IsBad(vx) && !IsBad(vy) && !IsBad(vz); bool flag2 = vz > 0f; if (flag && flag2 && vx >= 0f && vx <= 1f && vy >= 0f && vy <= 1f) { return new ScreenMarker(vx * width, (1f - vy) * height, 180f, onScreen: true); } float num = width / 2f; float num2 = height / 2f; float num3 = 0f; float num4 = 1f; if (flag) { num3 = vx * width - num; num4 = (1f - vy) * height - num2; if (!flag2) { num3 = 0f - num3; num4 = 0f - num4; } if (Math.Abs(num3) < 0.0001f && Math.Abs(num4) < 0.0001f) { num3 = 0f; num4 = 1f; } } float num5 = Math.Max(1f, num - margin); float num6 = Math.Max(1f, num2 - margin); float num7 = float.MaxValue; if (Math.Abs(num3) > 0.0001f) { num7 = Math.Min(num7, num5 / Math.Abs(num3)); } if (Math.Abs(num4) > 0.0001f) { num7 = Math.Min(num7, num6 / Math.Abs(num4)); } float angleDegrees = (float)(Math.Atan2(num3, 0f - num4) * 180.0 / Math.PI); return new ScreenMarker(num + num3 * num7, num2 + num4 * num7, angleDegrees, onScreen: false); } private static bool IsBad(float value) { if (!float.IsNaN(value)) { return float.IsInfinity(value); } return true; } } } namespace TidyChests.Find { public sealed class ChestFinder : MonoBehaviour { private readonly List<ChestHighlight> _highlights = new List<ChestHighlight>(); private readonly List<Container> _nearby = new List<Container>(); private bool _overlayFailed; public int HighlightCount => _highlights.Count; public int Find(string sharedName, string displayName) { //IL_002e: 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) //IL_00de: Unknown result type (might be due to invalid IL or missing references) ModConfig settings = Plugin.Settings; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return 0; } RemoveHighlights(); _nearby.Clear(); ContainerRegistry.CollectNearby(((Component)localPlayer).transform.position, settings.Radius.Value, _nearby); HighlightOptions options = settings.ToHighlightOptions(); foreach (Container item in _nearby) { Inventory inventory = item.GetInventory(); if (inventory == null || IsCarriedByAnotherPlayer(item)) { continue; } int num = inventory.CountItems(sharedName, -1, false); if (num > 0) { try { _highlights.Add(new ChestHighlight(item, $"{num}x {displayName}", options)); Plugin.Debug(string.Format(" {0} at {1} m holds {2}", ContainerAccess.NameOf(item), Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position).ToString("0.#", CultureInfo.InvariantCulture), num)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not highlight " + ContainerAccess.NameOf(item) + ": " + ex.Message)); } } } return _highlights.Count; } public void RemoveHighlights() { foreach (ChestHighlight highlight in _highlights) { highlight.Remove(); } _highlights.Clear(); } private void Update() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) try { if (!Plugin.Enabled || (Object)(object)Player.m_localPlayer == (Object)null) { if (_highlights.Count > 0) { RemoveHighlights(); } return; } UpdateHighlights(); if (InventoryGui.IsVisible() && IsPressed(Plugin.Settings.FindKey.Value)) { FindHovered(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Update failed: {arg}"); } } private void OnGUI() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (_overlayFailed || _highlights.Count == 0) { return; } ModConfig settings = Plugin.Settings; if (!settings.Enabled.Value || !settings.ScreenMarker.Value || (int)Event.current.type != 7) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || Hud.IsUserHidden() || Minimap.IsOpen() || Menu.IsVisible()) { return; } Camera mainCamera = Utils.GetMainCamera(); if ((Object)(object)mainCamera == (Object)null) { return; } try { MarkerOverlay.Draw(_highlights, mainCamera, ((Component)localPlayer).transform.position); } catch (Exception arg) { _overlayFailed = true; Plugin.Log.LogError((object)$"Screen markers disabled until restart: {arg}"); } } private void OnDestroy() { RemoveHighlights(); } private void UpdateHighlights() { for (int num = _highlights.Count - 1; num >= 0; num--) { ChestHighlight chestHighlight = _highlights[num]; if (chestHighlight.Expired) { chestHighlight.Remove(); _highlights.RemoveAt(num); } else { chestHighlight.Update(); } } } private void FindHovered() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) InventoryGui instance = InventoryGui.instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null) { return; } string text = HoveredInventoryItem(instance)?.m_shared.m_name ?? HoveredCraftingItem(instance); if (text == null) { ((Character)localPlayer).Message((MessageType)1, Translations.Get("tidychests_hover", Plugin.Settings.FindKey.Value), 0, (Sprite)null, false); return; } string text2 = Localization.instance.Localize(text); Plugin.Debug("Find " + text + " within " + Plugin.Settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture) + " m"); int num = Find(text, text2); if (num == 0) { string text3 = Plugin.Settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture); ((Character)localPlayer).Message((MessageType)2, Translations.Get("tidychests_not_found", text2, text3), 0, (Sprite)null, false); return; } if (Plugin.Settings.CloseInventory.Value) { instance.Hide(); } ((Character)localPlayer).Message((MessageType)2, Translations.Get("tidychests_found", text2, num), 0, (Sprite)null, false); } private static ItemData? HoveredInventoryItem(InventoryGui gui) { ItemData val = HoveredItem(gui.m_playerGrid) ?? HoveredItem(gui.m_containerGrid); if (val == null && ZInput.IsExclusiveGamepadActive()) { val = gui.m_playerGrid.GetGamepadSelectedItem() ?? gui.m_containerGrid.GetGamepadSelectedItem(); } return val; } private static string? HoveredCraftingItem(InventoryGui gui) { //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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) Vector3 pointerPosition = ZInput.pointerPosition; GameObject[] recipeRequirementList = gui.m_recipeRequirementList; foreach (GameObject val in recipeRequirementList) { if ((Object)(object)val == (Object)null) { continue; } Transform val2 = val.transform.Find("res_icon"); if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeInHierarchy || (!Contains((RectTransform?)/*isinst with value type is only supported in some contexts*/, pointerPosition) && !Contains((RectTransform?)(object)((val2 is RectTransform) ? val2 : null), pointerPosition))) { continue; } Image component = ((Component)val2).GetComponent<Image>(); Sprite val3 = ((component != null) ? component.sprite : null); foreach (Requirement req in gui.m_reqList) { ItemDrop resItem = req.m_resItem; if ((Object)(object)resItem != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)resItem.m_itemData.GetIcon() == (Object)(object)val3) { return resItem.m_itemData.m_shared.m_name; } } } Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe; if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && (Object)(object)gui.m_recipeIcon != (Object)null && ((Behaviour)gui.m_recipeIcon).enabled && Contains(((Graphic)gui.m_recipeIcon).rectTransform, pointerPosition)) { return recipe.m_item.m_itemData.m_shared.m_name; } RectTransform val4 = (RectTransform)(((Object)(object)gui.m_recipeListRoot != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val4 != (Object)null && Contains(val4, pointerPosition)) { foreach (RecipeDataPair availableRecipe in gui.m_availableRecipes) { RecipeDataPair current = availableRecipe; GameObject interfaceElement = ((RecipeDataPair)(ref current)).InterfaceElement; if ((Object)(object)interfaceElement != (Object)null && (Object)(object)((RecipeDataPair)(ref current)).Recipe != (Object)null && (Object)(object)((RecipeDataPair)(ref current)).Recipe.m_item != (Object)null && Contains((RectTransform?)/*isinst with value type is only supported in some contexts*/, pointerPosition)) { return ((RecipeDataPair)(ref current)).Recipe.m_item.m_itemData.m_shared.m_name; } } } return null; } private static bool Contains(RectTransform? rect, Vector3 pointer) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_002b: 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) if ((Object)(object)rect == (Object)null || !((Component)rect).gameObject.activeInHierarchy) { return false; } Vector2 val = Vector2.op_Implicit(((Transform)rect).InverseTransformPoint(pointer)); Rect rect2 = rect.rect; return ((Rect)(ref rect2)).Contains(val); } private static ItemData? HoveredItem(InventoryGrid? grid) { //IL_0034: 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) //IL_0040: 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) if ((Object)(object)grid == (Object)null || !((Component)grid).gameObject.activeInHierarchy || grid.GetInventory() == null) { return null; } InventoryElement hoveredElement = grid.GetHoveredElement(); if ((Object)(object)hoveredElement == (Object)null) { return null; } Vector2i elementPos = grid.GetElementPos(hoveredElement); return grid.GetInventory().GetItemAt(elementPos.x, elementPos.y); } private static bool IsCarriedByAnotherPlayer(Container container) { ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null) { return false; } Player component = ((Component)nview).GetComponent<Player>(); if ((Object)(object)component != (Object)null) { return (Object)(object)component != (Object)(object)Player.m_localPlayer; } return false; } private static bool IsPressed(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return false; } if (Console.IsVisible() || TextInput.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())) { return false; } if (!ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!ZInput.GetKey(modifier, false)) { return false; } } return true; } } public sealed class ChestHighlight { public static readonly Color Color = new Color(1f, 0.82f, 0.3f); private const float LightIntensity = 4f; private const float FadeFraction = 0.25f; private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); private readonly Container _container; private readonly float _start; private readonly float _duration; private readonly List<Renderer> _glowing = new List<Renderer>(); private GameObject? _root; private Light? _light; public string Label { get; } public Vector3 Position { get; } public bool Expired { get { if (!((Object)(object)_container == (Object)null)) { return Time.time - _start >= _duration; } return true; } } public float Alpha => Mathf.Clamp01((_duration - (Time.time - _start)) / (_duration * 0.25f)); public ChestHighlight(Container container, string label, HighlightOptions options) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_0059: 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_0068: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) _container = container; Label = label; _start = Time.time; _duration = Mathf.Max(0.5f, options.Duration); bool found; Bounds val = BoundsOf(((Component)container).gameObject, out found); Position = (found ? ((Bounds)(ref val)).center : (((Component)container).transform.position + Vector3.up * 0.5f)); float extent = (found ? Mathf.Max(new float[3] { ((Bounds)(ref val)).extents.x, ((Bounds)(ref val)).extents.y, ((Bounds)(ref val)).extents.z }) : 1f); float top = (found ? ((Bounds)(ref val)).max.y : (Position.y + 0.5f)); _root = new GameObject("TidyChests highlight"); _root.transform.position = Position; try { Build(options, top, extent, ((Component)container).gameObject); } catch { Remove(); throw; } } private void Build(HighlightOptions options, float top, float extent, GameObject target) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (options.Light) { GameObject val = new GameObject("Light"); val.transform.SetParent(_root.transform, false); val.transform.position = new Vector3(Position.x, top + 0.8f, Position.z); _light = val.AddComponent<Light>(); _light.type = (LightType)2; _light.color = Color; _light.range = Mathf.Clamp(extent * 3f + 3f, 4f, 12f); _light.intensity = 4f; _light.shadows = (LightShadows)0; _light.renderMode = (LightRenderMode)1; } if (options.Glow) { ApplyGlow(target, Color); } Update(); } public void Update() { if ((Object)(object)_light != (Object)null) { _light.intensity = 4f * (0.7f + 0.3f * Mathf.Sin(Time.time * 5f)) * Alpha; } } public void Remove() { foreach (Renderer item in _glowing) { if ((Object)(object)item != (Object)null) { item.SetPropertyBlock((MaterialPropertyBlock)null); } } _glowing.Clear(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } _light = null; } private void ApplyGlow(GameObject target, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) MaterialPropertyBlock val = new MaterialPropertyBlock(); Color val2 = color * 0.5f; Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren) { if (val3 is MeshRenderer || val3 is SkinnedMeshRenderer) { Material sharedMaterial = val3.sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && sharedMaterial.HasProperty(EmissionColorId)) { val3.GetPropertyBlock(val); val.SetColor(EmissionColorId, val2); val3.SetPropertyBlock(val); _glowing.Add(val3); } } } } private static Bounds BoundsOf(GameObject target, out bool found) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) Bounds result = default(Bounds); found = false; Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(false); foreach (Renderer val in componentsInChildren) { if (val.enabled && (val is MeshRenderer || val is SkinnedMeshRenderer)) { if (!found) { result = val.bounds; found = true; } else { ((Bounds)(ref result)).Encapsulate(val.bounds); } } } return result; } } public readonly struct HighlightOptions { public float Duration { get; } public bool Light { get; } public bool Glow { get; } public HighlightOptions(float duration, bool light, bool glow) { Duration = duration; Light = light; Glow = glow; } } } namespace TidyChests.Containers { internal static class ContainerAccess { public static bool CanStashInto(Container container, long playerId, bool multiUserChest, out string reason) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) reason = ""; if ((Object)(object)container == (Object)null || container.GetInventory() == null) { reason = "not initialised"; return false; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { reason = "no network object"; return false; } Player component = ((Component)nview).GetComponent<Player>(); if ((Object)(object)component != (Object)null && (Object)(object)component != (Object)(object)Player.m_localPlayer) { reason = "carried by another player"; return false; } if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { reason = "inside someone else's ward"; return false; } if (!container.CheckAccess(playerId)) { reason = "private chest of another player"; return false; } if (IsOpenByLocalPlayer(container) || multiUserChest) { return true; } if (container.IsInUse() || nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1) { reason = "in use by another player"; return false; } if ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse()) { reason = "cart in use"; return false; } if (!IsAlone() && (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null) { reason = "ship cargo in multiplayer without MultiUserChest"; return false; } return true; } public static bool PrepareForWrite(Container container, bool multiUserChest) { if (multiUserChest) { return true; } container.Load(); container.m_nview.ClaimOwnership(); return container.m_nview.IsOwner(); } public static bool IsOpenByLocalPlayer(Container container) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null) { return (Object)(object)instance.m_currentContainer == (Object)(object)container; } return false; } public static string NameOf(Container container) { Inventory inventory = container.GetInventory(); if (inventory != null) { return Localization.instance.Localize(inventory.GetName()); } return ((Object)container).name; } private static bool IsAlone() { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null)) { return instance.GetNrOfPlayers() <= 1; } return true; } } internal static class ContainerRegistry { private static readonly List<Container> All = new List<Container>(); public static int Count => All.Count; public static void Add(Container container) { if ((Object)(object)container != (Object)null && !All.Contains(container)) { All.Add(container); } } public static void Remove(Container container) { All.Remove(container); } public static void CollectNearby(Vector3 center, float radius, List<Container> result) { //IL_004c: 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_0052: 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) float num = radius * radius; Dictionary<Container, float> distances = new Dictionary<Container, float>(); for (int num2 = All.Count - 1; num2 >= 0; num2--) { Container val = All[num2]; if ((Object)(object)val == (Object)null) { All.RemoveAt(num2); } else { Vector3 val2 = ((Component)val).transform.position - center; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { result.Add(val); distances[val] = sqrMagnitude; } } } result.Sort((Container a, Container b) => distances[a].CompareTo(distances[b])); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class Container_Awake_Patch { private static void Postfix(Container __instance) { if (__instance.GetInventory() != null) { ContainerRegistry.Add(__instance); } } } [HarmonyPatch(typeof(Container), "OnDestroyed")] internal static class Container_OnDestroyed_Patch { private static void Postfix(Container __instance) { ContainerRegistry.Remove(__instance); } } } namespace TidyChests.Compat { internal static class ChestMods { public const string MultiUserChestGuid = "com.maxsch.valheim.MultiUserChest"; public static bool MultiUserChestLoaded => Chainloader.PluginInfos.ContainsKey("com.maxsch.valheim.MultiUserChest"); public static void Report() { if (MultiUserChestLoaded) { string text = Chainloader.PluginInfos["com.maxsch.valheim.MultiUserChest"].Metadata.Version.ToString(); Plugin.Log.LogInfo((object)("MultiUserChest " + text + " found: chests in use by other players are stashed into through its requests")); } } } internal static class SlotMods { internal abstract class Provider { private bool _resolved; private Func<ItemData, bool>? _classifier; public string Name { get; } public string PluginGuid { get; } public string SlotDescription { get; } public bool IsLoaded => Chainloader.PluginInfos.ContainsKey(PluginGuid); public string LoadedVersion { get { if (!Chainloader.PluginInfos.TryGetValue(PluginGuid, out var value) || ((value != null) ? value.Metadata : null) == null) { return "?"; } return value.Metadata.Version.ToString(); } } public Func<ItemData, bool>? Classifier { get { if (_resolved) { return _classifier; } _resolved = true; try { Assembly assembly = PluginAssembly(); _classifier = ((assembly == null) ? null : CreateClassifier(assembly)); } catch (Exception ex) { Plugin.Log.LogWarning((object)(Name + " API could not be resolved: " + ex.Message)); _classifier = null; } if (_classifier == null) { Plugin.Log.LogWarning((object)(Name + " " + LoadedVersion + " is installed but its API was not found or has changed. Update " + Name + "; its slots are not protected from stashing.")); } else { Plugin.Log.LogInfo((object)("Slot support: " + Name + " " + LoadedVersion + " (" + SlotDescription + " are never stashed)")); } return _classifier; } } protected Provider(string name, string pluginGuid, string slotDescription) { Name = name; PluginGuid = pluginGuid; SlotDescription = slotDescription; } protected abstract Func<ItemData, bool>? CreateClassifier(Assembly assembly); protected static MethodInfo? PublicStatic(Type api, string name, params Type[] parameters) { return api.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, parameters, null); } private Assembly? PluginAssembly() { if (Chainloader.PluginInfos.TryGetValue(PluginGuid, out var value) && (Object)(object)((value != null) ? value.Instance : null) != (Object)null) { return ((object)value.Instance).GetType().Assembly; } return null; } } private sealed class ExtraSlotsProvider : Provider { public ExtraSlotsProvider() : base("Extra Slots", "shudnal.ExtraSlots", "quick, food, ammo, misc and equipment slots") { } protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly) { Type type = assembly.GetType("ExtraSlots.API", throwOnError: false); MethodInfo isSlotPosition = ((type == null) ? null : Provider.PublicStatic(type, "IsGridPositionASlot", typeof(Vector2i))); if (isSlotPosition == null) { return null; } return delegate(ItemData item) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) object obj = isSlotPosition.Invoke(null, new object[1] { item.m_gridPos }); return obj is bool && (bool)obj; }; } } private sealed class EquipmentAndQuickSlotsProvider : Provider { public EquipmentAndQuickSlotsProvider() : base("EquipmentAndQuickSlots", "randyknapp.mods.equipmentandquickslots", "equipment and quick slots") { } protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly) { Type type = assembly.GetType("EquipmentAndQuickSlots.API", throwOnError: false); MethodInfo isSlotCell = ((type == null) ? null : Provider.PublicStatic(type, "IsSlotCell", typeof(int), typeof(int), typeof(string).MakeByRefType())); if (isSlotCell == null) { return null; } return delegate(ItemData item) { object obj = isSlotCell.Invoke(null, new object[3] { item.m_gridPos.x, item.m_gridPos.y, null }); return obj is bool && (bool)obj; }; } } private sealed class BetterArcheryProvider : Provider { public BetterArcheryProvider() : base("Better Archery", "ishid4.mods.betterarchery", "quiver rows") { } protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly) { Type type = assembly.GetTypes().FirstOrDefault((Type type2) => type2.IsClass && type2.Name == "BetterArchery"); FieldInfo enabled = type?.GetField("ConfigQuiverEnabled", BindingFlags.Static | BindingFlags.Public); FieldInfo rowIndex = type?.GetField("QuiverRowIndex", BindingFlags.Static | BindingFlags.Public); if (enabled == null || rowIndex == null) { return null; } return delegate(ItemData item) { if (!(enabled.GetValue(null) is ConfigEntry<bool> val) || !val.Value || !(rowIndex.GetValue(null) is int num) || num <= 0) { return false; } return item.m_gridPos.y == num || item.m_gridPos.y == num - 1; }; } } public const string ExtraSlotsGuid = "shudnal.ExtraSlots"; public const string EquipmentAndQuickSlotsGuid = "randyknapp.mods.equipmentandquickslots"; public const string BetterArcheryGuid = "ishid4.mods.betterarchery"; private static readonly Provider[] Providers = new Provider[3] { new ExtraSlotsProvider(), new EquipmentAndQuickSlotsProvider(), new BetterArcheryProvider() }; public static string SupportedMods => "Extra Slots, EquipmentAndQuickSlots 3.x, Better Archery"; public static IEnumerable<Provider> Active => Providers.Where((Provider provider) => provider.IsLoaded); public static void Report() { bool flag = false; foreach (Provider item in Active) { flag = true; _ = item.Classifier; } if (!flag) { Plugin.Log.LogInfo((object)("No slot mod loaded (" + SupportedMods + "); only equipped and hotbar items are protected")); } } public static Func<ItemData, bool>? GetClassifier() { List<KeyValuePair<string, Func<ItemData, bool>>> classifiers = new List<KeyValuePair<string, Func<ItemData, bool>>>(); foreach (Provider item in Active) { Func<ItemData, bool> classifier = item.Classifier; if (classifier != null) { classifiers.Add(new KeyValuePair<string, Func<ItemData, bool>>(item.Name, classifier)); } } if (classifiers.Count == 0) { return null; } return delegate(ItemData item) { foreach (KeyValuePair<string, Func<ItemData, bool>> item2 in classifiers) { try { if (item2.Value(item)) { return true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)(item2.Key + " API call failed for " + item.m_shared.m_name + ", treating it as a regular item: " + ex.Message)); } } return false; }; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }