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 ValheimChestSort v1.0.0
plugins/ValheimChestSort.dll
Decompiled 6 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.Events; 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("ValheimChestSort")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ValheimChestSort")] [assembly: AssemblyTitle("ValheimChestSort")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimChestSort; [BepInPlugin("SuperVikingDepartment.ValheimChestSort", "ValheimChestSort", "1.0.0")] public class ChestSortPlugin : BaseUnityPlugin { public const string PluginGuid = "SuperVikingDepartment.ValheimChestSort"; public const string PluginName = "ValheimChestSort"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<string> SortKeyName; internal static ConfigEntry<string> SortModeName; internal static ConfigEntry<bool> MergeStacks; internal static ConfigEntry<bool> AddButton; internal static ConfigEntry<string> ButtonLabel; internal static ConfigEntry<bool> DebugLogging; internal static KeyCode SortKey = (KeyCode)114; private static readonly FieldInfo FItems = AccessTools.Field(typeof(Inventory), "m_inventory"); private static readonly MethodInfo MChanged = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private static readonly FieldInfo FCurrentContainer = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private static readonly FieldInfo FDragItem = AccessTools.Field(typeof(InventoryGui), "m_dragItem"); private static GameObject _sortButtonGo; private static TMP_Text _sortLabel; private static readonly Dictionary<ItemType, int> CategoryRank = new Dictionary<ItemType, int> { { (ItemType)1, 10 }, { (ItemType)2, 20 }, { (ItemType)9, 30 }, { (ItemType)23, 31 }, { (ItemType)3, 40 }, { (ItemType)14, 41 }, { (ItemType)22, 42 }, { (ItemType)4, 43 }, { (ItemType)20, 44 }, { (ItemType)5, 50 }, { (ItemType)15, 51 }, { (ItemType)6, 60 }, { (ItemType)7, 61 }, { (ItemType)11, 62 }, { (ItemType)12, 63 }, { (ItemType)17, 64 }, { (ItemType)18, 70 }, { (ItemType)19, 71 }, { (ItemType)24, 72 }, { (ItemType)21, 73 }, { (ItemType)13, 74 }, { (ItemType)10, 75 }, { (ItemType)16, 80 }, { (ItemType)0, 90 } }; private void Awake() { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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) Log = ((BaseUnityPlugin)this).Logger; SortKeyName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SortKey", "R", "整理当前打开箱子的快捷键(按钮之外的备用入口)。填 KeyCode 名称,如 R / T / B / LeftAlt / Mouse3。改完需重启游戏。"); SortModeName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SortMode", "Category", "排序方式:Category = 先按类别(材料/消耗品/武器/护甲/工具…)再按名称;Name = 直接按名称。"); MergeStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "MergeStacks", true, "整理时是否合并同种物品的零散堆叠(补满已有堆叠),默认开启。"); AddButton = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AddButton", true, "是否在箱子界面「物品堆叠」按钮左边添加一个「整理」按钮。关闭后只能靠快捷键整理。改完需重启游戏。"); ButtonLabel = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ButtonLabel", "", "「整理」按钮上显示的文字。留空 = 自动:中文界面显示「整理」,其他语言显示「Sort」。"); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "DebugLogging", false, "输出整理细节日志,用于排查问题。正常游玩建议关闭。"); SortKey = ParseKey(SortKeyName.Value, (KeyCode)114); LogReflectionStatus(); Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(ApplyLabel)); Log.LogInfo((object)"ValheimChestSort loaded (v1.0.0)"); Log.LogInfo((object)($"[ChestSort] 快捷键 = {SortKey},排序方式 = {SortModeName.Value}," + $"合并堆叠 = {MergeStacks.Value},界面按钮 = {AddButton.Value}")); } private static KeyCode ParseKey(string raw, KeyCode fallback) { //IL_002b: 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_003b: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(raw) && Enum.TryParse<KeyCode>(raw.Trim(), ignoreCase: true, out KeyCode result)) { return result; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[ChestSort] 无法识别按键 \"{raw}\",回退为 {fallback}"); } return fallback; } private static void LogReflectionStatus() { if (FItems == null) { Log.LogError((object)"[ChestSort][diag] Inventory.m_inventory 未找到!"); } if (MChanged == null) { Log.LogError((object)"[ChestSort][diag] Inventory.Changed 未找到!"); } if (FCurrentContainer == null) { Log.LogError((object)"[ChestSort][diag] InventoryGui.m_currentContainer 未找到!"); } if (FDragItem == null) { Log.LogWarning((object)"[ChestSort][diag] InventoryGui.m_dragItem 未找到(拖拽保护将失效)"); } } private void OnDestroy() { Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(ApplyLabel)); } private void Update() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) InventoryGui instance = InventoryGui.instance; if (!((Object)(object)instance == (Object)null)) { if (AddButton.Value && (Object)(object)_sortButtonGo == (Object)null) { EnsureSortButton(instance); } if ((Object)(object)_sortButtonGo != (Object)null && _sortButtonGo.activeSelf != instance.IsContainerOpen()) { _sortButtonGo.SetActive(instance.IsContainerOpen()); } if (instance.IsContainerOpen() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && ZInput.GetKeyDown(SortKey, false)) { TrySortOpenedContainer(); } } } private static void EnsureSortButton(InventoryGui gui) { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown if ((Object)(object)gui.m_stackAllButton == (Object)null) { return; } Transform transform = ((Component)gui.m_stackAllButton).transform; Transform parent = transform.parent; if ((Object)(object)parent == (Object)null) { return; } Transform val = parent.Find("ChestSortButton"); if ((Object)(object)val != (Object)null) { _sortButtonGo = ((Component)val).gameObject; _sortLabel = ((Component)val).GetComponentInChildren<TMP_Text>(true) ?? ((Component)transform).GetComponentInChildren<TMP_Text>(true); ApplyLabel(); return; } GameObject val2; try { val2 = Object.Instantiate<GameObject>(((Component)transform).gameObject, parent); } catch (Exception ex) { Log.LogError((object)("[ChestSort] 创建界面按钮失败:" + ex)); return; } ((Object)val2).name = "ChestSortButton"; val2.transform.SetSiblingIndex(transform.GetSiblingIndex()); Button component = val2.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((UnityEventBase)component.onClick).RemoveAllListeners(); ((UnityEvent)component.onClick).AddListener(new UnityAction(OnSortButtonClicked)); ((Selectable)component).interactable = true; } else { Log.LogWarning((object)"[ChestSort] 克隆出的按钮对象上没有 Button 组件"); } val2.AddComponent<SortButtonPlacement>().Bind((RectTransform)(object)((transform is RectTransform) ? transform : null)); RectTransform val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val3); } _sortButtonGo = val2; _sortLabel = val2.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)_sortLabel == (Object)null) { Log.LogWarning((object)"[ChestSort] 未找到按钮文字组件,将沿用原按钮外观"); } ApplyLabel(); if (DebugLogging.Value) { bool flag = (Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null; Log.LogInfo((object)($"[ChestSort] 已添加界面按钮,父节点={((Object)parent).name},父节点带布局组={flag}," + string.Format("按钮数={0},按钮文字={1}", parent.childCount, ((Object)(object)_sortLabel != (Object)null) ? _sortLabel.text : "<无>"))); } } private static void OnSortButtonClicked() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsTeleporting()) { TrySortOpenedContainer(); } } private static void ApplyLabel() { if ((Object)(object)_sortLabel == (Object)null) { return; } string text = ((ButtonLabel != null) ? ButtonLabel.Value : null); if (string.IsNullOrEmpty(text)) { text = (IsChinese() ? "整理" : "Sort"); } _sortLabel.text = text; try { Localization instance = Localization.instance; if (instance != null) { instance.RemoveTextFromCache(_sortLabel); } } catch { } } private static bool IsChinese() { try { Localization instance = Localization.instance; string text = ((instance != null) ? instance.GetSelectedLanguage() : null); return text == "Chinese" || text == "Chinese_Trad"; } catch { return false; } } internal static string L(string zh, string en) { if (!IsChinese()) { return en; } return zh; } internal static bool TrySortOpenedContainer() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || !instance.IsContainerOpen()) { return false; } Container currentContainer = GetCurrentContainer(instance); if ((Object)(object)currentContainer == (Object)null) { return false; } if (IsDraggingItem(instance)) { Message(L("请先放下手中的物品再整理", "Put down the item you are dragging first")); return false; } Inventory inventory = currentContainer.GetInventory(); if (inventory == null || inventory.NrOfItems() == 0) { return false; } if (!currentContainer.IsOwner()) { Message(L("暂时无法整理,请重新打开箱子", "Cannot sort right now, reopen the container")); Log.LogWarning((object)"[ChestSort] 容器不是本地所有,跳过整理"); return false; } int num; try { num = SortInventory(inventory, MergeStacks.Value, SortModeName.Value); } catch (Exception ex) { Log.LogError((object)("[ChestSort] 整理失败:" + ex)); Message(L("整理失败,详见日志", "Sort failed, see log")); return false; } Message(L($"箱子已整理({num} 格)", $"Chest sorted ({num} slots)")); if (DebugLogging.Value) { Log.LogInfo((object)$"[ChestSort] sorted {num} slots"); } return true; } private static Container GetCurrentContainer(InventoryGui gui) { if (!(FCurrentContainer != null)) { return null; } object? value = FCurrentContainer.GetValue(gui); return (Container)((value is Container) ? value : null); } private static bool IsDraggingItem(InventoryGui gui) { if (FDragItem != null) { return FDragItem.GetValue(gui) != null; } return false; } internal static void Message(string msg) { try { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, msg, 0, (Sprite)null, false); } } catch { } } internal static int SortInventory(Inventory inv, bool merge, string mode) { //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) List<ItemData> list = new List<ItemData>(inv.GetAllItems()); if (list.Count == 0) { return 0; } List<ItemData> list2 = new List<ItemData>(list.Count); foreach (ItemData item in list) { if (item == null) { continue; } if (merge && item.m_shared.m_maxStackSize > 1) { for (int i = 0; i < list2.Count; i++) { ItemData val = list2[i]; if (val.m_shared.m_maxStackSize > 1 && val.m_stack < val.m_shared.m_maxStackSize && SameStackKind(val, item)) { int num = Math.Min(val.m_shared.m_maxStackSize - val.m_stack, item.m_stack); val.m_stack += num; item.m_stack -= num; if (item.m_stack <= 0) { break; } } } } if (item.m_stack > 0) { list2.Add(item); } } bool byName = string.Equals(mode, "Name", StringComparison.OrdinalIgnoreCase); list2.Sort((ItemData a, ItemData b) => CompareItems(a, b, byName)); int num2 = Math.Max(1, inv.GetWidth()); for (int num3 = 0; num3 < list2.Count; num3++) { list2[num3].m_gridPos = new Vector2i(num3 % num2, num3 / num2); } List<ItemData> itemList = GetItemList(inv); itemList.Clear(); itemList.AddRange(list2); InvokeChanged(inv); return list2.Count; } private static int CompareItems(ItemData a, ItemData b, bool byName) { //IL_000e: 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 (!byName) { int value; int num = (CategoryRank.TryGetValue(a.m_shared.m_itemType, out value) ? value : 85); int value3; int value2 = (CategoryRank.TryGetValue(b.m_shared.m_itemType, out value3) ? value3 : 85); int num2 = num.CompareTo(value2); if (num2 != 0) { return num2; } } int num3 = string.Compare(DisplayName(a), DisplayName(b), StringComparison.CurrentCultureIgnoreCase); if (num3 != 0) { return num3; } int num4 = b.m_quality.CompareTo(a.m_quality); if (num4 != 0) { return num4; } int num5 = b.m_worldLevel.CompareTo(a.m_worldLevel); if (num5 != 0) { return num5; } return b.m_stack.CompareTo(a.m_stack); } private static bool SameStackKind(ItemData a, ItemData b) { if (a.m_shared.m_name != b.m_shared.m_name) { return false; } if (a.m_quality != b.m_quality) { return false; } if (a.m_worldLevel != b.m_worldLevel) { return false; } if (a.m_variant != b.m_variant) { return false; } Dictionary<string, string> customData = a.m_customData; Dictionary<string, string> customData2 = b.m_customData; int num = customData?.Count ?? 0; int num2 = customData2?.Count ?? 0; if (num != num2) { return false; } if (num == 0) { return true; } foreach (KeyValuePair<string, string> item in customData) { if (!customData2.TryGetValue(item.Key, out var value) || value != item.Value) { return false; } } return true; } private static string DisplayName(ItemData item) { try { Localization instance = Localization.instance; if (instance != null) { return instance.Localize(item.m_shared.m_name); } } catch { } return item.m_shared.m_name; } private static List<ItemData> GetItemList(Inventory inv) { return (List<ItemData>)FItems.GetValue(inv); } private static void InvokeChanged(Inventory inv) { MChanged.Invoke(inv, new object[2] { false, false }); } } internal class SortButtonPlacement : MonoBehaviour { private RectTransform _self; private RectTransform _target; private bool _sized; public void Bind(RectTransform target) { ref RectTransform self = ref _self; Transform transform = ((Component)this).transform; self = (RectTransform)(object)((transform is RectTransform) ? transform : null); _target = target; } private void LateUpdate() { //IL_006c: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_00dd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_self == (Object)null || (Object)(object)_target == (Object)null) { return; } Transform parent = ((Transform)_self).parent; if (!((Object)(object)parent != (Object)null) || !((Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null)) { if (!_sized) { _self.sizeDelta = _target.sizeDelta; _sized = true; } Rect rect = _target.rect; float num; if (!(((Rect)(ref rect)).width > 1f)) { num = _target.sizeDelta.x; } else { rect = _target.rect; num = ((Rect)(ref rect)).width; } float num2 = num; if (!(num2 <= 1f)) { _self.anchoredPosition = new Vector2(_target.anchoredPosition.x - num2 - 6f, _target.anchoredPosition.y); } } } }