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 QuickSort v0.1.16
QuickSort.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ChatCommandAPI; using ChatCommandAPI.Utils; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("0Harmony")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("BepInEx")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("UnityEngine.IMGUIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("QuickSort")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("made by Asta")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+281fa2489a2dceffcae1bcc94896ce9370550825")] [assembly: AssemblyProduct("QuickSort")] [assembly: AssemblyTitle("QuickSort")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace QuickSort { public static class Chat { [HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool OnChatSubmit(HUDManager __instance, CallbackContext context) { if (!((CallbackContext)(ref context)).performed || !Player.Local.isTypingChat) { return true; } string text = __instance.chatTextField.text; if (text == "/help") { string text2 = "Commands: "; text2 += string.Join(", ", ChatCommand.Commands); Log.Chat(text2); CloseChat(__instance); return false; } if (text.StartsWith("/")) { text = text.Substring(1).Trim(); foreach (ChatCommand command in ChatCommand.Commands) { if (text.StartsWith(command.keyword)) { CloseChat(__instance); try { command.action(command.GetArgs(text)); } catch (Exception e) { Log.Exception(e); } return false; } } } return true; } public static void CloseChat(HUDManager instance) { instance.localPlayer.isTypingChat = false; instance.chatTextField.text = ""; EventSystem.current.SetSelectedGameObject((GameObject)null); ((Behaviour)instance.typingIndicator).enabled = false; } } public abstract class Argument { public string name; } public class Argument<T> : Argument { public T value; public Argument(string name, T value) { base.name = name; this.value = value; } public static implicit operator T(Argument<T> arg) { return arg.value; } } public struct ChatArgs { public Argument[] arguments; public string help; public int Length => arguments.Length; public bool Empty => arguments.Length == 0; public bool this[string name] => Get<bool>(name); public string this[int name] => Get<string>(name); public T Get<T>(string name) { Argument[] array = arguments; Argument[] array2 = array; foreach (Argument argument in array2) { if (argument.name == name) { return ((Argument<T>)argument).value; } } return default(T); } public T Get<T>(int name) { Argument[] array = arguments; Argument[] array2 = array; foreach (Argument argument in array2) { if (argument.name == name.ToString()) { return ((Argument<T>)argument).value; } } return default(T); } } public class ChatCommand { public static List<ChatCommand> Commands = new List<ChatCommand>(); public string keyword; public Action<ChatArgs> action; public string help; public ChatCommand(string keyword, string help, Action<ChatArgs> action) { this.keyword = keyword; this.help = help; this.action = action; } public static ChatCommand New(string keyword, string help, Action<ChatArgs> action) { ChatCommand chatCommand = new ChatCommand(keyword, help, action); Commands.Add(chatCommand); return chatCommand; } public ChatArgs GetArgs(string raw) { List<Argument> list = new List<Argument>(); string[] array = raw.Split(' '); keyword = array[0]; for (int i = 1; i < array.Length; i++) { if (array[i].StartsWith("-")) { string name = array[i].Substring(1); list.Add(new Argument<bool>(name, value: true)); } else { list.Add(new Argument<string>((i - 1).ToString(), array[i])); } } return new ChatArgs { arguments = list.ToArray(), help = help }; } public override string ToString() { return keyword; } } public static class Extensions { private static readonly Dictionary<string, string> NameAliases = BuildNameAliases(); private static string NormalizeKeyNoAlias(string s) { if (string.IsNullOrWhiteSpace(s)) { return ""; } return s.ToLower().Replace(" ", "_").Replace("-", "_") .Trim(); } private static Dictionary<string, string> BuildNameAliases() { Dictionary<string, string> d = new Dictionary<string, string>(); (string, string[])[] array = new(string, string[])[19] { ("boombox", new string[1] { "붐박스" }), ("flashlight", new string[1] { "손전등" }), ("jetpack", new string[1] { "제트팩" }), ("key", new string[1] { "열쇠" }), ("lockpicker", new string[1] { "자물쇠 따개" }), ("apparatus", new string[2] { "장치", "apparatice" }), ("pro_flashlight", new string[2] { "프로 손전등", "pro-flashlight" }), ("shovel", new string[1] { "철제 삽" }), ("stun_grenade", new string[2] { "기절 수류탄", "stun grenade" }), ("extension_ladder", new string[2] { "연장형 사다리", "extension ladder" }), ("tzp_inhalant", new string[2] { "tzp-흡입제", "tzp-inhalant" }), ("walkie_talkie", new string[2] { "무전기", "walkie-talkie" }), ("zap_gun", new string[2] { "잽건", "zap gun" }), ("radar_booster", new string[3] { "레이더 부스터", "radar booster", "radar-booster" }), ("spray_paint", new string[3] { "페인트 스프레이", "스프레이 페인트", "spray paint" }), ("shotgun", new string[1] { "산탄총" }), ("ammo", new string[1] { "탄약" }), ("clipboard", new string[1] { "클립보드" }), ("sticky_note", new string[3] { "스티커 메모", "스티커메모", "sticky note" }) }; (string, string[])[] array2 = array; for (int i = 0; i < array2.Length; i++) { (string, string[]) tuple = array2[i]; AddAliases(tuple.Item1, tuple.Item2); } (string, string[])[] array3 = new(string, string[])[9] { ("coffee_mug", new string[4] { "mug", "머그잔", "coffee mug", "커피 머그잔" }), ("hair_brush", new string[3] { "brush", "hair brush", "빗" }), ("brass_bell", new string[4] { "bell", "brass bell", "황동 종", "종" }), ("bee_hive", new string[3] { "hive", "bee hive", "벌집" }), ("wedding_ring", new string[3] { "ring", "wedding ring", "반지" }), ("robot_toy", new string[4] { "toy robot", "robot toy", "장난감 로봇", "로봇 장난감" }), ("rubber_ducky", new string[2] { "rubber ducky", "고무 오리" }), ("tattered_metal_sheet", new string[4] { "metal sheet", "tattered metal sheet", "금속 판", "너덜너덜한 금속 판" }), ("homemade_flashbang", new string[2] { "homemade flashbang", "사제 섬광탄" }) }; (string, string[])[] array4 = array3; for (int j = 0; j < array4.Length; j++) { (string, string[]) tuple2 = array4[j]; AddAliases(tuple2.Item1, tuple2.Item2); } (string, string[])[] array5 = new(string, string[])[31] { ("bubblegun", new string[1] { "비눗방울 총" }), ("broken_p88", new string[2] { "망가진 p88", "broken p88" }), ("employee", new string[1] { "직원" }), ("mine", new string[1] { "지뢰" }), ("toothles", new string[2] { "투슬리스", "toothles" }), ("crossbow", new string[2] { "석궁", "crossbow" }), ("physgun", new string[2] { "피직스건", "physgun" }), ("ammo_crate", new string[2] { "탄약 상자", "ammo crate" }), ("drink", new string[1] { "음료수" }), ("radio", new string[1] { "라디오" }), ("mouse", new string[1] { "마우스" }), ("monitor", new string[1] { "모니터" }), ("battery", new string[1] { "건전지" }), ("cannon", new string[1] { "대포" }), ("health_drink", new string[2] { "건강 음료", "health drink" }), ("chemical", new string[1] { "화학 약품" }), ("disinfecting_alcohol", new string[2] { "소독용 알코올", "disinfecting alcohol" }), ("ampoule", new string[1] { "앰풀" }), ("blood_pack", new string[2] { "혈액 팩", "blood pack" }), ("flip_lighter", new string[2] { "라이터", "flip lighter" }), ("rubber_ball", new string[2] { "고무 공", "rubber ball" }), ("video_tape", new string[2] { "비디오 테이프", "video tape" }), ("first_aid_kit", new string[2] { "구급 상자", "first aid kit" }), ("gold_medallion", new string[2] { "금메달", "gold medallion" }), ("steel_pipe", new string[2] { "금속 파이프", "steel pipe" }), ("axe", new string[1] { "도끼" }), ("emergency_hammer", new string[2] { "비상용 망치", "emergency hammer" }), ("katana", new string[1] { "카타나" }), ("silver_medallion", new string[2] { "은메달", "silver medallion" }), ("pocket_radio", new string[2] { "휴대용 라디오", "pocket radio" }), ("teddy_plush", new string[2] { "곰 인형", "teddy plush" }) }; (string, string[])[] array6 = array5; for (int k = 0; k < array6.Length; k++) { (string, string[]) tuple3 = array6[k]; AddAliases(tuple3.Item1, tuple3.Item2); } Add("마법의 7번 공", "Magic 7 ball"); Add("에어혼", "Airhorn"); Add("황동 종", "Brass bell"); Add("큰 나사", "Big bolt"); Add("병 묶음", "Bottles"); Add("빗", "Hair brush"); Add("사탕", "Candy"); Add("금전 등록기", "Cash register"); Add("화학 용기", "Chemical jug"); Add("광대 나팔", "Clown horn"); Add("대형 축", "Large axle"); Add("틀니", "Teeth"); Add("쓰레받기", "Dust pan"); Add("달걀 거품기", "Egg beater"); Add("v형 엔진", "V-type engine"); Add("황금 컵", "Golden cup"); Add("멋진 램프", "Fancy lamp"); Add("그림", "Painting"); Add("플라스틱 물고기", "Plastic fish"); Add("레이저 포인터", "Laser pointer"); Add("금 주괴", "Gold Bar"); Add("헤어 드라이기", "Hairdryer"); Add("돋보기", "Magnifying glass"); Add("너덜너덜한 금속 판", "Tattered metal sheet"); Add("쿠키 틀", "Cookie mold pan"); Add("머그잔", "Coffee mug"); Add("커피 머그잔", "Coffee mug"); Add("향수 병", "Perfume bottle"); Add("구식 전화기", "Old phone"); Add("피클 병", "Jar of pickles"); Add("약 병", "Pill bottle"); Add("리모컨", "Remote"); Add("결혼 반지", "Wedding ring"); Add("로봇 장난감", "Robot Toy"); Add("고무 오리", "Rubber ducky"); Add("빨간색 소다", "Red soda"); Add("운전대", "Steering wheel"); Add("정지 표지판", "Stop sign"); Add("찻주전자", "Tea Kettle"); Add("치약", "Toothpaste"); Add("장난감 큐브", "Toy cube"); Add("벌집", "Bee hive"); Add("양보 표지판", "Yield sign"); Add("산탄총", "Shotgun"); Add("더블 배럴", "Double-barrel"); Add("산탄총 탄약", "ammo"); Add("탄약", "ammo"); Add("사제 섬광탄", "Homemade Flashbang"); Add("선물", "Gift"); Add("선물 상자", "Gift box"); Add("플라스크", "Flask"); Add("비극", "Tragedy"); Add("희극", "Comedy"); Add("방귀 쿠션", "Whoopie cushion"); Add("방퀴 쿠션", "Whoopie cushion"); Add("식칼", "Kitchen knife"); Add("부활절 달걀", "Easter egg"); Add("제초제", "Weed killer"); Add("벨트 배낭", "Belt bag"); Add("축구공", "Soccer ball"); Add("조작 패드", "Control pad"); Add("쓰레기통 뚜껑", "Garbage lid"); Add("플라스틱 컵", "Plastic cup"); Add("화장실 휴지", "Toilet paper"); Add("장난감 기차", "Toy train"); Add("제드 도그", "Zed Dog"); Add("시계", "Clock"); Add("시체", "Body"); Add("알", "Egg"); Add("열쇠", "Key"); Add("데이터 칩", "Data chip"); Add("교육용 지침서", "Training manual"); Add("장치", "Apparatus"); Add("장치", "Apparatice"); Add("알코올 플라스크", "Alcohol Flask"); Add("모루", "Anvil"); Add("야구 방망이", "Baseball bat"); Add("맥주 캔", "Beer can"); Add("벽돌", "Brick"); Add("망가진 엔진", "Broken engine"); Add("양동이", "Bucket"); Add("페인트 캔", "Can paint"); Add("수통", "Canteen"); Add("자동차 배터리", "Car battery"); Add("조임틀", "Clamp"); Add("멋진 그림", "Fancy Painting"); Add("선풍기", "Fan"); Add("소방 도끼", "Fireaxe"); Add("소화기", "Fire extinguisher"); Add("소화전", "Fire hydrant"); Add("통조림", "Food can"); Add("게임보이", "Gameboy"); Add("쓰레기", "Garbage"); Add("망치", "Hammer"); Add("기름통", "Jerrycan"); Add("키보드", "Keyboard"); Add("랜턴", "Lantern"); Add("도서관 램프", "Library lamp"); Add("식물", "Plant"); Add("플라이어", "Pliers"); Add("뚫어뻥", "Plunger"); Add("레트로 장난감", "Retro Toy"); Add("스크류 드라이버", "Screwdriver"); Add("싱크대", "Sink"); Add("소켓 렌치", "Socket Wrench"); Add("여행 가방", "Suitcase"); Add("토스터기", "Toaster"); Add("공구 상자", "Toolbox"); Add("실크햇", "Top hat"); Add("라바콘", "Traffic cone"); Add("환풍구", "Vent"); Add("물뿌리개", "Watering Can"); Add("바퀴", "Wheel"); Add("와인 병", "Wine bottle"); Add("렌치", "Wrench"); Add("자수정 군집", "Amethyst Cluster"); Add("주사기", "Syringe"); Add("주사기총", "Syringe Gun"); Add("코너 파이프", "Corner Pipe"); Add("작은 파이프", "Small Pipe"); Add("파이프", "Flow Pipe"); Add("뇌가 담긴 병", "Brain Jar"); Add("호두까기 인형 장난감", "Toy Nutcracker"); Add("시험관", "Test Tube"); Add("시험관 랙", "Test Tube Rack"); Add("호두까기 인형 눈", "Nutcracker Eye"); Add("파란색 시험관", "Blue Test Tube"); Add("노란색 시험관", "Yellow Test Tube"); Add("빨간색 시험관", "Red Test Tube"); Add("초록색 시험관", "Green Test Tube"); Add("쇠지렛대", "Crowbar"); Add("플젠", "Plzen"); Add("컵", "Cup"); Add("전자레인지", "Microwave"); Add("hyper acid 실험 기록", "Experiment Log Hyper Acid"); Add("희극 가면 실험 기록", "Experiment Log Comedy Mask"); Add("저주받은 동전 실험 기록", "Experiment Log Cursed Coin"); Add("바이오 hxnv7 실험 기록", "Experiment Log BIO HXNV7"); Add("파란색 폴더", "Blue Folder"); Add("빨간색 폴더", "Red Folder"); Add("코일", "Coil"); Add("타자기", "Typewriter"); Add("서류 더미", "Documents"); Add("스테이플러", "Stapler"); Add("구식 컴퓨터", "Old Computer"); Add("브론즈 트로피", "Bronze Trophy"); Add("바나나", "Banana"); Add("스턴봉", "Stun Baton"); Add("바이오-hxnv7", "BIO-HXNV7"); Add("복구된 비밀 일지", "Recovered Secret Log"); Add("황금 단검 실험 기록", "Experiment Log Golden Dagger"); Add("대합", "Clam"); Add("거북이 등딱지", "Turtle Shell"); Add("생선 뼈", "Fish Bones"); Add("뿔 달린 껍질", "Horned Shell"); Add("도자기 찻잔", "Porcelain Teacup"); Add("대리석", "Marble"); Add("도자기 병", "Porcelain Bottle"); Add("도자기 향수 병", "Porcelain Perfume Bottle"); Add("발광구", "Glowing Orb"); Add("황금 해골", "Golden Skull"); Add("코스모코스 지도", "Map of Cosmocos"); Add("젖은 노트 1", "Wet Note 1"); Add("젖은 노트 2", "Wet Note 2"); Add("젖은 노트 3", "Wet Note 3"); Add("젖은 노트 4", "Wet Note 4"); Add("우주빛 파편", "Cosmic Shard"); Add("우주 생장물", "Cosmic Growth"); Add("천상의 두뇌 덩어리", "Chunk of Celestial Brain"); Add("파편이 든 양동이", "Bucket of Shards"); Add("우주빛 손전등", "Cosmic Flashlight"); Add("잊혀진 일지 1", "Forgotten Log 1"); Add("잊혀진 일지 2", "Forgotten Log 2"); Add("잊혀진 일지 3", "Forgotten Log 3"); Add("안경", "Glasses"); Add("생장한 배양 접시", "Grown Petri Dish"); Add("배양 접시", "Petri Dish"); Add("코스모채드", "Cosmochad"); Add("죽어가는 우주빛 손전등", "Dying Cosmic Flashlight"); Add("죽어가는 우주 생장물", "Dying Cosmic Growth"); Add("혈액 배양 접시", "Blood Petri Dish"); Add("악마 코스모채드", "Evil Cosmochad"); Add("악마 코스모", "Evil Cosmo"); Add("릴 코스모", "Lil Cosmo"); Add("죽어가는 생장물 배양 접시", "Dying Grown Petri Dish"); Add("감시하는 배양 접시", "Watching Petri Dish"); Add("현미경", "Microscope"); Add("원통형 바일", "Round Vile"); Add("사각형 바일", "Square Vile"); Add("타원형 바일", "Oval Vile"); Add("해링턴 일지 1", "Harrington Log 1"); Add("해링턴 일지 2", "Harrington Log 2"); Add("해링턴 일지 3", "Harrington Log 3"); Add("해링턴 일지 4", "Harrington Log 4"); Add("생장물이 든 병", "Jar of Growth"); Add("테이프 플레이어 일지 1", "Tape Player Log 1"); Add("테이프 플레이어 일지 2", "Tape Player Log 2"); Add("테이프 플레이어 일지 3", "Tape Player Log 3"); Add("테이프 플레이어 일지 4", "Tape Player Log 4"); Add("쇼핑 카트", "Shopping Cart"); return d; void Add(string localized, string canonical) { string text = NormalizeKeyNoAlias(localized); string value = NormalizeKeyNoAlias(canonical); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(value) && !d.ContainsKey(text)) { d.Add(text, value); } } void AddAliases(string canonicalKey, params string[] aliases) { Add(canonicalKey, canonicalKey); if (aliases != null) { foreach (string localized in aliases) { Add(localized, canonicalKey); } } } } public static string NormalizeName(string s) { string text = NormalizeKeyNoAlias(s); string value; return NameAliases.TryGetValue(text, out value) ? value : text; } public static string Name(this GrabbableObject item) { return NormalizeName(item.itemProperties.itemName); } public static string Name(this Item item) { return NormalizeName(item.itemName); } } [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] internal static class GrabPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGenerator) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); CodeMatcher val = new CodeMatcher((IEnumerable<CodeInstruction>)list, ilGenerator); CodeInstruction val2 = null; MethodInfo methodInfo = AccessTools.Method(typeof(NetworkBehaviour), "get_NetworkObject", (Type[])null, (Type[])null); for (int i = 0; i < list.Count - 1; i++) { CodeInstruction val3 = list[i]; if (val3.opcode == OpCodes.Callvirt && val3.operand is MethodInfo methodInfo2 && methodInfo2 == methodInfo) { CodeInstruction val4 = list[i + 1]; if (IsStloc(val4.opcode)) { val2 = val4.Clone(); break; } } } if (val2 == null) { val2 = new CodeInstruction(OpCodes.Stloc_0, (object)null); } Label label = default(Label); return val.MatchForward(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(GrabbableObject), "InteractItem", (Type[])null, (Type[])null), (string)null) }).MatchBack(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null) }).Insert((CodeInstruction[])(object)new CodeInstruction[4] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(PlayerControllerB), "currentlyGrabbingObject")), new CodeInstruction(OpCodes.Callvirt, (object)methodInfo), val2 }) .ThrowIfInvalid("QuickSort: BeginGrabObject pattern not found (InteractItem)") .CreateLabel(ref label) .Start() .Insert((CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Player), "OverrideGrabbingObject", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Brtrue, (object)label) }) .InstructionEnumeration(); } private static bool IsStloc(OpCode op) { return op == OpCodes.Stloc || op == OpCodes.Stloc_S || op == OpCodes.Stloc_0 || op == OpCodes.Stloc_1 || op == OpCodes.Stloc_2 || op == OpCodes.Stloc_3; } } internal static class InteractionLockPatch { [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] [HarmonyPrefix] private static bool BlockBeginGrabObject() { if (!Sorter.IsInteractionLocked) { return true; } if (Sorter.IsInteractionBypassActive) { return true; } return false; } [HarmonyPatch(typeof(PlayerControllerB), "DiscardHeldObject")] [HarmonyPrefix] private static bool BlockDiscardHeldObject() { if (!Sorter.IsInteractionLocked) { return true; } if (Sorter.IsInteractionBypassActive) { return true; } return false; } } public class Log { private static ManualLogSource _log; public static void Init(ManualLogSource log) { _log = log; } public static void NotifyPlayer(string header, string body = "", bool isWarning = false) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(header, body, isWarning, false, "LC_Tip1"); } Debug(header); Debug(body); } public static void Chat(string body, string color = "FFFFFF") { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.AddChatMessage("<color=#" + color + ">[Pasta] " + body + "</color>", "", -1, false); } Debug(body); } public static void ConfirmSound() { if ((Object)(object)HUDManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance != (Object)null) { HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonTuneSFX); } } public static void Exception(Exception e) { string message = e.Message; string stackTrace = e.StackTrace; _log.LogError((object)message); _log.LogError((object)stackTrace); } public static void Error(params object[] objects) { _log.LogError((object)string.Join(" ", objects)); } public static void Warning(params object[] objects) { _log.LogWarning((object)string.Join(" ", objects)); } public static void Info(params object[] objects) { _log.LogInfo((object)string.Join(" ", objects)); } public static void Debug(params object[] objects) { _log.LogDebug((object)string.Join(" ", objects)); } } internal static class MoveUtils { private static bool ShouldAvoidHeldSideEffects(PlayerControllerB player, GrabbableObject targetItem) { if ((Object)(object)player == (Object)null) { return true; } if ((Object)(object)targetItem == (Object)null) { return true; } GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; return (Object)(object)currentlyHeldObjectServer != (Object)null && (Object)(object)currentlyHeldObjectServer != (Object)(object)targetItem; } public static bool MoveItemOnShip(GrabbableObject item, Vector3 worldPos, int floorYRot = -1) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return false; } GameObject val2 = GameObject.Find("Environment/HangarShip"); if ((Object)(object)val2 == (Object)null) { return false; } Vector3 val3 = val2.transform.InverseTransformPoint(worldPos); ((Component)item).transform.position = worldPos; if (!ShouldAvoidHeldSideEffects(val, item)) { val.SetObjectAsNoLongerHeld(true, true, val3, item, floorYRot); } val.ThrowObjectServerRpc(NetworkObjectReference.op_Implicit(((NetworkBehaviour)item).NetworkObject), true, true, val3, floorYRot); return true; } public static bool MoveItemOnShipLocal(GrabbableObject item, Vector3 shipLocalPos, int floorYRot = -1) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_0064: 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_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return false; } GameObject val2 = GameObject.Find("Environment/HangarShip"); if ((Object)(object)val2 == (Object)null) { return false; } ((Component)item).transform.position = val2.transform.TransformPoint(shipLocalPos); try { if (!ShouldAvoidHeldSideEffects(val, item)) { val.SetObjectAsNoLongerHeld(true, true, shipLocalPos, item, floorYRot); } } catch { } try { val.PlaceGrabbableObject(val2.transform, shipLocalPos, false, item); val.PlaceObjectServerRpc(NetworkObjectReference.op_Implicit(((NetworkBehaviour)item).NetworkObject), NetworkObjectReference.op_Implicit(val2), shipLocalPos, false); } catch { try { val.ThrowObjectServerRpc(NetworkObjectReference.op_Implicit(((NetworkBehaviour)item).NetworkObject), true, true, shipLocalPos, floorYRot); } catch { return false; } } return true; } public static bool TryTeleportToWorld(GameObject go, Vector3 worldPos, Quaternion worldRot) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) GrabbableObject val = (((Object)(object)go != (Object)null) ? go.GetComponent<GrabbableObject>() : null); if ((Object)(object)val != (Object)null) { return MoveItemOnShip(val, worldPos, val.floorYRot); } if ((Object)(object)go == (Object)null) { return false; } go.transform.SetPositionAndRotation(worldPos, worldRot); return true; } } public static class Player { public static GrabbableObject overrideObject; public static PlayerControllerB Local => StartOfRound.Instance?.localPlayerController; public static bool CanGrabObject(GrabbableObject item) { if ((Object)(object)item == (Object)null || !item.grabbable || item.deactivated || item.isHeld || item.isPocketed) { return false; } if ((Object)(object)Local == (Object)null || Local.isPlayerDead || Local.isTypingChat || Local.inTerminalMenu || Local.throwingObject || Local.IsInspectingItem || Local.isGrabbingObjectAnimation || (Object)(object)Local.inAnimationWithEnemy != (Object)null || Local.inSpecialInteractAnimation || Local.jetpackControls || Local.disablingJetpackControls || Local.activatingItem || Local.waitingToDropItem || Local.FirstEmptyItemSlot((GrabbableObject)null) == -1) { return false; } return true; } public static bool OverrideGrabbingObject() { if ((Object)(object)overrideObject == (Object)null) { return false; } Local.currentlyGrabbingObject = overrideObject; overrideObject = null; return true; } public static IEnumerator StartGrabbingObject(GrabbableObject grabbableObject) { if (CanGrabObject(grabbableObject)) { overrideObject = grabbableObject; Local.BeginGrabObject(); yield return Local.grabObjectCoroutine; } } public static IEnumerator StartMovingObject(GrabbableObject item, Vector3 position, NetworkObject? parent = null) { //IL_000e: 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) yield return StartGrabbingObject(item); if ((Object)(object)Local == (Object)null) { yield break; } int frames = 0; while (frames < 30 && ((Object)(object)Local.currentlyHeldObjectServer == (Object)null || (Object)(object)Local.currentlyHeldObjectServer != (Object)(object)item)) { frames++; yield return null; } if ((Object)(object)Local.currentlyHeldObjectServer == (Object)null || (Object)(object)Local.currentlyHeldObjectServer != (Object)(object)item) { Log.Warning("Failed to grab " + (item?.itemProperties?.itemName ?? "item") + "; skipping move to avoid crash."); yield break; } try { item.floorYRot = -1; Local.DiscardHeldObject(true, parent, position, false); } catch (Exception e) { Log.Exception(e); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("pasta.quicksort", "QuickSort", "0.1.16")] public class Plugin : BaseUnityPlugin { private const string CurrentConfigSchemaVersion = "0.1.9"; public static ManualLogSource Log; public static ConfigFile config; public static ConfigEntry<string> configVersion; private static Harmony harmony; public static GameObject sorterObject; private static bool applicationIsQuitting; public static Plugin Instance { get; private set; } private void Awake() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Expected O, but got Unknown //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Expected O, but got Unknown applicationIsQuitting = false; Instance = this; Log = ((BaseUnityPlugin)this).Logger; config = ((BaseUnityPlugin)this).Config; bool flag = File.Exists(config.ConfigFilePath); ConfigEntry<string> val = default(ConfigEntry<string>); bool flag2 = config.TryGetEntry<string>(new ConfigDefinition("General", "configVersion"), ref val); configVersion = config.Bind<string>("General", "configVersion", "0.1.9", "Config schema version (used for internal migrations)."); bool flag3 = false; string text = configVersion.Value ?? ""; if (string.IsNullOrWhiteSpace(text)) { text = "0.0.0"; configVersion.Value = text; flag3 = true; } bool flag4 = flag && (!flag2 || IsVersionLessThan(text, "0.1.9")); if (flag4 && (!flag2 || IsVersionLessThan(text, "0.1.5"))) { ConfigEntry<float> val2 = config.Bind<float>("Sorter", "sortOriginY", 0.1f, "Y coordinate of the origin position for sorting items (relative to ship)"); if (Mathf.Abs(val2.Value - 0.5f) < 0.0001f) { val2.Value = 0.1f; flag3 = true; } } ConfigEntry<string> val3 = default(ConfigEntry<string>); if (flag4 && (!flag2 || IsVersionLessThan(text, "0.1.7")) && config.TryGetEntry<string>(new ConfigDefinition("Sorter", "skippedItems"), ref val3) && IsOnlyShotgunAmmo(val3.Value)) { val3.Value = "body, clipboard, sticky_note, boombox, shovel, jetpack, flashlight, pro_flashlight, key, stun_grenade, lockpicker, mapper, extension_ladder, tzp_inhalant, walkie_talkie, zap_gun, kitchen_knife, weed_killer, radar_booster, spray_paint, belt_bag"; flag3 = true; } if (flag4 && (!flag2 || IsVersionLessThan(text, "0.1.9"))) { ConfigEntry<string> val4 = config.Bind<string>("Sorter", "skippedItems", "body, clipboard, sticky_note, boombox, shovel, jetpack, flashlight, pro_flashlight, key, stun_grenade, lockpicker, mapper, extension_ladder, tzp_inhalant, walkie_talkie, zap_gun, kitchen_knife, weed_killer, radar_booster, spray_paint, belt_bag", "Global skip list (comma-separated, substring match). Applies to all grabbable items."); string text2 = RemoveTokensFromCommaList(val4.Value, "shotgun", "ammo", "double_barrel", "shotgun_shell"); if (!string.Equals(text2, val4.Value, StringComparison.Ordinal)) { val4.Value = text2; flag3 = true; } } if (flag4 && !string.Equals(configVersion.Value, "0.1.9", StringComparison.Ordinal)) { configVersion.Value = "0.1.9"; flag3 = true; } if (flag3) { config.Save(); } QuickSort.Log.Init(((BaseUnityPlugin)this).Logger); QuickSort.Log.Info("QuickSort - Item Sorter loading..."); SortShortcuts.EnsureFileExists(); SortPositions.EnsureFileExists(); harmony = new Harmony("pasta.quicksort"); harmony.PatchAll(typeof(Ship)); harmony.PatchAll(typeof(Startup)); harmony.PatchAll(typeof(GrabPatch)); harmony.PatchAll(typeof(InteractionLockPatch)); try { Startup.RegisterCommandsOnce(); QuickSort.Log.Info("Sort command registered in Awake"); } catch (Exception ex) { QuickSort.Log.Error("Failed to register sort command in Awake: " + ex.Message); QuickSort.Log.Error("Stack trace: " + ex.StackTrace); } try { if ((Object)(object)sorterObject == (Object)null) { sorterObject = new GameObject("PastaSorter"); sorterObject.AddComponent<Sorter>(); Object.DontDestroyOnLoad((Object)(object)sorterObject); QuickSort.Log.Info("Sorter initialized in Awake"); } } catch (Exception ex2) { QuickSort.Log.Error("Failed to initialize Sorter in Awake: " + ex2.Message); QuickSort.Log.Error("Stack trace: " + ex2.StackTrace); } QuickSort.Log.Info("QuickSort - Item Sorter loaded!"); } private void OnDestroy() { if (Instance == this) { Instance = null; } if (!applicationIsQuitting) { QuickSort.Log.Warning("Plugin OnDestroy fired before application quit; skipping cleanup to avoid self-unpatching."); return; } if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)sorterObject != (Object)null) { Object.Destroy((Object)(object)sorterObject); } } private void OnApplicationQuit() { applicationIsQuitting = true; } private static bool IsVersionLessThan(string a, string b) { int[] array = Parse(a); int[] array2 = Parse(b); for (int i = 0; i < 3; i++) { if (array[i] < array2[i]) { return true; } if (array[i] > array2[i]) { return false; } } return false; static int[] Parse(string s) { if (string.IsNullOrWhiteSpace(s)) { return new int[3]; } string[] array3 = s.Trim().Split('.'); int[] array4 = new int[3]; for (int j = 0; j < 3; j++) { if (j < array3.Length && int.TryParse(array3[j], out var result)) { array4[j] = result; } else { array4[j] = 0; } } return array4; } } private static string RemoveTokensFromCommaList(string? list, params string[] tokensToRemove) { List<string> list2 = new List<string>(); HashSet<string> hashSet = new HashSet<string>(); HashSet<string> hashSet2 = new HashSet<string>(); if (tokensToRemove != null) { foreach (string raw in tokensToRemove) { string text = Normalize(raw); if (!string.IsNullOrWhiteSpace(text)) { hashSet2.Add(text); } } } if (!string.IsNullOrWhiteSpace(list)) { string[] array = list.Split(','); foreach (string raw2 in array) { string text2 = Normalize(raw2); if (!string.IsNullOrWhiteSpace(text2) && !hashSet2.Contains(text2) && hashSet.Add(text2)) { list2.Add(text2); } } } return string.Join(", ", list2); static string Normalize(string text4) { string text3 = (text4 ?? "").Trim(); if (string.IsNullOrWhiteSpace(text3)) { return ""; } text3 = Extensions.NormalizeName(text3).Trim('_'); return string.IsNullOrWhiteSpace(text3) ? "" : text3; } } private static bool IsOnlyShotgunAmmo(string? list) { HashSet<string> seen = new HashSet<string>(); if (!string.IsNullOrWhiteSpace(list)) { string[] array = list.Split(','); foreach (string raw in array) { Add(raw); } } return seen.Count == 2 && seen.Contains("shotgun") && seen.Contains("ammo"); void Add(string text2) { string text = (text2 ?? "").Trim(); if (!string.IsNullOrWhiteSpace(text)) { text = Extensions.NormalizeName(text).Trim('_'); if (!string.IsNullOrWhiteSpace(text)) { seen.Add(text); } } } } } public static class Startup { private static bool commandRegistered; public static void RegisterCommandsOnce() { if (!commandRegistered) { new SortCommand(); new SortBindCommand(); new SortSetCommand(); new SortResetCommand(); new SortPositionsCommand(); new SortBindingsListCommand(); new SortSkipCommand(); new PileCommand(); commandRegistered = true; } } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] private static void OnLocalPlayerCreated(PlayerControllerB __instance) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown if ((Object)(object)__instance != (Object)(object)StartOfRound.Instance.localPlayerController) { return; } if (!commandRegistered) { try { RegisterCommandsOnce(); Log.Info("Sort command registered"); } catch (Exception ex) { Log.Error("Failed to register sort command: " + ex.Message); } } if ((Object)(object)Plugin.sorterObject != (Object)null) { Object.Destroy((Object)(object)Plugin.sorterObject); } Plugin.sorterObject = new GameObject("PastaSorter"); Plugin.sorterObject.AddComponent<Sorter>(); Object.DontDestroyOnLoad((Object)(object)Plugin.sorterObject); } } public static class Ship { public static Action OnShipOrbit; public static Action OnShipTouchdown; public static Action OnShipAscent; public static Action OnShipDescent; private static readonly Dictionary<string, FieldInfo?> _startOfRoundBoolFields = new Dictionary<string, FieldInfo>(); private static readonly Dictionary<string, PropertyInfo?> _startOfRoundBoolProps = new Dictionary<string, PropertyInfo>(); private static readonly Dictionary<string, MemberInfo?> _startOfRoundBoolFuzzy = new Dictionary<string, MemberInfo>(); public static bool Stationary { get { if (GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded") == true) { return true; } if (GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase") == true && GetStartOfRoundBool("shipIsLeaving", "ShipIsLeaving", "isShipLeaving", "IsShipLeaving", "shipLeaving") != true) { return true; } return false; } } public static bool CanSortNow { get { if (GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded") == true) { return true; } if (GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase") == true) { return true; } return InOrbit || Stationary; } } public static bool InOrbit => GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase") == true && GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded") != true; public static bool IsCompanyLevel => CurrentLevelTextContains("Gordion", "Company", "CompanyBuilding"); public static bool ShouldSortAllDetectedItems => InOrbit || IsCompanyLevel; private static bool? GetStartOfRoundBoolExact(string name) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } Type type = ((object)instance).GetType(); if (!_startOfRoundBoolFields.TryGetValue(name, out FieldInfo value)) { value = AccessTools.Field(type, name); _startOfRoundBoolFields[name] = value; } if (value != null) { try { if (value.GetValue(instance) is bool value2) { return value2; } } catch { } } if (!_startOfRoundBoolProps.TryGetValue(name, out PropertyInfo value3)) { value3 = AccessTools.Property(type, name); _startOfRoundBoolProps[name] = value3; } if (value3 != null && value3.PropertyType == typeof(bool) && value3.GetIndexParameters().Length == 0) { try { if (value3.GetValue(instance, null) is bool value4) { return value4; } } catch { } } return null; } private static bool? GetStartOfRoundBoolFuzzy(string containsOrAltName) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } Type type = ((object)instance).GetType(); if (!_startOfRoundBoolFuzzy.TryGetValue(containsOrAltName, out MemberInfo value)) { string needle = containsOrAltName; FieldInfo fieldInfo = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(bool) && string.Equals(f.Name, needle, StringComparison.OrdinalIgnoreCase)); if (fieldInfo != null) { value = fieldInfo; } else { PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(bool) && p.GetIndexParameters().Length == 0 && string.Equals(p.Name, needle, StringComparison.OrdinalIgnoreCase)); value = propertyInfo; } if (value == null) { FieldInfo fieldInfo2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(bool) && f.Name.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0); if (fieldInfo2 != null) { value = fieldInfo2; } else { PropertyInfo propertyInfo2 = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(bool) && p.GetIndexParameters().Length == 0 && p.Name.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0); value = propertyInfo2; } } _startOfRoundBoolFuzzy[containsOrAltName] = value; } try { if (value is FieldInfo fieldInfo3 && fieldInfo3.FieldType == typeof(bool)) { return (bool)fieldInfo3.GetValue(instance); } if (value is PropertyInfo propertyInfo3 && propertyInfo3.PropertyType == typeof(bool) && propertyInfo3.GetIndexParameters().Length == 0) { return (bool)propertyInfo3.GetValue(instance, null); } } catch { } return null; } private static bool? GetStartOfRoundBool(params string[] candidates) { foreach (string text in candidates) { if (!string.IsNullOrWhiteSpace(text)) { bool? startOfRoundBoolExact = GetStartOfRoundBoolExact(text); if (startOfRoundBoolExact.HasValue) { return startOfRoundBoolExact; } } } foreach (string text2 in candidates) { if (!string.IsNullOrWhiteSpace(text2)) { bool? startOfRoundBoolFuzzy = GetStartOfRoundBoolFuzzy(text2); if (startOfRoundBoolFuzzy.HasValue) { return startOfRoundBoolFuzzy; } } } return null; } private static object? GetCurrentLevel() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } Type type = ((object)instance).GetType(); try { FieldInfo fieldInfo = type.GetField("currentLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("CurrentLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo != null) { return fieldInfo.GetValue(instance); } PropertyInfo propertyInfo = type.GetProperty("currentLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperty("CurrentLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (propertyInfo != null && propertyInfo.GetIndexParameters().Length == 0) { return propertyInfo.GetValue(instance, null); } } catch { } return null; } private static bool CurrentLevelTextContains(params string[] needles) { object currentLevel = GetCurrentLevel(); if (currentLevel == null) { return false; } try { Type type = currentLevel.GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string value = null; if (fieldInfo.FieldType == typeof(string)) { value = fieldInfo.GetValue(currentLevel) as string; } else if (fieldInfo.FieldType == typeof(int) && string.Equals(fieldInfo.Name, "levelID", StringComparison.OrdinalIgnoreCase)) { value = fieldInfo.GetValue(currentLevel)?.ToString(); } if (ContainsAny(value, needles)) { return true; } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.GetIndexParameters().Length == 0) { string value2 = null; if (propertyInfo.PropertyType == typeof(string)) { value2 = propertyInfo.GetValue(currentLevel, null) as string; } else if (propertyInfo.PropertyType == typeof(int) && string.Equals(propertyInfo.Name, "levelID", StringComparison.OrdinalIgnoreCase)) { value2 = propertyInfo.GetValue(currentLevel, null)?.ToString(); } if (ContainsAny(value2, needles)) { return true; } } } } catch { } return false; } private static bool ContainsAny(string? value, string[] needles) { if (string.IsNullOrWhiteSpace(value)) { return false; } foreach (string value2 in needles) { if (!string.IsNullOrWhiteSpace(value2) && value.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnShipLeave() { OnShipAscent?.Invoke(); OnShipDescent?.Invoke(); } [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnShipLanded() { OnShipTouchdown?.Invoke(); OnShipOrbit?.Invoke(); } } public class Sorter : MonoBehaviour { private sealed class DisposeAction : IDisposable { private readonly Action _onDispose; private bool _disposed; public DisposeAction(Action onDispose) { _onDispose = onDispose; } public void Dispose() { if (!_disposed) { _disposed = true; _onDispose?.Invoke(); } } } public const string DefaultSkippedItems = "body, clipboard, sticky_note, boombox, shovel, jetpack, flashlight, pro_flashlight, key, stun_grenade, lockpicker, mapper, extension_ladder, tzp_inhalant, walkie_talkie, zap_gun, kitchen_knife, weed_killer, radar_booster, spray_paint, belt_bag"; private static readonly HashSet<string> LowerYOffsetTypes = new HashSet<string> { "toilet_paper", "chemical_jug", "cash_register", "fancy_lamp", "large_axle", "v_type_engine" }; public ConfigEntry<float> sortOriginX; public ConfigEntry<float> sortOriginY; public ConfigEntry<float> sortOriginZ; public ConfigEntry<float> itemSpacing; public ConfigEntry<float> rowSpacing; public ConfigEntry<int> itemsPerRow; public ConfigEntry<string> skippedItems; public ConfigEntry<float> sortAreaWidth; public ConfigEntry<float> sortAreaDepth; public ConfigEntry<float> wallPadding; public ConfigEntry<bool> stackSameTypeTogether; public ConfigEntry<float> sameTypeStackStepY; private List<GrabbableObject> scrap; public static bool inProgress; private static bool _interactionLocked; private static int _interactionBypassCount; private static List<(string name, bool isProperty, float value)>? _savedWeight; private Vector3 SortOrigin => new Vector3(sortOriginX.Value, sortOriginY.Value, sortOriginZ.Value); private bool CanSort => true; internal static bool IsInteractionLocked => _interactionLocked; internal static bool IsInteractionBypassActive => _interactionBypassCount > 0; private static bool ShouldLowerYOffset(GrabbableObject item) { if ((Object)(object)item == (Object)null) { return false; } string text = item.Name(); if (string.IsNullOrWhiteSpace(text)) { return false; } return LowerYOffsetTypes.Contains(text); } private static string ApplyDefaultInputAliases(string normalizedKey) { if (string.IsNullOrWhiteSpace(normalizedKey)) { return normalizedKey; } if (1 == 0) { } string result = ((normalizedKey == "double_barrel") ? "shotgun" : ((!(normalizedKey == "shotgun_shell")) ? normalizedKey : "ammo")); if (1 == 0) { } return result; } internal static bool EnsureLocalPlayerInShip(out string? error) { //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) error = null; PlayerControllerB local = Player.Local; if ((Object)(object)local == (Object)null) { error = "Local player not ready yet"; return false; } try { Type type = ((object)local).GetType(); string[] array = new string[6] { "isInHangarShipRoom", "IsInHangarShipRoom", "isInShipRoom", "IsInShipRoom", "inShipRoom", "InShipRoom" }; string[] array2 = array; foreach (string name in array2) { bool? flag = TryGetBool(local, type, name); if (flag.HasValue) { if (!flag.Value) { error = "You must be inside the ship to use this command."; return false; } return true; } } } catch { } GameObject val = GameObject.Find("Environment/HangarShip"); if ((Object)(object)val == (Object)null) { error = "Ship not found"; return false; } Vector3 val2 = val.transform.InverseTransformPoint(((Component)local).transform.position); if (!(val2.x >= -10f) || !(val2.x <= 10f) || !(val2.z >= -20f) || !(val2.z <= 10f) || !(val2.y >= -6f) || !(val2.y <= 10f)) { error = "You must be inside the ship to use this command."; return false; } return true; static bool? TryGetBool(object obj2, Type type2, string name2) { FieldInfo field = type2.GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { return (bool)field.GetValue(obj2); } PropertyInfo property = type2.GetProperty(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0) { return (bool)property.GetValue(obj2, null); } return null; } } private void Awake() { sortOriginX = Plugin.config.Bind<float>("Sorter", "sortOriginX", -2.8f, "X coordinate of the origin position for sorting items (relative to ship)"); sortOriginY = Plugin.config.Bind<float>("Sorter", "sortOriginY", 0.1f, "Y coordinate of the origin position for sorting items (relative to ship)"); sortOriginZ = Plugin.config.Bind<float>("Sorter", "sortOriginZ", -4.8f, "Z coordinate of the origin position for sorting items (relative to ship)"); itemSpacing = Plugin.config.Bind<float>("Sorter", "itemSpacing", 0.8f, "Spacing between items horizontally"); rowSpacing = Plugin.config.Bind<float>("Sorter", "rowSpacing", 0.8f, "Spacing between rows vertically"); itemsPerRow = Plugin.config.Bind<int>("Sorter", "itemsPerRow", 9, "Number of items per row"); skippedItems = Plugin.config.Bind<string>("Sorter", "skippedItems", "body, clipboard, sticky_note, boombox, shovel, jetpack, flashlight, pro_flashlight, key, stun_grenade, lockpicker, mapper, extension_ladder, tzp_inhalant, walkie_talkie, zap_gun, kitchen_knife, weed_killer, radar_booster, spray_paint, belt_bag", "Global skip list (comma-separated, substring match). Applies to all grabbable items."); skippedItems.Value = NormalizeSkipListConfig(skippedItems.Value); sortAreaWidth = Plugin.config.Bind<float>("Sorter", "sortAreaWidth", 9f, "Reserved: used only for future bounding. (Keeping for compatibility)"); sortAreaDepth = Plugin.config.Bind<float>("Sorter", "sortAreaDepth", 6f, "How far forward (Z) types are allowed to expand from sortOriginZ before starting a new type-layer above."); wallPadding = Plugin.config.Bind<float>("Sorter", "wallPadding", 0.25f, "Padding used for depth calculation (prevents placing type rows into doors/walls)."); stackSameTypeTogether = Plugin.config.Bind<bool>("Sorter", "stackSameTypeTogether", true, "If true, items of the same type will be stacked at the exact same X/Z position (only Y increases), instead of being spread in a small grid."); sameTypeStackStepY = Plugin.config.Bind<float>("Sorter", "sameTypeStackStepY", 0f, "Vertical spacing between items when stackSameTypeTogether is enabled. Set to 0 for exact overlap; increase (e.g. 0.1~0.2) if physics makes items push apart."); } internal static IDisposable BeginInteractionBypass() { _interactionBypassCount++; return new DisposeAction(delegate { _interactionBypassCount = Math.Max(0, _interactionBypassCount - 1); }); } private static void CapturePlayerWeightIfNeeded() { if (_savedWeight != null) { return; } PlayerControllerB local = Player.Local; if ((Object)(object)local == (Object)null) { return; } Type type = ((object)local).GetType(); List<(string, bool, float)> list = new List<(string, bool, float)>(); try { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.FieldType != typeof(float)) && IsCarryWeightName(fieldInfo.Name)) { list.Add((fieldInfo.Name, false, (float)fieldInfo.GetValue(local))); } } } catch { } try { PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (!(propertyInfo.PropertyType != typeof(float)) && propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.CanRead && propertyInfo.CanWrite && IsCarryWeightName(propertyInfo.Name)) { list.Add((propertyInfo.Name, true, (float)propertyInfo.GetValue(local, null))); } } } catch { } if (list.Count > 0) { _savedWeight = list; } static bool IsCarryWeightName(string n) { return n != null && n.IndexOf("carry", StringComparison.OrdinalIgnoreCase) >= 0 && n.IndexOf("weight", StringComparison.OrdinalIgnoreCase) >= 0; } } private static void RestorePlayerWeightIfNeeded() { if (_savedWeight == null) { return; } PlayerControllerB local = Player.Local; if ((Object)(object)local == (Object)null) { _savedWeight = null; return; } Type type = ((object)local).GetType(); foreach (var item in _savedWeight) { try { if (!item.isProperty) { FieldInfo field = type.GetField(item.name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(float)) { field.SetValue(local, item.value); } } else { PropertyInfo property = type.GetProperty(item.name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(float) && property.GetIndexParameters().Length == 0 && property.CanWrite) { property.SetValue(local, item.value, null); } } } catch { } } _savedWeight = null; } private static void BeginOperationLock() { if (!_interactionLocked) { CapturePlayerWeightIfNeeded(); _interactionLocked = true; } } private static void EndOperationLock() { _interactionLocked = false; _interactionBypassCount = 0; RestorePlayerWeightIfNeeded(); } private IEnumerator DropHeldItemIfAny(GameObject ship) { PlayerControllerB player = Player.Local; if ((Object)(object)player == (Object)null) { yield break; } GrabbableObject held = player.currentlyHeldObjectServer; if ((Object)(object)held == (Object)null) { yield break; } NetworkObject shipNetObj = (((Object)(object)ship != (Object)null) ? ship.GetComponent<NetworkObject>() : null); Vector3 dropLocal = ship.transform.InverseTransformPoint(((Component)player).transform.position + ((Component)player).transform.forward * 0.75f); if (TryGetGroundYLocalAt(dropLocal, out float groundYLocal, out string _)) { float vOff = 0f; try { if ((Object)(object)held.itemProperties != (Object)null) { vOff = held.itemProperties.verticalOffset - 0.05f; } } catch { } dropLocal.y = groundYLocal + vOff; } using (BeginInteractionBypass()) { held.floorYRot = -1; player.DiscardHeldObject(true, shipNetObj, dropLocal, false); } int frames = 0; while (frames < 30 && (Object)(object)player.currentlyHeldObjectServer != (Object)null) { frames++; yield return null; } } private void Update() { if (inProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { inProgress = false; Log.Chat("Sorting cancelled", "FF0000"); } if (_interactionLocked && !inProgress) { EndOperationLock(); } } private void SortCommandHandler(string[] args) { if (args.Length != 0 && args[0] == "help") { Log.Chat("Usage: /sort [help]", "FFFF00"); return; } if (inProgress) { Log.NotifyPlayer("Sorter Error", "Operation in progress", isWarning: true); return; } CategorizeItems(); Log.ConfirmSound(); ((MonoBehaviour)this).StartCoroutine(SortItems(force: false)); } public IEnumerator SortItems(bool force, bool ignoreSkippedItems = false, bool includeSavedPositionTypesEvenIfSkipped = false) { inProgress = true; Log.Chat("Press [Escape] to cancel sorting", "FFFF00"); if ((Object)(object)Player.Local == (Object)null) { Log.NotifyPlayer("Sorter Error", "Local player not ready yet", isWarning: true); inProgress = false; yield break; } GameObject ship = GameObject.Find("Environment/HangarShip"); if ((Object)(object)ship == (Object)null) { Log.NotifyPlayer("Sorter Error", "Ship not found", isWarning: true); inProgress = false; yield break; } BeginOperationLock(); Vector3 originLocal = SortOrigin; HashSet<string> savedTypes = null; string posListError; List<(string itemKey, Vector3 shipLocalPos)> savedPositions = SortPositions.ListAll(out posListError); if (posListError != null) { Log.Warning(posListError); } else { savedTypes = new HashSet<string>(savedPositions.Select<(string, Vector3), string>(((string itemKey, Vector3 shipLocalPos) p) => p.itemKey)); } Dictionary<string, List<GrabbableObject>> groupedItems = new Dictionary<string, List<GrabbableObject>>(); foreach (GrabbableObject item in scrap) { string itemName = item.Name(); bool ignoreSkipTokensForThisItem = ignoreSkippedItems || (includeSavedPositionTypesEvenIfSkipped && savedTypes != null && savedTypes.Contains(itemName)); if (!ShouldSkipFullSort(item, ignoreSkipTokensForThisItem)) { if (!groupedItems.ContainsKey(itemName)) { groupedItems[itemName] = new List<GrabbableObject>(); } groupedItems[itemName].Add(item); } } List<KeyValuePair<string, List<GrabbableObject>>> orderedGroups = (from kvp in groupedItems orderby kvp.Value != null && kvp.Value.Any(IsTwoHandedItem) descending, kvp.Key select kvp).ToList(); List<string> orderedTypeNames = orderedGroups.Select((KeyValuePair<string, List<GrabbableObject>> kvp) => kvp.Key).ToList(); HashSet<string> reservedTypes = savedTypes; Dictionary<string, Vector3> layout = CreateLayout(orderedTypeNames, reservedTypes); RaycastHit hitCenter = default(RaycastHit); foreach (KeyValuePair<string, List<GrabbableObject>> group in orderedGroups) { string itemName2 = group.Key; List<GrabbableObject> items = group.Value; Vector3 typePos = (layout.ContainsKey(itemName2) ? layout[itemName2] : Vector3.zero); Vector3 customShipLocal; string posError; bool hasCustomPos = SortPositions.TryGet(itemName2, out customShipLocal, out posError); if (posError != null) { Log.Warning(posError); } bool ignoreSkipTokensForThisType = includeSavedPositionTypesEvenIfSkipped && hasCustomPos; Vector3 pileCenterLocal = (Vector3)(hasCustomPos ? new Vector3(customShipLocal.x, 0f, customShipLocal.z) : (originLocal + new Vector3(typePos.x, 0f, typePos.z))); float customYOffset = (hasCustomPos ? customShipLocal.y : 0f); float typeLayerYOffset = (hasCustomPos ? 0f : typePos.y); float originYOffset = (hasCustomPos ? 0f : originLocal.y); float groundYLocal = pileCenterLocal.y; Vector3 rayStartCenter = ship.transform.TransformPoint(pileCenterLocal + Vector3.up * 2f); if (Physics.Raycast(rayStartCenter, Vector3.down, ref hitCenter, 80f, 268437761, (QueryTriggerInteraction)1)) { groundYLocal = ship.transform.InverseTransformPoint(((RaycastHit)(ref hitCenter)).point).y; } for (int stackIndex = 0; stackIndex < items.Count; stackIndex++) { GrabbableObject item2 = items[stackIndex]; if (ShouldBreak(item2)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; yield break; } float pileX = 0f; float pileZ = 0f; float pileY; if (stackSameTypeTogether.Value) { pileY = (float)stackIndex * Mathf.Max(0f, sameTypeStackStepY.Value); } else { int cols = Mathf.Max(1, itemsPerRow.Value); int rows = cols; int perLayer = cols * rows; int layer = stackIndex / perLayer; int inLayer = stackIndex % perLayer; int r = inLayer / cols; int c = inLayer % cols; pileX = ((float)c - (float)(cols - 1) / 2f) * 0.1f; pileZ = ((float)r - (float)(rows - 1) / 2f) * 0.1f; pileY = (float)layer * 0.07f; } Vector3 targetLocal = new Vector3(pileCenterLocal.x + pileX, groundYLocal + originYOffset + (item2.itemProperties.verticalOffset - 0.1f) + (typeLayerYOffset + customYOffset) + pileY - (ShouldLowerYOffset(item2) ? 0.2f : 0f), pileCenterLocal.z + pileZ); Vector3 worldPos = ship.transform.TransformPoint(targetLocal); if (!force && Vector3.Distance(worldPos, ((Component)item2).transform.position) < 0.25f) { continue; } yield return GrabbableRetry(item2); if (!ShouldSkipFullSort(item2, ignoreSkippedItems || ignoreSkipTokensForThisType)) { item2.floorYRot = -1; if (!MoveUtils.MoveItemOnShipLocal(item2, targetLocal, item2.floorYRot)) { Log.Warning("Failed to move " + (item2.itemProperties?.itemName ?? ((Object)item2).name)); } int retry = 15; while (!Player.CanGrabObject(item2) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } } posError = null; } Log.Chat("Sorting complete!", "00FF00"); inProgress = false; } private static bool IsTwoHandedItem(GrabbableObject item) { try { if ((Object)(object)item == (Object)null) { return false; } Item itemProperties = item.itemProperties; if ((Object)(object)itemProperties == (Object)null) { return false; } Type type = ((object)itemProperties).GetType(); FieldInfo fieldInfo = type.GetField("twoHanded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("isTwoHanded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("twoHandedItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("<twoHanded>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("<isTwoHanded>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("<twoHandedItem>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { return (bool)fieldInfo.GetValue(itemProperties); } PropertyInfo propertyInfo = type.GetProperty("twoHanded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperty("isTwoHanded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperty("twoHandedItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool) && propertyInfo.GetIndexParameters().Length == 0) { return (bool)propertyInfo.GetValue(itemProperties, null); } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo2 in fields) { if (!(fieldInfo2.FieldType != typeof(bool)) && fieldInfo2.Name.IndexOf("twohand", StringComparison.OrdinalIgnoreCase) >= 0) { return (bool)fieldInfo2.GetValue(itemProperties); } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo2 in properties) { if (!(propertyInfo2.PropertyType != typeof(bool)) && propertyInfo2.GetIndexParameters().Length == 0 && propertyInfo2.Name.IndexOf("twohand", StringComparison.OrdinalIgnoreCase) >= 0) { return (bool)propertyInfo2.GetValue(itemProperties, null); } } return false; } catch { return false; } } public bool TryStartGatherByQuery(string query, bool force, out string? error) { //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) error = null; if (inProgress) { error = "Operation in progress"; return false; } if (string.IsNullOrWhiteSpace(query)) { error = "Missing item name"; return false; } CategorizeItems(includeSkippedItems: true); if (scrap == null || scrap.Count == 0) { error = "No items found in ship"; return false; } Dictionary<string, List<GrabbableObject>> dictionary = new Dictionary<string, List<GrabbableObject>>(); foreach (GrabbableObject item in scrap) { if (!ShouldSkipExplicitQuery(item)) { string key = item.Name(); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<GrabbableObject>()); } value.Add(item); } } if (!TryResolveItemKeyFromGrouped(dictionary, query, out string resolvedKey, out error)) { return false; } if (!TryGetPlayerShipLocalTarget(out var shipLocalTarget, out error)) { return false; } if (!TryGetGroundYLocalAt(shipLocalTarget, out float groundYLocal, out string error2)) { error = error2; return false; } Vector3 targetCenterShipLocal = default(Vector3); ((Vector3)(ref targetCenterShipLocal))..ctor(shipLocalTarget.x, shipLocalTarget.y - groundYLocal, shipLocalTarget.z); ((MonoBehaviour)this).StartCoroutine(MoveItemsOfTypeToPosition(resolvedKey, targetCenterShipLocal, force, announce: true, ignoreSkipLists: true, applyTwoHandedSortYOffset: true)); return true; } public bool TryStartPileByQueryOrHeld(string? queryOrNull, bool force, out string? error) { //IL_01d7: 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_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) error = null; if (inProgress) { error = "Operation in progress"; return false; } GrabbableObject val = (((Object)(object)Player.Local != (Object)null) ? Player.Local.currentlyHeldObjectServer : null); string text = ((!string.IsNullOrWhiteSpace(queryOrNull)) ? queryOrNull : (((Object)(object)val != (Object)null) ? val.Name() : "")); bool dropHeldFirst = string.IsNullOrWhiteSpace(queryOrNull); if (string.IsNullOrWhiteSpace(text)) { error = "Missing item name (hold the item or provide a name)."; return false; } CategorizeItems(includeSkippedItems: true); Dictionary<string, List<GrabbableObject>> dictionary = new Dictionary<string, List<GrabbableObject>>(); if (scrap != null) { foreach (GrabbableObject item in scrap) { if (!ShouldSkipExplicitQuery(item)) { string key = item.Name(); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<GrabbableObject>()); } value.Add(item); } } } if ((Object)(object)val != (Object)null && !ShouldSkipExplicitQuery(val)) { string key2 = val.Name(); if (!dictionary.TryGetValue(key2, out var value2)) { value2 = (dictionary[key2] = new List<GrabbableObject>()); } if (!value2.Contains(val)) { value2.Add(val); } } if (dictionary.Count == 0) { error = "No items found in ship"; return false; } if (!TryResolveItemKeyFromGrouped(dictionary, text, out string resolvedKey, out error)) { return false; } if (!TryGetPlayerShipLocalTarget(out var shipLocalTarget, out error)) { return false; } if (!TryGetGroundYLocalAt(shipLocalTarget, out float groundYLocal, out string error2)) { error = error2; return false; } Vector3 targetCenterShipLocal = default(Vector3); ((Vector3)(ref targetCenterShipLocal))..ctor(shipLocalTarget.x, shipLocalTarget.y - groundYLocal, shipLocalTarget.z); ((MonoBehaviour)this).StartCoroutine(MoveItemsOfTypeToPosition(resolvedKey, targetCenterShipLocal, force, announce: true, ignoreSkipLists: true, applyTwoHandedSortYOffset: false, dropHeldFirst)); return true; } public bool TrySetAndMoveTypeToPlayer(string? queryOrNull, bool force, out string resolvedItemKey, out string? error) { //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) resolvedItemKey = ""; error = null; if (inProgress) { error = "Operation in progress"; return false; } GrabbableObject val = (((Object)(object)Player.Local != (Object)null) ? Player.Local.currentlyHeldObjectServer : null); if (!string.IsNullOrWhiteSpace(queryOrNull)) { CategorizeItems(includeSkippedItems: true); if ((scrap == null || scrap.Count == 0) && (Object)(object)val == (Object)null) { error = "No items found in ship to match that name (hold the item or omit the name)."; return false; } Dictionary<string, List<GrabbableObject>> dictionary = new Dictionary<string, List<GrabbableObject>>(); if (scrap != null) { foreach (GrabbableObject item in scrap) { if (!ShouldSkipExplicitQuery(item)) { string key = item.Name(); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<GrabbableObject>()); } value.Add(item); } } } if ((Object)(object)val != (Object)null && !ShouldSkipExplicitQuery(val)) { string key2 = val.Name(); if (!dictionary.TryGetValue(key2, out var value2)) { value2 = (dictionary[key2] = new List<GrabbableObject>()); } if (!value2.Contains(val)) { value2.Add(val); } } if (dictionary.Count == 0) { error = "No valid items found to match that name."; return false; } if (!TryResolveItemKeyFromGrouped(dictionary, queryOrNull, out resolvedItemKey, out error)) { return false; } } else { if ((Object)(object)val == (Object)null) { error = "No item name provided and no held item."; return false; } resolvedItemKey = val.Name(); } bool dropHeldFirst = string.IsNullOrWhiteSpace(queryOrNull); if (string.IsNullOrWhiteSpace(resolvedItemKey)) { error = "Invalid item name"; return false; } if (!TryGetPlayerShipLocalTarget(out var shipLocalTarget, out error)) { return false; } if (!TryGetGroundYLocalAt(shipLocalTarget, out float groundYLocal, out string error2)) { error = error2; return false; } Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(shipLocalTarget.x, shipLocalTarget.y - groundYLocal, shipLocalTarget.z); if (!SortPositions.Set(resolvedItemKey, val2, out error)) { return false; } Log.ConfirmSound(); ((MonoBehaviour)this).StartCoroutine(MoveItemsOfTypeToPosition(resolvedItemKey, val2, force, announce: true, ignoreSkipLists: true, applyTwoHandedSortYOffset: false, dropHeldFirst)); return true; } private bool TryResolveItemKeyFromGrouped(Dictionary<string, List<GrabbableObject>> grouped, string query, out string resolvedKey, out string? error) { resolvedKey = ""; error = null; string q = ApplyDefaultInputAliases(Extensions.NormalizeName(query)); if (string.IsNullOrWhiteSpace(q)) { error = "Invalid item name"; return false; } if (grouped.ContainsKey(q)) { resolvedKey = q; return true; } List<string> source = grouped.Keys.ToList(); string qLoose = q.Replace("_", ""); List<string> list = source.Where(delegate(string k) { if (k.Contains(q) || q.Contains(k)) { return true; } string text2 = k.Replace("_", ""); return text2.Contains(qLoose) || qLoose.Contains(text2); }).Distinct().ToList(); if (list.Count == 1) { resolvedKey = list[0]; return true; } if (list.Count == 0) { error = "No item match for '" + query + "'."; return false; } string text = string.Join(", ", list.Take(8)); if (list.Count > 8) { text += ", ..."; } error = "Ambiguous item '" + query + "'. Matches: " + text; return false; } private bool TryGetPlayerShipLocalTarget(out Vector3 shipLocalTarget, out string? error) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0085: Unknown result type (might be due to invalid IL or missing references) shipLocalTarget = default(Vector3); error = null; if ((Object)(object)Player.Local == (Object)null) { error = "Local player not ready yet"; return false; } GameObject val = GameObject.Find("Environment/HangarShip"); if ((Object)(object)val == (Object)null) { error = "Ship not found"; return false; } Vector3 val2 = ((Component)Player.Local).transform.position + ((Component)Player.Local).transform.forward * 0.75f; shipLocalTarget = val.transform.InverseTransformPoint(val2); return true; } private bool TryGetGroundYLocalAt(Vector3 shipLocalXZ, out float groundYLocal, out string? error) { //IL_0035: 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_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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_006c: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) groundYLocal = 0f; error = null; GameObject val = GameObject.Find("Environment/HangarShip"); if ((Object)(object)val == (Object)null) { error = "Ship not found"; return false; } Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(shipLocalXZ.x, 0f, shipLocalXZ.z); Vector3 val3 = val.transform.TransformPoint(val2 + Vector3.up * 2f); RaycastHit val4 = default(RaycastHit); if (Physics.Raycast(val3, Vector3.down, ref val4, 80f, 268437761, (QueryTriggerInteraction)1)) { groundYLocal = val.transform.InverseTransformPoint(((RaycastHit)(ref val4)).point).y; return true; } groundYLocal = 0f; return true; } private IEnumerator MoveItemsOfTypeToPosition(string itemKey, Vector3 targetCenterShipLocal, bool force, bool announce, bool ignoreSkipLists = false, bool applyTwoHandedSortYOffset = false, bool dropHeldFirst = false) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) inProgress = true; if (announce) { Log.Chat("Moving '" + itemKey + "' to your position... Press [Escape] to cancel", "FFFF00"); } if ((Object)(object)Player.Local == (Object)null) { Log.NotifyPlayer("Sorter Error", "Local player not ready yet", isWarning: true); inProgress = false; yield break; } GameObject ship = GameObject.Find("Environment/HangarShip"); if ((Object)(object)ship == (Object)null) { Log.NotifyPlayer("Sorter Error", "Ship not found", isWarning: true); inProgress = false; yield break; } if (dropHeldFirst) { GrabbableObject heldBeforeDrop = Player.Local.currentlyHeldObjectServer; yield return DropHeldItemIfAny(ship); CategorizeItems(includeSkippedItems: true); if ((Object)(object)heldBeforeDrop != (Object)null && heldBeforeDrop.Name() == itemKey && heldBeforeDrop.isInShipRoom) { if (scrap == null) { scrap = new List<GrabbableObject>(); } if (!scrap.Contains(heldBeforeDrop)) { scrap.Add(heldBeforeDrop); } } } BeginOperationLock(); Dictionary<string, List<GrabbableObject>> groupedItems = new Dictionary<string, List<GrabbableObject>>(); foreach (GrabbableObject item in scrap) { if (!(ignoreSkipLists ? ShouldSkipExplicitQuery(item) : ShouldSkip(item))) { string name = item.Name(); if (!groupedItems.TryGetValue(name, out List<GrabbableObject> list)) { list = (groupedItems[name] = new List<GrabbableObject>()); } list.Add(item); list = null; } } if (!groupedItems.TryGetValue(itemKey, out List<GrabbableObject> items) || items.Count == 0) { items = new List<GrabbableObject>(); } Vector3 pileCenterLocal = new Vector3(targetCenterShipLocal.x, 0f, targetCenterShipLocal.z); float extraYOffset = targetCenterShipLocal.y; float groundYLocal = pileCenterLocal.y; Vector3 rayStartCenter = ship.transform.TransformPoint(pileCenterLocal + Vector3.up * 2f); RaycastHit hitCenter = default(RaycastHit); if (Physics.Raycast(rayStartCenter, Vector3.down, ref hitCenter, 80f, 268437761, (QueryTriggerInteraction)1)) { groundYLocal = ship.transform.InverseTransformPoint(((RaycastHit)(ref hitCenter)).point).y; } GrabbableObject held = (((Object)(object)Player.Local != (Object)null) ? Player.Local.currentlyHeldObjectServer : null); List<GrabbableObject> itemsToMove = new List<GrabbableObject>(items); if ((Object)(object)held != (Object)null && held.Name() == itemKey && !itemsToMove.Contains(held)) { itemsToMove.Insert(0, held); } int moved = 0; for (int stackIndex = 0; stackIndex < itemsToMove.Count; stackIndex++) { GrabbableObject item2 = itemsToMove[stackIndex]; if (ShouldBreak(item2)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; yield break; } float pileX = 0f; float pileZ = 0f; float pileY; if (stackSameTypeTogether.Value) { pileY = (float)stackIndex * Mathf.Max(0f, sameTypeStackStepY.Value); } else { int cols = Mathf.Max(1, itemsPerRow.Value); int rows = cols; int perLayer = cols * rows; int layer = stackIndex / perLayer; int inLayer = stackIndex % perLayer; int r = inLayer / cols; int c = inLayer % cols; pileX = ((float)c - (float)(cols - 1) / 2f) * 0.1f; pileZ = ((float)r - (float)(rows - 1) / 2f) * 0.1f; pileY = (float)layer * 0.07f; } Vector3 targetLocal = new Vector3(pileCenterLocal.x + pileX, groundYLocal + (item2.itemProperties.verticalOffset - 0.05f) + extraYOffset + pileY, pileCenterLocal.z + pileZ); Vector3 worldPos = ship.transform.TransformPoint(targetLocal); if (!force && Vector3.Distance(worldPos, ((Component)item2).transform.position) < 0.25f) { continue; } if ((Object)(object)held != (Object)null && (Object)(object)item2 == (Object)(object)held) { item2.floorYRot = -1; MoveUtils.MoveItemOnShipLocal(item2, targetLocal, item2.floorYRot); moved++; yield return null; continue; } yield return GrabbableRetry(item2); if (!(ignoreSkipLists ? ShouldSkipExplicitQuery(item2) : ShouldSkip(item2))) { item2.floorYRot = -1; if (!MoveUtils.MoveItemOnShipLocal(item2, targetLocal, item2.floorYRot)) { Log.Warning("Failed to move " + (item2.itemProperties?.itemName ?? ((Object)item2).name)); } int retry = 15; while (!Player.CanGrabObject(item2) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } moved++; } } Log.Chat($"Moved '{itemKey}' ({moved} items)", "00FF00"); inProgress = false; } private Dictionary<string, Vector3> CreateLayout(List<string> itemNames, HashSet<string>? reservedTypes = null) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, Vector3> dictionary = new Dictionary<string, Vector3>(); int num = Mathf.Max(1, itemsPerRow.Value); int num2 = 0; for (int i = 0; i < itemNames.Count; i++) { string text = itemNames[i]; if (reservedTypes != null && reservedTypes.Contains(text)) { num2++; continue; } int num3 = i - num2; int num4 = num3 / num; int num5 = num3 % num; float num6 = (float)(num - 1) * 0.5f; float num7 = ((float)num5 - num6) * itemSpacing.Value; float num8 = 0f; float num9 = (float)num4 * rowSpacing.Value + 0.05f; dictionary[text] = new Vector3(num7, num9, num8); } return dictionary; } public void CategorizeItems(bool includeSkippedItems = false) { scrap = new List<GrabbableObject>(); GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); GrabbableObject[] array2 = array; foreach (GrabbableObject item in array2) { if (!(includeSkippedItems ? ShouldSkipExplicitQuery(item) : ShouldSkip(item)) && ShouldIncludeItemByLocation(item)) { scrap.Add(item); } } scrap = scrap.OrderBy((GrabbableObject val) => val.scrapValue).ThenBy((GrabbableObject item2) => item2.Name()).ToList(); } private static bool ShouldIncludeItemByLocation(GrabbableObject item) { return (Object)(object)item != (Object)null && (Ship.ShouldSortAllDetectedItems || item.isInShipRoom); } private IEnumerator GrabbableRetry(GrabbableObject item) { int retry = 15; while (!Player.CanGrabObject(item) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } private bool ShouldBreak(GrabbableObject item) { return !inProgress || ((Object)(object)Player.Local != (Object)null && (Player.Local.beamOutParticle.isPlaying || Player.Local.beamUpParticle.isPlaying)); } private bool ShouldSkipExplicitQuery(GrabbableObject item) { if ((Object)(object)item == (Object)null) { return true; } if (!item.grabbable || item.deactivated || item.isHeld || item.isPocketed) { return true; } if (item.Name() == "body") { return true; } if (!ShouldIncludeItemByLocation(item)) { return true; } return false; } private static string NormalizeSkipListConfig(string list) { if (string.IsNullOrWhiteSpace(list)) { return list ?? ""; } HashSet<string> hashSet = new HashSet<string>(); List<string> list2 = new List<string>(); string[] array = list.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i]?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text)) { text = NormalizeSkipToken(text); if (text == "rader_booster") { text = "radar_booster"; } if (!string.IsNullOrWhiteSpace(text) && hashSet.Add(text)) { list2.Add(text); } } } return string.Join(", ", list2); } private static List<string> ParseSkipListTokens(string list) { if (string.IsNullOrWhiteSpace(list)) { return new List<string>(); } return (from t in list.Split(',') select NormalizeSkipToken(t) into t where !string.IsNullOrWhiteSpace(t) select (t == "rader_booster") ? "radar_booster" : t).Distinct().ToList(); } private static string NormalizeSkipToken(string s) { if (string.IsNullOrWhiteSpace(s)) { return ""; } string normalizedKey = Extensions.NormalizeName(s); normalizedKey = ApplyDefaultInputAliases(normalizedKey); normalizedKey = normalizedKey.Trim('_'); if (normalizedKey == "rader_booster") { normalizedKey = "radar_booster"; } return normalizedKey; } private static void TrySaveConfig() { try { ConfigFile config = Plugin.config; if (config != null) { config.Save(); } } catch (Exception ex) { Log.Warning("Failed to save config: " + ex.Message); } } public bool TrySkipAdd(string rawItemName, out string? error, out string? message) { error = null; message = null; string text = NormalizeSkipToken(rawItemName); if (string.IsNullOrWhiteSpace(text)) { error = "Usage: /sort skip add <itemName>"; return false; } List<string> list = ParseSkipListTokens(skippedItems.Value); if (list.Contains(text)) { message = "Already in skippedItems: " + text; return true; } list.Add(text); skippedItems.Value = NormalizeSkipListConfig(string.Join(", ", list)); TrySaveConfig(); message = "Added to skippedItems: " + text; return true; } public bool TrySkipRemove(string rawItemName, out string? error, out string? message) { error = null; message = null; string token = NormalizeSkipToken(rawItemName); if (string.IsNullOrWhiteSpace(token)) { error = "Usage: /sort skip remove <itemName>"; return false; } List<string> list = ParseSkipListTokens(skippedItems.Value); int count = list.Count; list = list.Where((string t) => t != token).ToList(); if (list.Count == count) { message = "Not in skippedItems: " + token; return true; } skippedItems.Value = NormalizeSkipListConfig(string.Join(", ", list)); TrySaveConfig(); message = "Removed from skippedItems: " + token; return true; } public List<string> GetSkippedTokens() { return (from t in ParseSkipListTokens(skippedItems.Value) orderby t select t).ToList(); } private bool ShouldSkip(GrabbableObject item) { if ((Object)(object)item == (Object)null) { return true; } if (!item.grabbable || item.deactivated || item.isHeld || item.isPocketed) { return true; } if (item.Name() == "body") { return true; } if (!ShouldIncludeItemByLocation(item)) { return true; } string normalizedKey = item.Name(); string value = skippedItems.Value; if (!string.IsNullOrWhiteSpace(value)) { string[] array = value.Split(','); string[] array2 = array; foreach (string s in array2) { string value2 = NormalizeSkipToken(s); if (!string.IsNullOrWhiteSpace(value2)) { string text = ApplyDefaultInputAliases(normalizedKey); if (text.Contains(value2)) { return true; } } } } return false; } private bool ShouldSkipFullSort(GrabbableObject item, bool ignoreSkipTokens = false) { if ((Object)(object)item == (Object)null) { return true; } if (!item.grabbable || item.deactivated || item.isHeld || item.isPocketed) { return true; } if (item.Name() == "body") { return true; } if (!ShouldIncludeItemByLocation(item)) { return true; } if (ignoreSkipTokens) { return false; } string text = item.Name(); string value = skippedItems.Value; if (!string.IsNullOrWhiteSpace(value)) { string[] array = value.Split(','); foreach (string s in array) { string value2 = NormalizeSkipToken(s); if (!string.IsNullOrWhiteSpace(value2) && text.Contains(value2)) { return true; } } } return false; } } public abstract class QuickSortCommand : Command { public virtual string[] Commands => new string[1] { ((BaseCommand)this).Name.ToLowerInvariant() }; public sealed override string Command => Commands[0]; public sealed override string[] Aliases => Commands.Skip(1).ToArray(); public sealed override void Invoke(string args) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) string[] args2; try { args2 = Args.Parse(args, (uint?)null, true).ToArray(); } catch (InvalidArgumentsException) { throw new CommandException("Invalid arguments."); } if (!InvokeParsed(args2, out string error)) { throw new CommandException(error ?? "Command failed."); } } protected abstract bool InvokeParsed(string[] args, out string? error); } public class SortCommand : QuickSortCommand { private const string SortDescription = "Sorts items on the ship.\nUsage:\n /sort -> sort everything\n /sort -a -> sort everything, IGNORE skippedItems\n /sort -b -> full sort, but DO NOT skip item types that have a saved /sort set position\n (Note: -a and -b cannot be combined)\n /sort skip list -> show skippedItems tokens\n /sort skip add <name> -> add token to skippedItems (name can include spaces)\n /sort skip remove <name> -> remove token from skippedItems\n /sort <itemName> -> pull that item type to YOUR position (e.g. /sort cash_register)\n /sort <number> -> shortcut from JSON (pull to you) (e.g. /sort 1)\n /sort bind <name|id> -> bind your HELD item to an alias name OR shortcut id (then /sort <name> or /sort <id> works)\n /sort set [itemName] -> set this type's future sort position to YOUR position (name optional if holding)\n /sort reset [itemName]-> delete saved sort position for this type (name optional if holding)\n /sort bindings -> list all binds (numbers + names)\n /sort positions -> list saved sort positions"; public override string Name => "sort"; public override string[] Commands => new string[2] { "sort", ((BaseCommand)this).Name }; public override string Description => "Sorts items on the ship.\nUsage:\n /sort -> sort everything\n /sort -a -> sort everything, IGNORE skippedItems\n /sort -b -> full sort, but DO NOT skip item types that have a saved /sort set position\n (Note: -a and -b cannot be combined)\n /sort skip list -> show skippedItems tokens\n /sort skip add <name> -> add token to skippedItems (name can include spaces)\n /sort skip remove <name> -> remove token from skippedItems\n /sort <itemName> -> pull that item type to YOUR position (e.g. /sort cash_register)\n /sort <number> -> shortcut from JSON (pull to you) (e.g. /sort 1)\n /sort bind <name|id> -> bind your HELD item to an alias name OR shortcut id (then /sort <name> or /sort <id> works)\n /sort set [itemName] -> set this type's future sort position to YOUR position (name optional if holding)\n /sort reset [itemName]-> delete saved sort position for this type (name optional if holding)\n /sort bindings -> list all binds (numbers + names)\n /sort positions -> list saved sort positions"; public SortCommand() { Log.Info("SortCommand constructor called"); } protected override bool InvokeParsed(string[] args, out string? error) { return InvokeSort(args, out error); } public static bool InvokeSort(string[] args, out string? error) { //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_0b3f: Unknown result type (might be due to invalid IL or missing references) //IL_0b4e: Unknown result type (might be due to invalid IL or missing references) //IL_0b5d: Unknown result type (might be due to invalid IL or missing references) error = null; Log.Info("SortCommand.Invoke called with args: [" + string.Join(", ", args) + "]"); if (args.Length != 0 && args[0] == "help") { Chat.Print("Sorts items on the ship.\nUsage:\n /sort -> sort everything\n /sort -a -> sort everything, IGNORE skippedItems\n /sort -b -> full sort, but DO NOT skip item types that have a saved /sort set position\n (Note: -a and -b cannot be combined)\n /sort skip list -> show skippedItems tokens\n /sort skip add <name> -> add token to skippedItems (name can include spaces)\n /sort skip remove <name> -> remove token from skippedItems\n /sort <itemName> -> pull that item type to YOUR position (e.g. /sort cash_register)\n /sort <number> -> shortcut from JSON (pull to you) (e.g. /sort 1)\n /sort bind <name|id> -> bind your HELD