Decompiled source of QuickSort v0.1.9
QuickSort.dll
Decompiled 2 weeks 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.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 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("baer1.ChatCommandAPI")] [assembly: IgnoresAccessChecksTo("BepInEx")] [assembly: IgnoresAccessChecksTo("BepInEx.Harmony")] [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+5911755556c3dd80e5dfe7380a8c65faa1bac1fb")] [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] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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])); } } ChatArgs result = default(ChatArgs); result.arguments = list.ToArray(); result.help = help; return result; } 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("산탄총 탄약", "Shotgun shell"); 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, string[] aliases) { Add(canonicalKey, canonicalKey); if (aliases != null) { foreach (string localized2 in aliases) { Add(localized2, 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; } } 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() == -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.8")] public class Plugin : BaseUnityPlugin { private const string CurrentConfigSchemaVersion = "0.1.8"; public static ManualLogSource Log; public static ConfigFile config; public static ConfigEntry<string> configVersion; private static Harmony harmony; public static GameObject sorterObject; public static Plugin Instance { get; private set; } private void Awake() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Expected O, but got Unknown //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown 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.8", "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.8")); 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 = 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, shotgun, ammo", "Global skip list (comma-separated, substring match). Applies to all grabbable items."); string text2 = AddTokensToCommaList(val3.Value, "shotgun", "ammo"); if (!string.Equals(text2, val3.Value, StringComparison.Ordinal)) { val3.Value = text2; flag3 = true; } } ConfigEntry<string> val4 = default(ConfigEntry<string>); if (flag4 && (!flag2 || IsVersionLessThan(text, "0.1.7")) && config.TryGetEntry<string>(new ConfigDefinition("Sorter", "skippedItems"), ref val4) && IsOnlyShotgunAmmo(val4.Value)) { val4.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, shotgun, ammo"; flag3 = true; } if (flag4 && !string.Equals(configVersion.Value, "0.1.8", StringComparison.Ordinal)) { configVersion.Value = "0.1.8"; 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)); try { new SortCommand(); new SortBindCommand(); new SortSetCommand(); new SortResetCommand(); new SortPositionsCommand(); new SortBindingsListCommand(); new SortSkipCommand(); new PileCommand(); 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 (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)sorterObject != (Object)null) { Object.Destroy((Object)(object)sorterObject); } } 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 AddTokensToCommaList(string? list, params string[] tokensToAdd) { List<string> tokens = new List<string>(); HashSet<string> seen = new HashSet<string>(); if (!string.IsNullOrWhiteSpace(list)) { string[] array = list.Split(','); foreach (string raw2 in array) { Add(raw2); } } if (tokensToAdd != null) { foreach (string raw3 in tokensToAdd) { Add(raw3); } } return string.Join(", ", tokens); void Add(string raw) { string text = (raw ?? "").Trim(); if (!string.IsNullOrWhiteSpace(text)) { text = Extensions.NormalizeName(text).Trim('_'); if (!string.IsNullOrWhiteSpace(text) && seen.Add(text)) { tokens.Add(text); } } } } private static bool IsOnlyShotgunAmmo(string? list) { HashSet<string> seen = new HashSet<string>(); if (!string.IsNullOrWhiteSpace(list)) { string[] array = list.Split(','); foreach (string raw2 in array) { Add(raw2); } } return seen.Count == 2 && seen.Contains("shotgun") && seen.Contains("ammo"); void Add(string raw) { string text = (raw ?? "").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; [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] private static void OnLocalPlayerCreated(PlayerControllerB __instance) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if ((Object)(object)__instance != (Object)(object)StartOfRound.Instance.localPlayerController) { return; } if (!commandRegistered) { try { new SortCommand(); new SortBindCommand(); new SortSetCommand(); new SortResetCommand(); new SortPositionsCommand(); new SortBindingsListCommand(); new SortSkipCommand(); new PileCommand(); commandRegistered = true; 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>(); public static bool Stationary { get { if (GetStartOfRoundBool("shipHasLanded").GetValueOrDefault()) { return true; } if (GetStartOfRoundBool("inShipPhase").GetValueOrDefault() && !GetStartOfRoundBool("shipIsLeaving").GetValueOrDefault()) { return true; } return false; } } public static bool InOrbit => GetStartOfRoundBool("inShipPhase").GetValueOrDefault() && !GetStartOfRoundBool("shipHasLanded").GetValueOrDefault(); private static bool? GetStartOfRoundBool(string fieldName) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } if (!_startOfRoundBoolFields.TryGetValue(fieldName, out FieldInfo value)) { value = AccessTools.Field(((object)instance).GetType(), fieldName); _startOfRoundBoolFields[fieldName] = value; } if (value == null) { return null; } try { object value2 = value.GetValue(instance); if (value2 is bool) { bool value3 = (bool)value2; if (true) { return value3; } } } catch { } return null; } [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 { 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, shotgun, ammo"; 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 Vector3 SortOrigin => new Vector3(sortOriginX.Value, sortOriginY.Value, sortOriginZ.Value); private bool CanSort => Ship.InOrbit || Ship.Stationary; 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 type2 = ((object)local).GetType(); string[] array = new string[6] { "isInHangarShipRoom", "IsInHangarShipRoom", "isInShipRoom", "IsInShipRoom", "inShipRoom", "InShipRoom" }; string[] array2 = array; foreach (string name2 in array2) { bool? flag = TryGetBool(local, type2, name2); 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 obj, Type type, string name) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { return (bool)field.GetValue(obj); } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0) { return (bool)property.GetValue(obj, 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, shotgun, ammo", "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."); } private void Update() { if (inProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { inProgress = false; Log.Chat("Sorting cancelled", "FF0000"); } } private void SortCommandHandler(string[] args) { if (args.Length != 0 && args[0] == "help") { Log.Chat("Usage: /sort [help]", "FFFF00"); return; } if (!CanSort) { Log.NotifyPlayer("Sorter Error", "Must be in orbit or stationary at company", isWarning: true); 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; } PlayerControllerB dropWaitPlayer = Player.Local; bool shouldWaitForDrop = false; try { GrabbableObject held = (((Object)(object)dropWaitPlayer != (Object)null) ? dropWaitPlayer.currentlyHeldObjectServer : null); if ((Object)(object)dropWaitPlayer != (Object)null && (Object)(object)held != (Object)null) { NetworkObject shipNetObj = ship.GetComponent<NetworkObject>(); Vector3 heldShipLocal = ship.transform.InverseTransformPoint(((Component)held).transform.position); held.floorYRot = -1; dropWaitPlayer.DiscardHeldObject(true, shipNetObj, heldShipLocal, false); shouldWaitForDrop = true; } } catch (Exception ex) { Exception e = ex; Log.Warning("Failed to drop held item before /sort: " + e.Message); } if (shouldWaitForDrop && (Object)(object)dropWaitPlayer != (Object)null) { int frames = 0; while (frames < 30 && (Object)(object)dropWaitPlayer.currentlyHeldObjectServer != (Object)null) { frames++; yield return null; } } 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; } hitCenter = default(RaycastHit); 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--; } } } customShipLocal = default(Vector3); 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) error = null; if (!CanSort) { error = "Must be in orbit or stationary at company"; return false; } 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_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) error = null; if (!CanSort) { error = "Must be in orbit or stationary at company"; return false; } 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() : "")); 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)); return true; } public bool TrySetAndMoveTypeToPlayer(string? queryOrNull, bool force, out string resolvedItemKey, out string? error) { //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) resolvedItemKey = ""; error = null; if (!CanSort) { error = "Must be in orbit or stationary at company"; return false; } 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(); } 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)); 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) { //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; } 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; } hitCenter = default(RaycastHit); 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 val in array2) { if (!(includeSkippedItems ? ShouldSkipExplicitQuery(val) : ShouldSkip(val)) && val.isInShipRoom) { scrap.Add(val); } } scrap = (from item in scrap orderby item.scrapValue, item.Name() select item).ToList(); } 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 || !Ship.Stationary || ((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 (!item.isInShipRoom) { 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 (!item.isInShipRoom) { 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 (!item.isInShipRoom) { 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 class SortCommand : Command { public override string Name => "sort"; public override string[] Commands => new string[2] { "sort", ((Command)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"); } public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown //IL_0abc: Unknown result type (might be due to invalid IL or missing references) //IL_0acb: Unknown result type (might be due to invalid IL or missing references) //IL_0ada: 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") { ChatCommandAPI.Print(((Command)this).Description); return true; } if (!Ship.Stationary) { error = "Must be in orbit or stationary at company"; Log.Warning("SortCommand failed: " + error); return false; } if (!Sorter.EnsureLocalPlayerInShip(out string error2)) { error = error2 ?? "You must be inside the ship to use this command."; Log.Warning("SortCommand failed: " + error); return false; } if (Sorter.inProgress) { error = "Operation in progress"; Log.Warning("SortCommand failed: " + error); return false; } if (args.Contains("-r") || args.Contains("-redo")) { error = "Flag '-r/-redo' was removed."; return false; } if (args.Contains("-ab") || args.Contains("-ba")) { error = "Flags '-a' and '-b' cannot be combined. Use only one."; return false; } bool flag = args.Contains("-a") || args.Contains("-all") || (kwargs != null && (kwargs.ContainsKey("a") || kwargs.ContainsKey("all"))); bool flag2 = args.Contains("-b") || args.Contains("-bound") || (kwargs != null && (kwargs.ContainsKey("b") || kwargs.ContainsKey("bound"))); if (flag && flag2) { error = "Flags '-a' and '-b' cannot be combined. Use only one."; return false; } string[] array = args.Where((string a) => a != "-a" && a != "-all" && a != "-b" && a != "-bound" && a != "-ab" && a != "-ba").ToArray(); Sorter sorter = null; if ((Object)(object)Plugin.sorterObject != (Object)null) { sorter = Plugin.sorterObject.GetComponent<Sorter>(); } if ((Object)(object)sorter == (Object)null) { try { if ((Object)(object)Plugin.sorterObject == (Object)null) { Plugin.sorterObject = new GameObject("PastaSorter"); Object.DontDestroyOnLoad((Object)(object)Plugin.sorterObject); Log.Info("Sorter lazily initialized from SortCommand"); } sorter = Plugin.sorterObject.GetComponent<Sorter>(); if ((Object)(object)sorter == (Object)null) { sorter = Plugin.sorterObject.AddComponent<Sorter>(); Log.Info("Sorter component added from SortCommand"); } } catch (Exception ex) { error = "Sorter not initialized yet"; Log.Error("SortCommand failed: " + error); Log.Error("Exception: " + ex.Message); return false; } if ((Object)(object)sorter == (Object)null) { error = "Sorter not initialized yet"; Log.Warning("SortCommand failed: " + error); return false; } } Log.Info("SortCommand executing..."); if (array.Length != 0) { if (array[0] == "skip") { if (array.Length < 2) { error = "Usage: /sort skip <list|add|remove> ..."; return false; } string text = array[1]; if (text == "list" || text == "ls") { List<string> skippedTokens = sorter.GetSkippedTokens(); if (skippedTokens.Count == 0) { ChatCommandAPI.Print("skippedItems is empty."); return true; } string text2 = string.Join(", ", skippedTokens.Take(20)); if (skippedTokens.Count > 20) { text2 += ", ..."; } ChatCommandAPI.Print($"skippedItems ({skippedTokens.Count}): {text2}"); return true; } switch (text) { case "add": { string text4 = string.Join(" ", array.Skip(2)).Trim(); if (string.IsNullOrWhiteSpace(text4)) { text4 = (Player.Local?.currentlyHeldObjectServer)?.Name() ?? ""; if (string.IsNullOrWhiteSpace(text4)) { error = "Usage: /sort skip add <itemName> (or hold an item)"; return false; } } text4 = ResolveSkipTarget(text4); if (!sorter.TrySkipAdd(text4, out error, out string message2)) { return false; } if (!string.IsNullOrWhiteSpace(message2)) { ChatCommandAPI.Print(message2); } return true; } default: if (!(text == "del")) { error = "Usage: /sort skip <list|add|remove> ..."; return false; } goto case "remove"; case "remove": case "rm": { string text3 = string.Join(" ", array.Skip(2)).Trim(); if (string.IsNullOrWhiteSpace(text3)) { text3 = (Player.Local?.currentlyHeldObjectServer)?.Name() ?? ""; if (string.IsNullOrWhiteSpace(text3)) { error = "Usage: /sort skip remove <itemName> (or hold an item)"; return false; } } text3 = ResolveSkipTarget(text3); if (!sorter.TrySkipRemove(text3, out error, out string message)) { return false; } if (!string.IsNullOrWhiteSpace(message)) { ChatCommandAPI.Print(message); } return true; } } } if (array[0] == "bind") { if (array.Length < 2) { error = "Usage: /sort bind <name> (hold the item you want to bind)"; return false; } if (array.Length >= 3 && (array[1] == "reset" || array[1] == "rm" || array[1] == "remove" || array[1] == "del")) { string text5 = string.Join(" ", array.Skip(2)).Trim(); if (string.IsNullOrWhiteSpace(text5)) { error = "Usage: /sort bind reset <name|id>"; return false; } if (int.TryParse(text5, out var result) && result > 0) { if (!SortShortcuts.RemoveShortcut(result, out bool removed, out string error3)) { error = error3 ?? "Failed to remove shortcut."; return false; } ChatCommandAPI.Print(removed ? $"Unbound {result}" : $"No binding for {result}"); return true; } if (!SortShortcuts.RemoveAlias(text5, out bool removed2, out string error4)) { error = error4 ?? "Failed to remove alias."; return false; } string text6 = Extensions.NormalizeName(text5); ChatCommandAPI.Print(removed2 ? ("Unbound " + text6) : ("No binding for " + text6)); return true; } GrabbableObject val = (((Object)(object)Player.Local != (Object)null) ? Player.Local.currentlyHeldObjectServer : null); if ((Object)(object)val == (Object)null) { error = "You must hold an item to bind."; return false; } string text7 = string.Join(" ", array.Skip(1)); string text8 = val.Name(); if (int.TryParse(text7.Trim(), out var result2) && result2 > 0) { if (!SortShortcuts.SetShortcut(result2, text8, out string error5)) { error = error5 ?? "Failed to bind shortcut."; return false; } ChatCommandAPI.Print($"Bound {result2} => {text8}"); return true; } if (!SortShortcuts.BindAlias(text7, text8, out string error6)) { error = error6 ?? "Failed to bind alias."; return false; } ChatCommandAPI.Print("Bound " + Extensions.NormalizeName(text7) + " => " + text8); return true; } if (array[0] == "reset") { string text9 = ((array.Length > 1) ? Extensions.NormalizeName(string.Join(" ", array.Skip(1))) : (Player.Local?.currentlyHeldObjectServer)?.Name()); if (string.IsNullOrWhiteSpace(text9)) { error = "Missing item name (hold the item or provide a name)."; return false; } if (!SortPositions.Remove(text9, out bool removed3, out string error7)) { error = error7 ?? "Failed to remove saved position."; return false; } if (error7 != null) { error = error7; return false; } if (removed3) { ChatCommandAPI.Print("Removed saved sort position for '" + text9 + "'."); } else { ChatCommandAPI.Print("No saved sort position for '" + text9 + "'."); } return true; } if (array[0] == "set") { string text10 = ((array.Length > 1) ? string.Join(" ", array.Skip(1)) : null); if (!sorter.TrySetAndMoveTypeToPlayer(text10, force: false, out string resolvedItemKey, out error)) { return false; } if (!string.IsNullOrWhiteSpace(text10)) { string text11 = Extensions.NormalizeName(text10); if (!string.IsNullOrWhiteSpace(text11) && text11 != resolvedItemKey) { ChatCommandAPI.Print("Resolved '" + text10 + "' => '" + resolvedItemKey + "'."); } } string text12 = (string.IsNullOrWhiteSpace(resolvedItemKey) ? "held_item" : resolvedItemKey); if (SortPositions.TryGet(resolvedItemKey, out Vector3 shipLocalPos, out string error8)) { ChatCommandAPI.Print($"Saved sort position for '{text12}' => (x={shipLocalPos.x:F2}, y={shipLocalPos.y:F2}, z={shipLocalPos.z:F2})."); } else { if (error8 != null) { Log.Warning(error8); } ChatCommandAPI.Print("Saved sort position for '" + text12 + "'."); } return true; } if (array[0] == "positions") { string error9; List<(string, Vector3)> list = SortPositions.ListAll(out error9); if (error9 != null) { error = error9; return false; } if (list.Count == 0) { ChatCommandAPI.Print("No saved sort positions."); return true; } string text13 = string.Join(", ", from p in list.Take(8) select $"{p.itemKey}=(x={p.shipLocalPos.x:F1},y={p.shipLocalPos.y:F1},z={p.shipLocalPos.z:F1})"); if (list.Count > 8) { text13 += ", ..."; } ChatCommandAPI.Print(text13); return true; } if (array[0] == "bindings" || array[0] == "binds" || array[0] == "shortcuts" || array[0] == "aliases") { string error10; List<(int, string)> list2 = SortShortcuts.ListShortcuts(out error10); if (error10 != null) { error = error10; return false; } string error11; List<(string, string)> list3 = SortShortcuts.ListAliases(out error11); if (error11 != null) { error = error11; return false; } if (list2.Count == 0 && list3.Count == 0) { ChatCommandAPI.Print("No bindings found."); return true; } if (list2.Count > 0) { string text14 = string.Join(", ", list2.Select<(int, string), string>(((int id, string itemKey) s) => $"{s.id}={s.itemKey}")); ChatCommandAPI.Print(text14); } if (list3.Count > 0) { string text15 = string.Join(", ", from a in list3.Take(12) select a.alias + "=" + a.itemKey); if (list3.Count > 12) { text15 += ", ..."; } ChatCommandAPI.Print(text15); } return true; } if (array.Length == 1 && int.TryParse(array[0], out var result3)) { if (!SortShortcuts.TryResolve(result3, out string itemKey, out string error12)) { error = error12 ?? "Unknown shortcut error"; return false; } Log.ConfirmSound(); if (!sorter.TryStartGatherByQuery(itemKey, force: false, out error)) { return false; } Log.Info("SortCommand executed successfully (shortcut move)"); return true; } if (array.Length == 1 && SortShortcuts.TryResolveAlias(array[0], out string itemKey2, out string _)) { Log.ConfirmSound(); if (!sorter.TryStartGatherByQuery(itemKey2, force: false, out error)) { return false; } Log.Info("SortCommand executed successfully (alias move)"); return true; } string query = string.Join(" ", array); Log.ConfirmSound(); if (!sorter.TryStartGatherByQuery(query, force: false, out error)) { return false; } Log.Info("SortCommand executed successfully (item move)"); return true; } sorter.CategorizeItems(flag || flag2); Log.ConfirmSound(); ((MonoBehaviour)sorter).StartCoroutine(sorter.SortItems(force: false, flag, flag2)); Log.Info("SortCommand executed successfully (full sort)"); return true; static string ResolveSkipTarget(string raw) { raw = (raw ?? "").Trim(); if (string.IsNullOrWhiteSpace(raw)) { return raw; } string error14; if (int.TryParse(raw, out var result4) && result4 > 0) { if (SortShortcuts.TryResolve(result4, out string itemKey3, out error14)) { return itemKey3; } return raw; } if (SortShortcuts.TryResolveAlias(raw, out string itemKey4, out error14)) { return itemKey4; } return raw; } } } public class SortBindCommand : Command { public override string Name => "sb"; public override string[] Commands => new string[2] { "sb", ((Command)this).Name }; public override string Description => "Shortcut for /sort bind\nUsage:\n /sb <name|id> -> bind your HELD item to an alias name OR shortcut id"; public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { string[] array = new string[1] { "bind" }.Concat(args ?? Array.Empty<string>()).ToArray(); return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class SortSetCommand : Command { public override string Name => "ss"; public override string[] Commands => new string[2] { "ss", ((Command)this).Name }; public override string Description => "Shortcut for /sort set\nUsage:\n /ss [itemName] -> set saved sort position for this type (name optional if holding; partial match supported)"; public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { string[] array = new string[1] { "set" }.Concat(args ?? Array.Empty<string>()).ToArray(); return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class SortResetCommand : Command { public override string Name => "sr"; public override string[] Commands => new string[2] { "sr", ((Command)this).Name }; public override string Description => "Shortcut for /sort reset\nUsage:\n /sr [itemName] -> delete saved sort position for this type (name optional if holding)"; public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { string[] array = new string[1] { "reset" }.Concat(args ?? Array.Empty<string>()).ToArray(); return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class SortPositionsCommand : Command { public override string Name => "sp"; public override string[] Commands => new string[2] { "sp", ((Command)this).Name }; public override string Description => "Shortcut for /sort positions\nUsage:\n /sp -> list saved sort positions"; public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { string[] array = new string[1] { "positions" }; return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class SortBindingsListCommand : Command { public override string Name => "sbl"; public override string[] Commands => new string[2] { "sbl", ((Command)this).Name }; public override string Description => "Shortcut for /sort bindings\nUsage:\n /sbl -> list bindings (shortcuts + aliases)"; public override bool Invoke(string[] args, Dictionary<string, string> kwargs, out string? error) { string[] array = new string[1] { "bindings" }; return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class SortSkipCommand : Command { public override string Name => "sk"; public override string[] Commands => new string[2] { "sk", ((Command)this).Name }; public override string Description => "Shortcut for /sort skip\nUsage:\n /sk list -> s