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("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+644f591bb1e79799e329bdb84240036f1945b788")] [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 : Argument { public T value; public Argument(string name, T value) { base.name = name; this.value = value; } public static implicit operator T(Argument 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(name); public string this[int name] => Get(name); public T Get(string name) { Argument[] array = arguments; Argument[] array2 = array; foreach (Argument argument in array2) { if (argument.name == name) { return ((Argument)argument).value; } } return default(T); } public T Get(int name) { Argument[] array = arguments; Argument[] array2 = array; foreach (Argument argument in array2) { if (argument.name == name.ToString()) { return ((Argument)argument).value; } } return default(T); } } public class ChatCommand { public static List Commands = new List(); public string keyword; public Action action; public string help; public ChatCommand(string keyword, string help, Action action) { this.keyword = keyword; this.help = help; this.action = action; } public static ChatCommand New(string keyword, string help, Action action) { ChatCommand chatCommand = new ChatCommand(keyword, help, action); Commands.Add(chatCommand); return chatCommand; } public ChatArgs GetArgs(string raw) { List list = new List(); 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(name, value: true)); } else { list.Add(new Argument((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 NameAliases = BuildNameAliases(); private static string NormalizeKeyNoAlias(string s) { if (string.IsNullOrWhiteSpace(s)) { return ""; } return s.ToLower().Replace(" ", "_").Replace("-", "_") .Trim(); } private static Dictionary BuildNameAliases() { Dictionary d = new Dictionary(); (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 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 Transpiler(IEnumerable 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 list = new List(instructions); CodeMatcher val = new CodeMatcher((IEnumerable)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("[Pasta] " + body + "", "", -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() : 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 { [CompilerGenerated] private sealed class d__5 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public GrabbableObject grabbableObject; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (CanGrabObject(grabbableObject)) { overrideObject = grabbableObject; Local.BeginGrabObject(); <>2__current = Local.grabObjectCoroutine; <>1__state = 1; return true; } break; case 1: <>1__state = -1; break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__6 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public GrabbableObject item; public Vector3 position; public NetworkObject parent; private int 5__1; private Exception 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_015d: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = StartGrabbingObject(item); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)Local == (Object)null) { return false; } 5__1 = 0; break; case 2: <>1__state = -1; break; } if (5__1 < 30 && ((Object)(object)Local.currentlyHeldObjectServer == (Object)null || (Object)(object)Local.currentlyHeldObjectServer != (Object)(object)item)) { 5__1++; <>2__current = null; <>1__state = 2; return true; } 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."); return false; } try { item.floorYRot = -1; Local.DiscardHeldObject(true, parent, position, false); } catch (Exception ex) { 5__2 = ex; Log.Exception(5__2); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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; } [IteratorStateMachine(typeof(d__5))] public static IEnumerator StartGrabbingObject(GrabbableObject grabbableObject) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__5(0) { grabbableObject = grabbableObject }; } [IteratorStateMachine(typeof(d__6))] 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 decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(0) { item = item, position = position, parent = parent }; } } [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 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_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Expected O, but got Unknown //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown applicationIsQuitting = false; Instance = this; Log = ((BaseUnityPlugin)this).Logger; config = ((BaseUnityPlugin)this).Config; bool flag = File.Exists(config.ConfigFilePath); ConfigEntry val = default(ConfigEntry); bool flag2 = config.TryGetEntry(new ConfigDefinition("General", "configVersion"), ref val); configVersion = config.Bind("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 val2 = config.Bind("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 val3 = config.Bind("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 val4 = default(ConfigEntry); if (flag4 && (!flag2 || IsVersionLessThan(text, "0.1.7")) && config.TryGetEntry(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)); harmony.PatchAll(typeof(InteractionLockPatch)); 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(); 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 AddTokensToCommaList(string? list, params string[] tokensToAdd) { List tokens = new List(); HashSet seen = new HashSet(); 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 seen = new HashSet(); 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(); 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 _startOfRoundBoolFields = new Dictionary(); private static readonly Dictionary _startOfRoundBoolProps = new Dictionary(); private static readonly Dictionary _startOfRoundBoolFuzzy = new Dictionary(); public static bool Stationary { get { if (GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded").GetValueOrDefault()) { return true; } if (GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase").GetValueOrDefault() && !GetStartOfRoundBool("shipIsLeaving", "ShipIsLeaving", "isShipLeaving", "IsShipLeaving", "shipLeaving").GetValueOrDefault()) { return true; } return false; } } public static bool CanSortNow { get { if (GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded").GetValueOrDefault()) { return true; } if (GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase").GetValueOrDefault()) { return true; } return InOrbit || Stationary; } } public static bool InOrbit => GetStartOfRoundBool("inShipPhase", "InShipPhase", "shipPhase", "ShipPhase").GetValueOrDefault() && !GetStartOfRoundBool("shipHasLanded", "ShipHasLanded", "hasLanded", "HasLanded").GetValueOrDefault(); 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 { object value2 = value.GetValue(instance); if (value2 is bool) { bool value3 = (bool)value2; if (true) { return value3; } } } catch { } } if (!_startOfRoundBoolProps.TryGetValue(name, out PropertyInfo value4)) { value4 = AccessTools.Property(type, name); _startOfRoundBoolProps[name] = value4; } if (value4 != null && value4.PropertyType == typeof(bool) && value4.GetIndexParameters().Length == 0) { try { object value2 = value4.GetValue(instance, null); if (value2 is bool) { bool value5 = (bool)value2; if (true) { return value5; } } } 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(); } } } [CompilerGenerated] private sealed class d__37 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public GameObject ship; public Sorter <>4__this; private PlayerControllerB 5__1; private GrabbableObject 5__2; private NetworkObject 5__3; private Vector3 5__4; private float 5__5; private int 5__6; private float 5__7; private IDisposable <>s__8; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__37(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; 5__2 = null; 5__3 = null; <>s__8 = null; <>1__state = -2; } private bool MoveNext() { //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: { <>1__state = -1; 5__1 = Player.Local; if ((Object)(object)5__1 == (Object)null) { return false; } 5__2 = 5__1.currentlyHeldObjectServer; if ((Object)(object)5__2 == (Object)null) { return false; } 5__3 = (((Object)(object)ship != (Object)null) ? ship.GetComponent() : null); 5__4 = ship.transform.InverseTransformPoint(((Component)5__1).transform.position + ((Component)5__1).transform.forward * 0.75f); if (<>4__this.TryGetGroundYLocalAt(5__4, out 5__5, out string _)) { 5__7 = 0f; try { if ((Object)(object)5__2.itemProperties != (Object)null) { 5__7 = 5__2.itemProperties.verticalOffset - 0.05f; } } catch { } 5__4.y = 5__5 + 5__7; } <>s__8 = BeginInteractionBypass(); try { 5__2.floorYRot = -1; 5__1.DiscardHeldObject(true, 5__3, 5__4, false); } finally { if (<>s__8 != null) { <>s__8.Dispose(); } } <>s__8 = null; 5__6 = 0; break; } case 1: <>1__state = -1; break; } if (5__6 < 30 && (Object)(object)5__1.currentlyHeldObjectServer != (Object)null) { 5__6++; <>2__current = null; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__52 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public GrabbableObject item; public Sorter <>4__this; private int 5__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__52(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = 15; break; case 1: <>1__state = -1; 5__1--; break; } if (!Player.CanGrabObject(item) && 5__1 > 0) { <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__48 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string itemKey; public Vector3 targetCenterShipLocal; public bool force; public bool announce; public bool ignoreSkipLists; public bool applyTwoHandedSortYOffset; public bool dropHeldFirst; public Sorter <>4__this; private GameObject 5__1; private Dictionary> 5__2; private List 5__3; private Vector3 5__4; private float 5__5; private float 5__6; private GrabbableObject 5__7; private List 5__8; private int 5__9; private GrabbableObject 5__10; private List.Enumerator <>s__11; private GrabbableObject 5__12; private string 5__13; private List 5__14; private Vector3 5__15; private RaycastHit 5__16; private int 5__17; private GrabbableObject 5__18; private float 5__19; private float 5__20; private float 5__21; private Vector3 5__22; private Vector3 5__23; private int 5__24; private int 5__25; private int 5__26; private int 5__27; private int 5__28; private int 5__29; private int 5__30; private int 5__31; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__48(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; 5__2 = null; 5__3 = null; 5__7 = null; 5__8 = null; 5__10 = null; <>s__11 = default(List.Enumerator); 5__12 = null; 5__13 = null; 5__14 = null; 5__18 = null; <>1__state = -2; } private bool MoveNext() { //IL_07b6: Unknown result type (might be due to invalid IL or missing references) //IL_0821: Unknown result type (might be due to invalid IL or missing references) //IL_082b: Expected O, but got Unknown //IL_0664: Unknown result type (might be due to invalid IL or missing references) //IL_0669: Unknown result type (might be due to invalid IL or missing references) //IL_067b: Unknown result type (might be due to invalid IL or missing references) //IL_0680: Unknown result type (might be due to invalid IL or missing references) //IL_0685: Unknown result type (might be due to invalid IL or missing references) //IL_0693: Unknown result type (might be due to invalid IL or missing references) //IL_06a3: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_06ff: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; 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; return false; } 5__1 = GameObject.Find("Environment/HangarShip"); if ((Object)(object)5__1 == (Object)null) { Log.NotifyPlayer("Sorter Error", "Ship not found", isWarning: true); inProgress = false; return false; } if (dropHeldFirst) { 5__10 = Player.Local.currentlyHeldObjectServer; <>2__current = <>4__this.DropHeldItemIfAny(5__1); <>1__state = 1; return true; } goto IL_01ca; case 1: <>1__state = -1; <>4__this.CategorizeItems(includeSkippedItems: true); if ((Object)(object)5__10 != (Object)null && 5__10.Name() == itemKey && 5__10.isInShipRoom) { if (<>4__this.scrap == null) { <>4__this.scrap = new List(); } if (!<>4__this.scrap.Contains(5__10)) { <>4__this.scrap.Add(5__10); } } 5__10 = null; goto IL_01ca; case 2: <>1__state = -1; goto IL_0888; case 3: <>1__state = -1; if (!(ignoreSkipLists ? <>4__this.ShouldSkipExplicitQuery(5__18) : <>4__this.ShouldSkip(5__18))) { 5__18.floorYRot = -1; if (!MoveUtils.MoveItemOnShipLocal(5__18, 5__22, 5__18.floorYRot)) { Log.Warning("Failed to move " + (5__18.itemProperties?.itemName ?? ((Object)5__18).name)); } 5__31 = 15; goto IL_084e; } goto IL_0880; case 4: { <>1__state = -1; 5__31--; goto IL_084e; } IL_089a: if (5__17 < 5__8.Count) { 5__18 = 5__8[5__17]; if (<>4__this.ShouldBreak(5__18)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; return false; } 5__19 = 0f; 5__20 = 0f; if (<>4__this.stackSameTypeTogether.Value) { 5__21 = (float)5__17 * Mathf.Max(0f, <>4__this.sameTypeStackStepY.Value); } else { 5__24 = Mathf.Max(1, <>4__this.itemsPerRow.Value); 5__25 = 5__24; 5__26 = 5__24 * 5__25; 5__27 = 5__17 / 5__26; 5__28 = 5__17 % 5__26; 5__29 = 5__28 / 5__24; 5__30 = 5__28 % 5__24; 5__19 = ((float)5__30 - (float)(5__24 - 1) / 2f) * 0.1f; 5__20 = ((float)5__29 - (float)(5__25 - 1) / 2f) * 0.1f; 5__21 = (float)5__27 * 0.07f; } 5__22 = new Vector3(5__4.x + 5__19, 5__6 + (5__18.itemProperties.verticalOffset - 0.05f) + 5__5 + 5__21, 5__4.z + 5__20); 5__23 = 5__1.transform.TransformPoint(5__22); if (!force && Vector3.Distance(5__23, ((Component)5__18).transform.position) < 0.25f) { goto IL_0888; } if ((Object)(object)5__7 != (Object)null && (Object)(object)5__18 == (Object)(object)5__7) { 5__18.floorYRot = -1; MoveUtils.MoveItemOnShipLocal(5__18, 5__22, 5__18.floorYRot); 5__9++; <>2__current = null; <>1__state = 2; return true; } <>2__current = <>4__this.GrabbableRetry(5__18); <>1__state = 3; return true; } Log.Chat($"Moved '{itemKey}' ({5__9} items)", "00FF00"); inProgress = false; return false; IL_084e: if (!Player.CanGrabObject(5__18) && 5__31 > 0) { <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 4; return true; } 5__9++; goto IL_0880; IL_01ca: BeginOperationLock(); 5__2 = new Dictionary>(); <>s__11 = <>4__this.scrap.GetEnumerator(); try { while (<>s__11.MoveNext()) { 5__12 = <>s__11.Current; if (!(ignoreSkipLists ? <>4__this.ShouldSkipExplicitQuery(5__12) : <>4__this.ShouldSkip(5__12))) { 5__13 = 5__12.Name(); if (!5__2.TryGetValue(5__13, out 5__14)) { 5__14 = new List(); 5__2[5__13] = 5__14; } 5__14.Add(5__12); 5__13 = null; 5__14 = null; 5__12 = null; } } } finally { ((IDisposable)<>s__11).Dispose(); } <>s__11 = default(List.Enumerator); if (!5__2.TryGetValue(itemKey, out 5__3) || 5__3.Count == 0) { 5__3 = new List(); } 5__4 = new Vector3(targetCenterShipLocal.x, 0f, targetCenterShipLocal.z); 5__5 = targetCenterShipLocal.y; 5__6 = 5__4.y; 5__15 = 5__1.transform.TransformPoint(5__4 + Vector3.up * 2f); if (Physics.Raycast(5__15, Vector3.down, ref 5__16, 80f, 268437761, (QueryTriggerInteraction)1)) { 5__6 = 5__1.transform.InverseTransformPoint(((RaycastHit)(ref 5__16)).point).y; } 5__7 = (((Object)(object)Player.Local != (Object)null) ? Player.Local.currentlyHeldObjectServer : null); 5__8 = new List(5__3); if ((Object)(object)5__7 != (Object)null && 5__7.Name() == itemKey && !5__8.Contains(5__7)) { 5__8.Insert(0, 5__7); } 5__9 = 0; 5__17 = 0; goto IL_089a; IL_0888: 5__17++; goto IL_089a; IL_0880: 5__18 = null; goto IL_0888; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__40 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public bool force; public bool ignoreSkippedItems; public bool includeSavedPositionTypesEvenIfSkipped; public Sorter <>4__this; private GameObject 5__1; private Vector3 5__2; private HashSet 5__3; private List<(string itemKey, Vector3 shipLocalPos)> 5__4; private string 5__5; private Dictionary> 5__6; private List>> 5__7; private List 5__8; private HashSet 5__9; private Dictionary 5__10; private List.Enumerator <>s__11; private GrabbableObject 5__12; private string 5__13; private bool 5__14; private List>>.Enumerator <>s__15; private KeyValuePair> 5__16; private string 5__17; private List 5__18; private Vector3 5__19; private bool 5__20; private Vector3 5__21; private string 5__22; private bool 5__23; private Vector3 5__24; private float 5__25; private float 5__26; private float 5__27; private float 5__28; private Vector3 5__29; private RaycastHit 5__30; private int 5__31; private GrabbableObject 5__32; private float 5__33; private float 5__34; private float 5__35; private Vector3 5__36; private Vector3 5__37; private int 5__38; private int 5__39; private int 5__40; private int 5__41; private int 5__42; private int 5__43; private int 5__44; private int 5__45; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__40(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } 5__1 = null; 5__3 = null; 5__4 = null; 5__5 = null; 5__6 = null; 5__7 = null; 5__8 = null; 5__9 = null; 5__10 = null; <>s__11 = default(List.Enumerator); 5__12 = null; 5__13 = null; <>s__15 = default(List>>.Enumerator); 5__16 = default(KeyValuePair>); 5__17 = null; 5__18 = null; 5__22 = null; 5__32 = null; <>1__state = -2; } private bool MoveNext() { //IL_082c: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_0897: Unknown result type (might be due to invalid IL or missing references) //IL_08a1: Expected O, but got Unknown //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_0762: Unknown result type (might be due to invalid IL or missing references) //IL_0767: Unknown result type (might be due to invalid IL or missing references) //IL_0779: Unknown result type (might be due to invalid IL or missing references) //IL_077e: Unknown result type (might be due to invalid IL or missing references) //IL_0783: Unknown result type (might be due to invalid IL or missing references) //IL_0791: Unknown result type (might be due to invalid IL or missing references) //IL_07a1: Unknown result type (might be due to invalid IL or missing references) //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0430: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04e5: Unknown result type (might be due to invalid IL or missing references) //IL_04ef: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_04f9: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_0509: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; break; case 0: <>1__state = -1; 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; result = false; break; } 5__1 = GameObject.Find("Environment/HangarShip"); if ((Object)(object)5__1 == (Object)null) { Log.NotifyPlayer("Sorter Error", "Ship not found", isWarning: true); inProgress = false; result = false; break; } BeginOperationLock(); 5__2 = <>4__this.SortOrigin; 5__3 = null; 5__4 = SortPositions.ListAll(out 5__5); if (5__5 != null) { Log.Warning(5__5); } else { 5__3 = new HashSet(5__4.Select<(string, Vector3), string>(((string itemKey, Vector3 shipLocalPos) p) => p.itemKey)); } 5__6 = new Dictionary>(); <>s__11 = <>4__this.scrap.GetEnumerator(); try { while (<>s__11.MoveNext()) { 5__12 = <>s__11.Current; 5__13 = 5__12.Name(); 5__14 = ignoreSkippedItems || (includeSavedPositionTypesEvenIfSkipped && 5__3 != null && 5__3.Contains(5__13)); if (!<>4__this.ShouldSkipFullSort(5__12, 5__14)) { if (!5__6.ContainsKey(5__13)) { 5__6[5__13] = new List(); } 5__6[5__13].Add(5__12); 5__13 = null; 5__12 = null; } } } finally { ((IDisposable)<>s__11).Dispose(); } <>s__11 = default(List.Enumerator); 5__7 = (from kvp in 5__6 orderby kvp.Value != null && kvp.Value.Any(IsTwoHandedItem) descending, kvp.Key select kvp).ToList(); 5__8 = 5__7.Select((KeyValuePair> kvp) => kvp.Key).ToList(); 5__9 = 5__3; 5__10 = <>4__this.CreateLayout(5__8, 5__9); <>s__15 = 5__7.GetEnumerator(); <>1__state = -3; goto IL_0942; case 1: <>1__state = -3; if (!<>4__this.ShouldSkipFullSort(5__32, ignoreSkippedItems | 5__23)) { 5__32.floorYRot = -1; if (!MoveUtils.MoveItemOnShipLocal(5__32, 5__36, 5__32.floorYRot)) { Log.Warning("Failed to move " + (5__32.itemProperties?.itemName ?? ((Object)5__32).name)); } 5__45 = 15; goto IL_08ca; } goto IL_08ea; case 2: { <>1__state = -3; 5__45--; goto IL_08ca; } IL_08ca: if (!Player.CanGrabObject(5__32) && 5__45 > 0) { <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 2; result = true; break; } goto IL_08ea; IL_08ea: 5__32 = null; goto IL_08f2; IL_0904: if (5__31 < 5__18.Count) { 5__32 = 5__18[5__31]; if (<>4__this.ShouldBreak(5__32)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; result = false; <>m__Finally1(); break; } 5__33 = 0f; 5__34 = 0f; if (<>4__this.stackSameTypeTogether.Value) { 5__35 = (float)5__31 * Mathf.Max(0f, <>4__this.sameTypeStackStepY.Value); } else { 5__38 = Mathf.Max(1, <>4__this.itemsPerRow.Value); 5__39 = 5__38; 5__40 = 5__38 * 5__39; 5__41 = 5__31 / 5__40; 5__42 = 5__31 % 5__40; 5__43 = 5__42 / 5__38; 5__44 = 5__42 % 5__38; 5__33 = ((float)5__44 - (float)(5__38 - 1) / 2f) * 0.1f; 5__34 = ((float)5__43 - (float)(5__39 - 1) / 2f) * 0.1f; 5__35 = (float)5__41 * 0.07f; } 5__36 = new Vector3(5__24.x + 5__33, 5__28 + 5__27 + (5__32.itemProperties.verticalOffset - 0.1f) + (5__26 + 5__25) + 5__35 - (ShouldLowerYOffset(5__32) ? 0.2f : 0f), 5__24.z + 5__34); 5__37 = 5__1.transform.TransformPoint(5__36); if (!force && Vector3.Distance(5__37, ((Component)5__32).transform.position) < 0.25f) { goto IL_08f2; } <>2__current = <>4__this.GrabbableRetry(5__32); <>1__state = 1; result = true; break; } 5__17 = null; 5__18 = null; 5__22 = null; 5__16 = default(KeyValuePair>); goto IL_0942; IL_08f2: 5__31++; goto IL_0904; IL_0942: if (<>s__15.MoveNext()) { 5__16 = <>s__15.Current; 5__17 = 5__16.Key; 5__18 = 5__16.Value; 5__19 = (5__10.ContainsKey(5__17) ? 5__10[5__17] : Vector3.zero); 5__20 = SortPositions.TryGet(5__17, out 5__21, out 5__22); if (5__22 != null) { Log.Warning(5__22); } 5__23 = includeSavedPositionTypesEvenIfSkipped & 5__20; 5__24 = (Vector3)(5__20 ? new Vector3(5__21.x, 0f, 5__21.z) : (5__2 + new Vector3(5__19.x, 0f, 5__19.z))); 5__25 = (5__20 ? 5__21.y : 0f); 5__26 = (5__20 ? 0f : 5__19.y); 5__27 = (5__20 ? 0f : 5__2.y); 5__28 = 5__24.y; 5__29 = 5__1.transform.TransformPoint(5__24 + Vector3.up * 2f); if (Physics.Raycast(5__29, Vector3.down, ref 5__30, 80f, 268437761, (QueryTriggerInteraction)1)) { 5__28 = 5__1.transform.InverseTransformPoint(((RaycastHit)(ref 5__30)).point).y; } 5__31 = 0; goto IL_0904; } <>m__Finally1(); <>s__15 = default(List>>.Enumerator); Log.Chat("Sorting complete!", "00FF00"); inProgress = false; result = false; break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>s__15).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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 LowerYOffsetTypes = new HashSet { "toilet_paper", "chemical_jug", "cash_register", "fancy_lamp", "large_axle", "v_type_engine" }; public ConfigEntry sortOriginX; public ConfigEntry sortOriginY; public ConfigEntry sortOriginZ; public ConfigEntry itemSpacing; public ConfigEntry rowSpacing; public ConfigEntry itemsPerRow; public ConfigEntry skippedItems; public ConfigEntry sortAreaWidth; public ConfigEntry sortAreaDepth; public ConfigEntry wallPadding; public ConfigEntry stackSameTypeTogether; public ConfigEntry sameTypeStackStepY; private List 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 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("Sorter", "sortOriginX", -2.8f, "X coordinate of the origin position for sorting items (relative to ship)"); sortOriginY = Plugin.config.Bind("Sorter", "sortOriginY", 0.1f, "Y coordinate of the origin position for sorting items (relative to ship)"); sortOriginZ = Plugin.config.Bind("Sorter", "sortOriginZ", -4.8f, "Z coordinate of the origin position for sorting items (relative to ship)"); itemSpacing = Plugin.config.Bind("Sorter", "itemSpacing", 0.8f, "Spacing between items horizontally"); rowSpacing = Plugin.config.Bind("Sorter", "rowSpacing", 0.8f, "Spacing between rows vertically"); itemsPerRow = Plugin.config.Bind("Sorter", "itemsPerRow", 9, "Number of items per row"); skippedItems = Plugin.config.Bind("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("Sorter", "sortAreaWidth", 9f, "Reserved: used only for future bounding. (Keeping for compatibility)"); sortAreaDepth = Plugin.config.Bind("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("Sorter", "wallPadding", 0.25f, "Padding used for depth calculation (prevents placing type rows into doors/walls)."); stackSameTypeTogether = Plugin.config.Bind("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("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(); } [IteratorStateMachine(typeof(d__37))] private IEnumerator DropHeldItemIfAny(GameObject ship) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__37(0) { <>4__this = this, ship = ship }; } 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)); } [IteratorStateMachine(typeof(d__40))] public IEnumerator SortItems(bool force, bool ignoreSkippedItems = false, bool includeSavedPositionTypesEvenIfSkipped = false) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__40(0) { <>4__this = this, force = force, ignoreSkippedItems = ignoreSkippedItems, includeSavedPositionTypesEvenIfSkipped = includeSavedPositionTypesEvenIfSkipped }; } 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("k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("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> dictionary = new Dictionary>(); foreach (GrabbableObject item in scrap) { if (!ShouldSkipExplicitQuery(item)) { string key = item.Name(); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List()); } 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> dictionary = new Dictionary>(); 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()); } 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()); } 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> dictionary = new Dictionary>(); 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()); } 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()); } 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> 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 source = grouped.Keys.ToList(); string qLoose = q.Replace("_", ""); List 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; } [IteratorStateMachine(typeof(d__48))] 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) //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__48(0) { <>4__this = this, itemKey = itemKey, targetCenterShipLocal = targetCenterShipLocal, force = force, announce = announce, ignoreSkipLists = ignoreSkipLists, applyTwoHandedSortYOffset = applyTwoHandedSortYOffset, dropHeldFirst = dropHeldFirst }; } private Dictionary CreateLayout(List itemNames, HashSet? reservedTypes = null) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); 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[] array = Object.FindObjectsOfType(); GrabbableObject[] array2 = array; foreach (GrabbableObject item2 in array2) { if (!(includeSkippedItems ? ShouldSkipExplicitQuery(item2) : ShouldSkip(item2)) && ShouldIncludeItemByLocation(item2)) { scrap.Add(item2); } } scrap = (from item in scrap orderby item.scrapValue, item.Name() select item).ToList(); } private static bool ShouldIncludeItemByLocation(GrabbableObject item) { return (Object)(object)item != (Object)null && (Ship.ShouldSortAllDetectedItems || item.isInShipRoom); } [IteratorStateMachine(typeof(d__52))] private IEnumerator GrabbableRetry(GrabbableObject item) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__52(0) { <>4__this = this, item = item }; } 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 hashSet = new HashSet(); List list2 = new List(); 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 ParseSkipListTokens(string list) { if (string.IsNullOrWhiteSpace(list)) { return new List(); } 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 "; return false; } List 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 "; return false; } List 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 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 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 -> add token to skippedItems (name can include spaces)\n /sort skip remove -> remove token from skippedItems\n /sort -> pull that item type to YOUR position (e.g. /sort cash_register)\n /sort -> shortcut from JSON (pull to you) (e.g. /sort 1)\n /sort bind -> bind your HELD item to an alias name OR shortcut id (then /sort or /sort 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 kwargs, out string? error) { //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_0b0e: Unknown result type (might be due to invalid IL or missing references) //IL_0b1d: Unknown result type (might be due to invalid IL or missing references) //IL_0b2c: 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 (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(); string text = ((array != null && array.Length != 0) ? array[0] : ""); switch (text) { default: if (!(text == "reset")) { 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; } } break; case "skip": case "bindings": case "binds": case "shortcuts": case "aliases": case "positions": case "bind": break; } Sorter sorter = null; if ((Object)(object)Plugin.sorterObject != (Object)null) { sorter = Plugin.sorterObject.GetComponent(); } 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(); if ((Object)(object)sorter == (Object)null) { sorter = Plugin.sorterObject.AddComponent(); 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 ..."; return false; } string text2 = array[1]; if (text2 == "list" || text2 == "ls") { List skippedTokens = sorter.GetSkippedTokens(); if (skippedTokens.Count == 0) { ChatCommandAPI.Print("skippedItems is empty."); return true; } string text3 = string.Join(", ", skippedTokens.Take(20)); if (skippedTokens.Count > 20) { text3 += ", ..."; } ChatCommandAPI.Print($"skippedItems ({skippedTokens.Count}): {text3}"); return true; } switch (text2) { case "add": { string text5 = string.Join(" ", array.Skip(2)).Trim(); if (string.IsNullOrWhiteSpace(text5)) { text5 = (Player.Local?.currentlyHeldObjectServer)?.Name() ?? ""; if (string.IsNullOrWhiteSpace(text5)) { error = "Usage: /sort skip add (or hold an item)"; return false; } } text5 = ResolveSkipTarget(text5); if (!sorter.TrySkipAdd(text5, out error, out string message2)) { return false; } if (!string.IsNullOrWhiteSpace(message2)) { ChatCommandAPI.Print(message2); } return true; } default: if (!(text2 == "del")) { error = "Usage: /sort skip ..."; return false; } goto case "remove"; case "remove": case "rm": { 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 remove (or hold an item)"; return false; } } text4 = ResolveSkipTarget(text4); if (!sorter.TrySkipRemove(text4, 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 (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 text6 = string.Join(" ", array.Skip(2)).Trim(); if (string.IsNullOrWhiteSpace(text6)) { error = "Usage: /sort bind reset "; return false; } if (int.TryParse(text6, 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(text6, out bool removed2, out string error4)) { error = error4 ?? "Failed to remove alias."; return false; } string text7 = Extensions.NormalizeName(text6); ChatCommandAPI.Print(removed2 ? ("Unbound " + text7) : ("No binding for " + text7)); 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 text8 = string.Join(" ", array.Skip(1)); string text9 = val.Name(); if (int.TryParse(text8.Trim(), out var result2) && result2 > 0) { if (!SortShortcuts.SetShortcut(result2, text9, out string error5)) { error = error5 ?? "Failed to bind shortcut."; return false; } ChatCommandAPI.Print($"Bound {result2} => {text9}"); return true; } if (!SortShortcuts.BindAlias(text8, text9, out string error6)) { error = error6 ?? "Failed to bind alias."; return false; } ChatCommandAPI.Print("Bound " + Extensions.NormalizeName(text8) + " => " + text9); return true; } if (array[0] == "reset") { string text10 = ((array.Length > 1) ? Extensions.NormalizeName(string.Join(" ", array.Skip(1))) : (Player.Local?.currentlyHeldObjectServer)?.Name()); if (string.IsNullOrWhiteSpace(text10)) { error = "Missing item name (hold the item or provide a name)."; return false; } if (!SortPositions.Remove(text10, 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 '" + text10 + "'."); } else { ChatCommandAPI.Print("No saved sort position for '" + text10 + "'."); } return true; } if (array[0] == "set") { string text11 = ((array.Length > 1) ? string.Join(" ", array.Skip(1)) : null); if (!sorter.TrySetAndMoveTypeToPlayer(text11, force: false, out string resolvedItemKey, out error)) { return false; } if (!string.IsNullOrWhiteSpace(text11)) { string text12 = Extensions.NormalizeName(text11); if (!string.IsNullOrWhiteSpace(text12) && text12 != resolvedItemKey) { ChatCommandAPI.Print("Resolved '" + text11 + "' => '" + resolvedItemKey + "'."); } } string text13 = (string.IsNullOrWhiteSpace(resolvedItemKey) ? "held_item" : resolvedItemKey); if (SortPositions.TryGet(resolvedItemKey, out Vector3 shipLocalPos, out string error8)) { ChatCommandAPI.Print($"Saved sort position for '{text13}' => (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 '" + text13 + "'."); } 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 text14 = 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) { text14 += ", ..."; } ChatCommandAPI.Print(text14); 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 text15 = string.Join(", ", list2.Select<(int, string), string>(((int id, string itemKey) s) => $"{s.id}={s.itemKey}")); ChatCommandAPI.Print(text15); } if (list3.Count > 0) { string text16 = string.Join(", ", from a in list3.Take(12) select a.alias + "=" + a.itemKey); if (list3.Count > 12) { text16 += ", ..."; } ChatCommandAPI.Print(text16); } 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 -> bind your HELD item to an alias name OR shortcut id"; public override bool Invoke(string[] args, Dictionary kwargs, out string? error) { string[] array = new string[1] { "bind" }.Concat(args ?? Array.Empty()).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 kwargs, out string? error) { string[] array = new string[1] { "set" }.Concat(args ?? Array.Empty()).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 kwargs, out string? error) { string[] array = new string[1] { "reset" }.Concat(args ?? Array.Empty()).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 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 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 -> show skippedItems tokens\n /sk add [itemName] -> add token (or use held item if omitted)\n /sk remove [itemName] -> remove token (or use held item if omitted)"; public override bool Invoke(string[] args, Dictionary kwargs, out string? error) { string[] array = new string[1] { "skip" }.Concat(args ?? Array.Empty()).ToArray(); return ((Command)new SortCommand()).Invoke(array, kwargs, ref error); } } public class PileCommand : Command { public override string Name => "pile"; public override string[] Commands => new string[2] { "pile", ((Command)this).Name }; public override string Description => "Piles a specific item type onto your position (like /sort ).\nUsage:\n /pile -> pull that item type to YOUR position (partial match supported)\n /pile -> uses your HELD item type and also moves the held item"; public override bool Invoke(string[] args, Dictionary kwargs, out string? error) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown error = null; if (args.Length != 0 && args[0] == "help") { ChatCommandAPI.Print(((Command)this).Description); return true; } if (!Sorter.EnsureLocalPlayerInShip(out string error2)) { error = error2 ?? "You must be inside the ship to use this command."; return false; } Sorter sorter = null; if ((Object)(object)Plugin.sorterObject != (Object)null) { sorter = Plugin.sorterObject.GetComponent(); } 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); } sorter = Plugin.sorterObject.GetComponent(); if ((Object)(object)sorter == (Object)null) { sorter = Plugin.sorterObject.AddComponent(); } } catch (Exception ex) { error = "Sorter not initialized yet"; Log.Error("PileCommand failed: " + ex.Message); return false; } } string queryOrNull = ((args != null && args.Length != 0) ? string.Join(" ", args) : null); Log.ConfirmSound(); return sorter.TryStartPileByQueryOrHeld(queryOrNull, force: false, out error); } } internal static class SortPositions { [Serializable] private class PositionsFile { public List positions = new List(); } [Serializable] private class PositionEntry { public string item = ""; public float x; public float y; public float z; } public static string PositionsPath => Path.Combine(Paths.ConfigPath, "pasta.quicksort.sort.positions.json"); public static void EnsureFileExists() { try { string directoryName = Path.GetDirectoryName(PositionsPath); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(PositionsPath)) { PositionsFile positionsFile = new PositionsFile { positions = new List { new PositionEntry { item = "soccer_ball", x = 9f, y = -1.2f, z = -8.4f }, new PositionEntry { item = "whoopie_cushion", x = 9.4f, y = 1.81f, z = -6.3f } } }; string content = JsonConvert.SerializeObject((object)positionsFile, (Formatting)1); if (!TryWriteAllTextAtomic(PositionsPath, content, out string error)) { throw new IOException(error ?? "Failed to write positions file (unknown error)."); } } } catch (Exception ex) { Log.Warning("Failed to create positions file: " + ex.GetType().Name + ": " + ex.Message); Log.Warning("Stack trace: " + ex.StackTrace); } } private static PositionsFile Load(out string? error) { error = null; EnsureFileExists(); try { string text = File.ReadAllText(PositionsPath, Encoding.UTF8); PositionsFile positionsFile = JsonConvert.DeserializeObject(text) ?? new PositionsFile(); PositionsFile positionsFile2 = positionsFile; if (positionsFile2.positions == null) { positionsFile2.positions = new List(); } return positionsFile; } catch (Exception ex) { error = "Failed to load positions JSON: " + ex.Message; return new PositionsFile(); } } private static bool Save(PositionsFile data, out string? error) { error = null; try { string directoryName = Path.GetDirectoryName(PositionsPath); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } string content = JsonConvert.SerializeObject((object)data, (Formatting)1); if (!TryWriteAllTextAtomic(PositionsPath, content, out error)) { return false; } if (!File.Exists(PositionsPath)) { error = "Positions JSON not found after write. Path='" + PositionsPath + "', CWD='" + Directory.GetCurrentDirectory() + "', ConfigPath='" + Paths.ConfigPath + "', BepInExRoot='" + Paths.BepInExRootPath + "'."; return false; } FileInfo fileInfo = new FileInfo(PositionsPath); if (fileInfo.Length <= 0) { error = $"Positions JSON is empty after write. Path='{PositionsPath}', LastWriteUtc='{fileInfo.LastWriteTimeUtc:o}'."; return false; } return true; } catch (Exception ex) { error = "Failed to save positions JSON: " + ex.Message; return false; } } private static bool TryWriteAllTextAtomic(string path, string content, out string? error) { error = null; try { string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } string text = path + ".tmp"; File.WriteAllText(text, content, Encoding.UTF8); if (File.Exists(path)) { string destinationBackupFileName = path + ".bak"; try { File.Replace(text, path, destinationBackupFileName, ignoreMetadataErrors: true); } catch { File.Copy(text, path, overwrite: true); File.Delete(text); } } else { File.Move(text, path); } return true; } catch (Exception ex) { error = ex.GetType().Name + ": " + ex.Message + " (Path='" + path + "', CWD='" + Directory.GetCurrentDirectory() + "', ConfigPath='" + Paths.ConfigPath + "')"; return false; } } public static bool TryGet(string itemKey, out Vector3 shipLocalPos, out string? error) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) string itemKey2 = itemKey; shipLocalPos = default(Vector3); itemKey2 = Extensions.NormalizeName(itemKey2); PositionsFile positionsFile = Load(out error); if (error != null) { return false; } PositionEntry positionEntry = positionsFile.positions?.FirstOrDefault((PositionEntry p) => p != null && Extensions.NormalizeName(p.item) == itemKey2); if (positionEntry == null) { return false; } shipLocalPos = new Vector3(positionEntry.x, positionEntry.y, positionEntry.z); return true; } public static bool Set(string itemKey, Vector3 shipLocalPos, out string? error) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) string itemKey2 = itemKey; itemKey2 = Extensions.NormalizeName(itemKey2); PositionsFile positionsFile = Load(out error); if (error != null) { return false; } PositionsFile positionsFile2 = positionsFile; if (positionsFile2.positions == null) { positionsFile2.positions = new List(); } PositionEntry positionEntry = positionsFile.positions.FirstOrDefault((PositionEntry p) => p != null && Extensions.NormalizeName(p.item) == itemKey2); if (positionEntry == null) { positionEntry = new PositionEntry { item = itemKey2 }; positionsFile.positions.Add(positionEntry); } positionEntry.item = itemKey2; positionEntry.x = shipLocalPos.x; positionEntry.y = shipLocalPos.y; positionEntry.z = shipLocalPos.z; if (!Save(positionsFile, out error)) { if (error != null) { Log.Warning(error); } return false; } Log.Info($"Saved sort position '{itemKey2}' => (x={shipLocalPos.x:F3}, y={shipLocalPos.y:F3}, z={shipLocalPos.z:F3}) to {PositionsPath}"); return true; } public static bool Remove(string itemKey, out bool removed, out string? error) { string itemKey2 = itemKey; removed = false; itemKey2 = Extensions.NormalizeName(itemKey2); PositionsFile positionsFile = Load(out error); if (error != null) { return false; } if (positionsFile.positions == null || positionsFile.positions.Count == 0) { removed = false; return true; } int count = positionsFile.positions.Count; positionsFile.positions = positionsFile.positions.Where((PositionEntry p) => p != null && Extensions.NormalizeName(p.item) != itemKey2).ToList(); removed = positionsFile.positions.Count != count; if (!Save(positionsFile, out error)) { if (error != null) { Log.Warning(error); } return false; } if (removed) { Log.Info("Removed sort position '" + itemKey2 + "' from " + PositionsPath); } return true; } public static List<(string itemKey, Vector3 shipLocalPos)> ListAll(out string? error) { PositionsFile positionsFile = Load(out error); if (error != null) { return new List<(string, Vector3)>(); } if (positionsFile.positions == null) { return new List<(string, Vector3)>(); } return (from p in positionsFile.positions.Where((PositionEntry p) => p != null && !string.IsNullOrWhiteSpace(p.item)).Select((Func)((PositionEntry p) => (Extensions.NormalizeName(p.item), new Vector3(p.x, p.y, p.z)))) orderby p.Item1 select p).ToList(); } } internal static class SortShortcuts { [Serializable] private class ShortcutFile { public List shortcuts = new List(); public Dictionary aliases = new Dictionary(); } [Serializable] private class Shortcut { public int id; public string item = ""; } private static string OldShortcutPath => Path.Combine(Paths.ConfigPath, "pasta.quicksort.sort.shortcuts.json"); public static string ShortcutPath => Path.Combine(Paths.ConfigPath, "pasta.quicksort.sort.bindings.json"); public static void EnsureFileExists() { try { string directoryName = Path.GetDirectoryName(ShortcutPath); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(ShortcutPath) && File.Exists(OldShortcutPath)) { try { File.Copy(OldShortcutPath, ShortcutPath, overwrite: false); Log.Info("Migrated shortcuts file '" + OldShortcutPath + "' => '" + ShortcutPath + "'"); } catch (Exception ex) { Log.Warning("Failed to migrate shortcuts file: " + ex.GetType().Name + ": " + ex.Message); } } if (!File.Exists(ShortcutPath)) { ShortcutFile shortcutFile = new ShortcutFile { shortcuts = new List { new Shortcut { id = 1, item = "weed_killer" }, new Shortcut { id = 2, item = "shovel" } } }; string content = JsonConvert.SerializeObject((object)shortcutFile, (Formatting)1); if (!TryWriteAllTextAtomic(ShortcutPath, content, out string error)) { throw new IOException(error ?? "Failed to write shortcuts file (unknown error)."); } } } catch (Exception ex2) { Log.Warning("Failed to create shortcuts file: " + ex2.GetType().Name + ": " + ex2.Message); Log.Warning("Stack trace: " + ex2.StackTrace); } } public static bool TryResolve(int id, out string itemKey, out string? error) { itemKey = ""; error = null; EnsureFileExists(); try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text); if (shortcutFile?.shortcuts == null) { error = "Shortcuts file is empty or invalid."; return false; } ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } Shortcut shortcut = shortcutFile.shortcuts.FirstOrDefault((Shortcut s) => s != null && s.id == id); if (shortcut == null || string.IsNullOrWhiteSpace(shortcut.item)) { error = $"No shortcut found for {id}."; return false; } itemKey = Extensions.NormalizeName(shortcut.item); return true; } catch (Exception ex) { error = "Failed to load shortcuts JSON: " + ex.Message; return false; } } public static bool SetShortcut(int id, string itemKey, out string? error) { error = null; EnsureFileExists(); if (id <= 0) { error = "Shortcut id must be >= 1."; return false; } itemKey = Extensions.NormalizeName(itemKey); if (string.IsNullOrWhiteSpace(itemKey)) { error = "Invalid item key."; return false; } try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text) ?? new ShortcutFile(); ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.shortcuts == null) { shortcutFile2.shortcuts = new List(); } shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } Shortcut shortcut = shortcutFile.shortcuts.FirstOrDefault((Shortcut s) => s != null && s.id == id); if (shortcut == null) { shortcut = new Shortcut { id = id, item = itemKey }; shortcutFile.shortcuts.Add(shortcut); } shortcut.id = id; shortcut.item = itemKey; string content = JsonConvert.SerializeObject((object)shortcutFile, (Formatting)1); if (!TryWriteAllTextAtomic(ShortcutPath, content, out error)) { return false; } return true; } catch (Exception ex) { error = "Failed to save shortcuts JSON: " + ex.Message; return false; } } public static bool TryResolveAlias(string alias, out string itemKey, out string? error) { itemKey = ""; error = null; EnsureFileExists(); alias = Extensions.NormalizeName(alias); if (string.IsNullOrWhiteSpace(alias)) { error = "Invalid alias."; return false; } try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text); if (shortcutFile == null) { error = "Shortcuts file is empty or invalid."; return false; } ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } if (!shortcutFile.aliases.TryGetValue(alias, out string value) || string.IsNullOrWhiteSpace(value)) { error = "No alias found for '" + alias + "'."; return false; } itemKey = Extensions.NormalizeName(value); return true; } catch (Exception ex) { error = "Failed to load shortcuts JSON: " + ex.Message; return false; } } public static bool BindAlias(string alias, string itemKey, out string? error) { error = null; EnsureFileExists(); alias = Extensions.NormalizeName(alias); itemKey = Extensions.NormalizeName(itemKey); if (string.IsNullOrWhiteSpace(alias)) { error = "Invalid alias."; return false; } if (string.IsNullOrWhiteSpace(itemKey)) { error = "Invalid item key."; return false; } try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text) ?? new ShortcutFile(); ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.shortcuts == null) { shortcutFile2.shortcuts = new List(); } shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } shortcutFile.aliases[alias] = itemKey; string content = JsonConvert.SerializeObject((object)shortcutFile, (Formatting)1); if (!TryWriteAllTextAtomic(ShortcutPath, content, out error)) { return false; } return true; } catch (Exception ex) { error = "Failed to save shortcuts JSON: " + ex.Message; return false; } } public static bool RemoveShortcut(int id, out bool removed, out string? error) { removed = false; error = null; EnsureFileExists(); if (id <= 0) { error = "Shortcut id must be >= 1."; return false; } try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text) ?? new ShortcutFile(); ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.shortcuts == null) { shortcutFile2.shortcuts = new List(); } shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } int count = shortcutFile.shortcuts.Count; shortcutFile.shortcuts = shortcutFile.shortcuts.Where((Shortcut s) => s != null && s.id != id).ToList(); removed = shortcutFile.shortcuts.Count != count; string content = JsonConvert.SerializeObject((object)shortcutFile, (Formatting)1); if (!TryWriteAllTextAtomic(ShortcutPath, content, out error)) { return false; } return true; } catch (Exception ex) { error = "Failed to save shortcuts JSON: " + ex.Message; return false; } } public static bool RemoveAlias(string alias, out bool removed, out string? error) { removed = false; error = null; EnsureFileExists(); alias = Extensions.NormalizeName(alias); if (string.IsNullOrWhiteSpace(alias)) { error = "Invalid alias."; return false; } try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text) ?? new ShortcutFile(); ShortcutFile shortcutFile2 = shortcutFile; if (shortcutFile2.shortcuts == null) { shortcutFile2.shortcuts = new List(); } shortcutFile2 = shortcutFile; if (shortcutFile2.aliases == null) { shortcutFile2.aliases = new Dictionary(); } removed = shortcutFile.aliases.Remove(alias); string content = JsonConvert.SerializeObject((object)shortcutFile, (Formatting)1); if (!TryWriteAllTextAtomic(ShortcutPath, content, out error)) { return false; } return true; } catch (Exception ex) { error = "Failed to save shortcuts JSON: " + ex.Message; return false; } } public static List<(string alias, string itemKey)> ListAliases(out string? error) { error = null; EnsureFileExists(); try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text); if (shortcutFile?.aliases == null) { return new List<(string, string)>(); } return (from kv in shortcutFile.aliases where !string.IsNullOrWhiteSpace(kv.Key) && !string.IsNullOrWhiteSpace(kv.Value) select (Extensions.NormalizeName(kv.Key), Extensions.NormalizeName(kv.Value)) into x orderby x.Item1 select x).ToList(); } catch (Exception ex) { error = "Failed to load shortcuts JSON: " + ex.Message; return new List<(string, string)>(); } } public static List<(int id, string itemKey)> ListShortcuts(out string? error) { error = null; EnsureFileExists(); try { string text = File.ReadAllText(ShortcutPath, Encoding.UTF8); ShortcutFile shortcutFile = JsonConvert.DeserializeObject(text); if (shortcutFile?.shortcuts == null) { return new List<(int, string)>(); } return (from s in shortcutFile.shortcuts where s != null && s.id != 0 && !string.IsNullOrWhiteSpace(s.item) orderby s.id select (s.id, Extensions.NormalizeName(s.item))).ToList(); } catch (Exception ex) { error = "Failed to load shortcuts JSON: " + ex.Message; return new List<(int, string)>(); } } private static bool TryWriteAllTextAtomic(string path, string content, out string? error) { error = null; try { string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } string text = path + ".tmp"; File.WriteAllText(text, content, Encoding.UTF8); if (File.Exists(path)) { string destinationBackupFileName = path + ".bak"; try { File.Replace(text, path, destinationBackupFileName, ignoreMetadataErrors: true); } catch { File.Copy(text, path, overwrite: true); File.Delete(text); } } else { File.Move(text, path); } return true; } catch (Exception ex) { error = ex.GetType().Name + ": " + ex.Message + " (Path='" + path + "')"; return false; } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "QuickSort"; public const string PLUGIN_NAME = "QuickSort"; public const string PLUGIN_VERSION = "1.2.1"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }