using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; 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.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; 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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: AssemblyCompany("kylethescientist.scrapmagic")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("A client-sided mod for organizing and selling items ")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: AssemblyProduct("ScrapMagic")] [assembly: AssemblyTitle("kylethescientist.scrapmagic")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ScrapMagic { 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; foreach (Argument argument in array) { if (argument.name == name) { return ((Argument)argument).value; } } return default(T); } public T Get(int name) { Argument[] array = arguments; foreach (Argument argument in array) { 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("-") && array[i].Length > 1) { 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 Dictionary itemOffsets = new Dictionary { { "weed_killer", Vector3.forward * 0.5f }, { "stun_grenade", Vector3.forward * 0.05f }, { "walkie_talkie", Vector3.forward * 0.025f }, { "spray_paint", Vector3.forward * 0.05f }, { "belt_bag", Vector3.forward * 0.05f + Vector3.right * 0.01f }, { "zap_gun", Vector3.right * 0.25f }, { "jetpack", Vector3.forward * 0.35f + Vector3.up * -0.6f } }; public static string Name(this GrabbableObject item) { return item.itemProperties.Name(); } public static string Name(this Item item) { return item.itemName.ToLower().Replace(" ", "_").Replace("-", "_"); } public static float AvgValue(this GrabbableObject item) { return (item.itemProperties.maxValue + item.itemProperties.minValue) / 2; } public static Vector3 PlacementOffset(this GrabbableObject item) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) return itemOffsets.GetValueOrDefault(item.Name()); } public static string AddToList(this string s, string t) { t = t.Trim().ToLower().Replace(" ", "_") .Replace("-", "_"); List list = (from s in s.Split(',') select s.Trim()).ToList(); list.Add(t); return string.Join(", ", list); } public static string RemoveFromList(this string s, string t) { t = t.Trim().ToLower().Replace(" ", "_") .Replace("-", "_"); List list = (from s in s.Split(',') select s.Trim()).ToList(); list.Remove(t); return string.Join(", ", list); } public static bool ContainsItem(this string s, string t) { t = t.Trim().ToLower().Replace(" ", "_") .Replace("-", "_"); List list = (from s in s.Split(',') select s.Trim()).ToList(); return list.Contains(t); } public static GrabbableObject[] ItemsOnCounter(this DepositItemsDesk desk) { return ((Component)desk).GetComponentsInChildren(); } } public class Log { private static ManualLogSource _log; public static void Init(ManualLogSource log) { _log = log; } private static string JoinObjects(params object[] objects) { string text = ""; foreach (object obj in objects) { text = text + obj.ToString() + " "; } return text; } public static void NotifyPlayer(string header, string body = "", bool isWarning = false) { HUDManager.Instance.DisplayTip(header, body, isWarning, false, "LC_Tip1"); Debug(header); Debug(body); } public static void Chat(string body, string color = "FFFFFF") { HUDManager.Instance.AddChatMessage("[SM] " + body + "", "", -1, false); Debug(body); NeutralSound(); } public static void ConfirmSound() { HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonTuneSFX); } public static void ErrorSound() { HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonSelectSFX); } public static void NeutralSound() { HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonCancelSFX); } public static void PlaySound(int sound) { HUDManager instance = HUDManager.Instance; AudioSource uIAudio = instance.UIAudio; GameNetworkManager instance2 = GameNetworkManager.Instance; switch (sound) { case 0: uIAudio.PlayOneShot(instance2.buttonSelectSFX); break; case 1: uIAudio.PlayOneShot(instance2.buttonCancelSFX); break; case 2: uIAudio.PlayOneShot(instance2.buttonPressSFX); break; case 3: uIAudio.PlayOneShot(instance2.buttonTuneSFX); break; case 4: uIAudio.PlayOneShot(instance.addToScrapTotalSFX); break; case 5: uIAudio.PlayOneShot(instance.decreaseXPSFX); break; case 6: uIAudio.PlayOneShot(instance.displayCollectedScrapSFX); break; case 7: uIAudio.PlayOneShot(instance.displayCollectedScrapSFXSmall); break; case 8: uIAudio.PlayOneShot(instance.finishAddingToTotalSFX); break; case 9: uIAudio.PlayOneShot(instance.globalNotificationSFX); break; case 10: uIAudio.PlayOneShot(instance.increaseXPSFX); break; case 11: uIAudio.PlayOneShot(instance.levelDecreaseSFX); break; case 12: uIAudio.PlayOneShot(instance.levelIncreaseSFX); break; case 13: uIAudio.PlayOneShot(instance.newProfitQuotaSFX); break; case 14: uIAudio.PlayOneShot(instance.OneDayToMeetQuotaSFX); break; case 15: uIAudio.PlayOneShot(instance.profitQuotaDaysLeftCalmSFX); break; case 16: uIAudio.PlayOneShot(instance.reachedQuotaSFX); break; case 17: uIAudio.PlayOneShot(instance.scanSFX); break; } } 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 Fatal(params object[] objects) { _log.LogFatal((object)JoinObjects(objects)); } public static void Error(params object[] objects) { _log.LogError((object)JoinObjects(objects)); } public static void Warning(params object[] objects) { _log.LogWarning((object)JoinObjects(objects)); } public static void Message(params object[] objects) { _log.LogMessage((object)JoinObjects(objects)); } public static void Info(params object[] objects) { _log.LogInfo((object)JoinObjects(objects)); } public static void Debug(params object[] objects) { _log.LogDebug((object)JoinObjects(objects)); } } public static class Patches { public static void Init() { Harmony.CreateAndPatchAll(typeof(Ship), (string)null); Harmony.CreateAndPatchAll(typeof(Chat), (string)null); Harmony.CreateAndPatchAll(typeof(Startup), (string)null); Harmony.CreateAndPatchAll(typeof(Grab), (string)null); } } public static class Startup { private static GameObject scrapMagic; [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] private static void OnLocalPlayerCreated(PlayerControllerB __instance) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)scrapMagic)) { Object.Destroy((Object)(object)scrapMagic); } scrapMagic = new GameObject("ScrapMagic"); scrapMagic.AddComponent(); scrapMagic.AddComponent(); } [HarmonyPatch(typeof(GrabbableObject), "OnHitGround")] [HarmonyPostfix] private static void OnItemHitGround(GrabbableObject __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Sorter.inProgress) { __instance.floorYRot = -1; ((Component)__instance).transform.eulerAngles = Vector3.zero; } } } public static class Ship { public static Action OnShipOrbit; public static Action OnShipTouchdown; public static Action OnShipAscent; public static Action OnShipDescent; public static bool InOrbit { get; private set; } public static bool Stationary => InOrbit || StartOfRound.Instance.shipHasLanded; [HarmonyPatch(typeof(StartOfRound), "SwitchMapMonitorPurpose")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnShipStateChanged(bool displayInfo) { InOrbit = displayInfo; if (displayInfo) { OnShipOrbit?.Invoke(); } else { OnShipDescent?.Invoke(); } } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnShipLeave() { OnShipAscent?.Invoke(); } [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnShipLanded() { OnShipTouchdown?.Invoke(); } } 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.Trim(); if (!text.StartsWith("/") || text.Length == 1) { return true; } if (text == "/help") { string text2 = "Commands: "; text2 += string.Join(", ", ChatCommand.Commands); Log.Chat(text2); CloseChat(__instance); } 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 static void Reset() { ChatCommand.Commands.Clear(); } [HarmonyPatch(typeof(HUDManager), "Start")] [HarmonyPostfix] public static void OnHUDStart(HUDManager __instance) { try { Log.Info("Hud started"); ((TMP_Text)__instance.chatText).fontSize = 11f; } catch (Exception e) { Log.Exception(e); } } } [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] public static class Grab { 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown List list = new List(instructions); CodeMatcher val = new CodeMatcher((IEnumerable)list, ilGenerator); 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)AccessTools.Method(typeof(NetworkBehaviour), "get_NetworkObject", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Stloc_0, (object)null) }) .ThrowIfInvalid("InteractItem not found") .CreateLabel(ref label) .Start() .Insert((CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Callvirt, (object)AccessTools.Method(typeof(Player), "OverrideGrabbingObject", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Brtrue, (object)label) }) .InstructionEnumeration(); } } public static class Player { public static GrabbableObject overrideObject; public static PlayerControllerB Local => StartOfRound.Instance?.localPlayerController; public static bool CanGrabObject(GrabbableObject item) { List list = Seller.desk?.itemsOnCounter; if (Blocked(!Object.op_Implicit((Object)(object)item), "item is null") || Blocked(!item.grabbable, "item not grabbable") || Blocked(item.deactivated, "item is deactivated") || Blocked(item.isHeld, "item is held") || Blocked(item.isPocketed, "item is pocketed") || Blocked(list?.Contains(item) ?? false, "item is on counter")) { return false; } if (Blocked(Local.isPlayerDead, "player dead") || Blocked(Local.isTypingChat, "player in chat window") || Blocked(Local.inTerminalMenu, "player in terminal") || Blocked(Local.throwingObject, "player throwing object") || Blocked(Local.IsInspectingItem, "player inspecting item") || Blocked(Local.isGrabbingObjectAnimation, "player in grab animation") || Blocked(Object.op_Implicit((Object)(object)Local.inAnimationWithEnemy), "player in animation with enemy") || Blocked(Local.inSpecialInteractAnimation, "player in special interact animation") || Blocked(Local.jetpackControls, "player using jetpack") || Blocked(Local.disablingJetpackControls, "player disabling jetpack") || Blocked(Local.activatingItem, "player activating item") || Blocked(Local.waitingToDropItem, "player waiting to drop item") || Blocked(Local.FirstEmptyItemSlot((GrabbableObject)null) == -1, "player has no empty slots")) { return false; } return true; static bool Blocked(bool condition, string reason) { if (condition) { Log.Debug(" Can't grab: " + reason); } return condition; } } public static IEnumerator DefuseEgg(StunGrenadeItem egg) { if (Object.op_Implicit((Object)(object)egg)) { if (egg.explodeOnThrow) { Local.SwitchToItemSlot(Local.NextItemSlot(true), (GrabbableObject)null); yield return (object)new WaitForSeconds(0.5f); Local.SwitchToItemSlot(Local.NextItemSlot(false), (GrabbableObject)null); } int retry = 20; while (egg.explodeOnThrow && retry > 0) { Local.SwitchToItemSlot(Local.NextItemSlot(true), (GrabbableObject)null); yield return (object)new WaitForSeconds(0.5f); Local.SwitchToItemSlot(Local.NextItemSlot(false), (GrabbableObject)null); Log.Chat("Egg will pop if placed! Stand somewhere else!"); retry--; } if (retry == 0) { Log.Chat("Skipping egg."); } } } public static bool OverrideGrabbingObject() { Log.Debug("Overriding grabbing object?"); if (overrideObject == null) { return false; } Log.Debug("Overriding grabbing object to", overrideObject.Name()); Local.currentlyGrabbingObject = overrideObject; overrideObject = null; return true; } public static IEnumerator StartGrabbingObject(GrabbableObject grabbableObject) { Log.Debug("Grabbing " + grabbableObject.itemProperties.itemName); bool success = false; for (int i = 0; i < 30; i++) { if (CanGrabObject(grabbableObject)) { success = true; break; } yield return (object)new WaitForEndOfFrame(); yield return (object)new WaitForEndOfFrame(); } if (!success) { Log.Chat("Failed to grab " + grabbableObject.itemProperties.itemName + "!"); yield break; } overrideObject = grabbableObject; Local.BeginGrabObject(); yield return Local.grabObjectCoroutine; } public static IEnumerator StartMovingObject(GrabbableObject item, Vector3 position, NetworkObject parent = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StartGrabbingObject(item); Log.Debug($"Moving {item.itemProperties.itemName} to {position}"); yield return DefuseEgg(((Component)item).GetComponent()); try { Log.Debug("Placing " + item.itemProperties.itemName); item.floorYRot = -1; Local.DiscardHeldObject(true, parent, position, false); } catch (Exception e) { Log.Exception(e); } } } [BepInPlugin("kylethescientist.scrapmagic", "ScrapMagic", "1.0.3")] public class Plugin : BaseUnityPlugin { public static ConfigFile config; private static Harmony harmony; public static Plugin Instance { get; private set; } private void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Instance = this; config = new ConfigFile(Paths.ConfigPath + "\\kylethescientist.scrapmagic.cfg", true); Log.Init(((BaseUnityPlugin)this).Logger); Patches.Init(); Log.Info("kylethescientist.scrapmagic v1.0.3 has loaded!"); } } public class Seller : MonoBehaviour { public ConfigEntry skippedItems; public static DepositItemsDesk desk; private InteractTrigger bell; private List toSell; public static bool InProgress { get; private set; } private void FindDesk() { if (!Object.op_Implicit((Object)(object)desk)) { desk = Object.FindObjectOfType(); } } private void Awake() { Log.Info("Initializing Seller"); skippedItems = Plugin.config.Bind("Seller", "skippedItems", "body, knife, shotgun, zed_dog", "Which items should be skipped when selling"); Ship.OnShipDescent = (Action)Delegate.Combine(Ship.OnShipDescent, new Action(FindDesk)); ChatCommand.New("sell", "Usage: /sell <$amount|all|quota|skip|unskip>", Sell); ChatCommand.New("ring", "Rings the company bell until the door opens", delegate { if (!InProgress) { ((MonoBehaviour)this).StartCoroutine(OpenDoor()); } else { Log.NotifyPlayer("Error", "Selling in progress. Press escape to cancel."); } }); Log.Info("Seller initialized"); } private void Update() { if (InProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { InProgress = false; Log.ErrorSound(); } } private void Configure(ChatArgs args) { if (args.Empty || args.Length == 1) { Log.Chat("", "FFFF00"); Log.NeutralSound(); } else if (args[0] == "skip") { Skip(args[1]); } else if (args[0] == "unskip") { Unskip(args[1]); } } private void Skip(string name) { if (skippedItems.Value.ContainsItem(name)) { Log.Chat("Item " + name + " already skipped", "FFFF00"); Log.NeutralSound(); } else { skippedItems.Value = skippedItems.Value.AddToList(name); Log.Chat("Skipping " + name, "FFFF00"); Log.ConfirmSound(); } } private void Unskip(string name) { if (!skippedItems.Value.ContainsItem(name)) { Log.Chat("Item " + name + " not skipped", "FFFF00"); Log.NeutralSound(); } else { skippedItems.Value = skippedItems.Value.RemoveFromList(name); Log.Chat("Unskipping " + name, "FFFF00"); Log.ConfirmSound(); } } private void Sell(ChatArgs args) { if (args.Empty || args[0] == "help") { Log.Chat(args.help, "FFFF00"); Log.NeutralSound(); return; } if (args[0] == "skip" || args[0] == "unskip") { Configure(args); return; } if (args[0] == "quota") { Sell(-1); return; } if (args[0] == "all") { Sell(-2); return; } string text = args[0].ToLower(); int num = 1; if (text.EndsWith('k')) { num = 1000; text = text.Substring(0, text.Length - 1); } if (!int.TryParse(text, out var result)) { Log.NotifyPlayer("Error", "Amount must be a number"); Log.ErrorSound(); } else if (result <= 0) { Log.NotifyPlayer("Error", "Amount must be greater than 0"); Log.ErrorSound(); } else { Sell(result * num); } } private void Sell(int amount) { if (InProgress) { Log.NotifyPlayer("Seller Error", "Selling in progress. Press escape to cancel.", isWarning: true); return; } if (!Object.op_Implicit((Object)(object)bell)) { GameObject obj = GameObject.Find("BellDinger/Trigger"); bell = ((obj != null) ? obj.GetComponent() : null); } if (!Object.op_Implicit((Object)(object)desk) || !Object.op_Implicit((Object)(object)bell)) { Log.NotifyPlayer("Seller Error", "Not at the company", isWarning: true); } else { ((MonoBehaviour)this).StartCoroutine(SellTo(amount)); } } public IEnumerator SellTo(int total) { InProgress = true; if (!Object.op_Implicit((Object)(object)desk) || !Object.op_Implicit((Object)(object)bell)) { InProgress = false; yield break; } yield return GetItemsToFillQuota(total); if (toSell.Count == 0) { InProgress = false; yield break; } int counter = 0; foreach (GrabbableObject item in toSell) { counter++; if (!InProgress) { Log.Chat("Selling cancelled", "FF0000"); yield break; } if (item.isHeld) { continue; } yield return Player.StartGrabbingObject(item); while (true) { yield return PlaceItemOnCounter(item); if (!item.isHeld) { break; } yield return (object)new WaitForSeconds(0.15f); Log.Warning("--- Failed to place item. Counter full?"); } } yield return OpenDoor(); InProgress = false; Player.Local.carryWeight = 1f; } private IEnumerator PlaceItemOnCounter(GrabbableObject item) { if ((Object)(object)Player.Local.currentlyHeldObjectServer != (Object)(object)item) { Log.Warning("Item was not picked up"); yield break; } yield return Player.DefuseEgg(((Component)item).GetComponent()); desk.AddObjectToDeskServerRpc(NetworkObjectReference.op_Implicit(((Component)item).gameObject.GetComponent())); Vector3 vector2 = RoundManager.RandomPointInBounds(((Collider)desk.triggerCollider).bounds); Bounds bounds = ((Collider)desk.triggerCollider).bounds; vector2.y = ((Bounds)(ref bounds)).min.y; RaycastHit raycastHit = default(RaycastHit); if (Physics.Raycast(new Ray(vector2 + Vector3.up * 3f, Vector3.down), ref raycastHit, 8f, 1048640, (QueryTriggerInteraction)2)) { vector2 = ((RaycastHit)(ref raycastHit)).point; } yield return Player.DefuseEgg(((Component)item).GetComponent()); vector2.y += item.itemProperties.verticalOffset; vector2 = ((Component)desk.deskObjectsContainer).transform.InverseTransformPoint(vector2); Player.Local.DiscardHeldObject(true, desk.deskObjectsContainer, vector2, false); } private IEnumerator OpenDoor() { while (!desk.doorOpen) { bell.Interact(((Component)Player.Local).transform); yield return (object)new WaitForSeconds(0.5f); } while (desk.doorOpen) { yield return (object)new WaitForSeconds(1f); } } public IEnumerator GetItemsToFillQuota(float quota) { float buyRate = StartOfRound.Instance.companyBuyingRate; bool sellingToQuota = quota == -1f; bool sellingAll = quota == -2f; toSell = new List(); float alreadySold = 0f; if (sellingToQuota) { quota = TimeOfDay.Instance.profitQuota; alreadySold += (float)TimeOfDay.Instance.quotaFulfilled; alreadySold += (float)desk.itemsOnCounter.Sum((GrabbableObject i) => i.scrapValue) * buyRate; if (alreadySold >= quota) { Log.NotifyPlayer("Done", "Quota already fulfilled"); Log.ConfirmSound(); if (desk.itemsOnCounter.Count > 0) { yield return OpenDoor(); } yield break; } } GiftBoxItem[] boxes = Object.FindObjectsOfType(); GiftBoxItem[] array = boxes; foreach (GiftBoxItem box in array) { if (!box.hasUsedGift && Object.op_Implicit((Object)(object)((Component)box).GetComponent())) { yield return Player.StartGrabbingObject((GrabbableObject)(object)box); ((GrabbableObject)box).ItemActivate(true, true); yield return (object)new WaitForSeconds(0.2f); } } List sellableItems = new List(); GrabbableObject[] onCounter = desk.ItemsOnCounter(); GrabbableObject[] array2 = Object.FindObjectsOfType(); foreach (GrabbableObject obj in array2) { if (obj.itemProperties.isScrap && !obj.isHeld && !obj.deactivated && !((IEnumerable)(object)boxes).Contains(obj) && !onCounter.Contains(obj) && !IsSkipped(obj.Name())) { sellableItems.Add(obj); } } sellableItems.Sort((GrabbableObject a, GrabbableObject b) => b.scrapValue.CompareTo(a.scrapValue)); string sellable = string.Join("\n\t", sellableItems.Select((GrabbableObject i) => i.Name() + $" ${i.scrapValue}")); Log.Debug("Sellable items:\n" + sellable); if (sellingToQuota) { Log.Debug($"Quota: {quota}"); if (buyRate != 1f) { quota = Mathf.CeilToInt(quota / buyRate); Log.Debug($"Adjusted quota: {quota}"); } if (alreadySold > 0f) { Log.Debug($"Already sold: {alreadySold}"); } } int target = (int)(quota - alreadySold); if (!sellingAll) { int sum = 0; int[] scrapValues = sellableItems.Select((GrabbableObject i) => i.scrapValue).ToArray(); for (int j = 0; j < scrapValues.Length; j++) { if (sum >= target - 200) { break; } sum += scrapValues[j]; toSell.Add(sellableItems[j]); } foreach (GrabbableObject item in toSell) { sellableItems.Remove(item); } Log.Debug($"Pre-knapsack sum: {sum}/{target}, remaining to fill: {target - sum}"); scrapValues = sellableItems.Select((GrabbableObject i) => i.scrapValue).ToArray(); Task task = Task.Run(delegate { List list = Knapsack(scrapValues, target - sum); if (list.Count == 0) { Log.NotifyPlayer("Sell Error", $"Only ${scrapValues.Sum()} available to sell", isWarning: true); } list.ForEach(delegate(int i) { toSell.Add(sellableItems[i]); }); }); Log.Chat("Calculating items to sell...", "FFFF00"); yield return (object)new WaitUntil((Func)(() => task.IsCompleted)); } else { toSell.AddRange(sellableItems); } if (toSell.Count == 0) { Log.NotifyPlayer("Error", "No items to sell"); Log.ErrorSound(); yield break; } float total = toSell.Sum((GrabbableObject i) => i.scrapValue); Log.NotifyPlayer($"Selling ${total}.", $"{toSell.Count} items"); string items = string.Join(", ", toSell.Select((GrabbableObject i) => i.Name() + $" ${i.scrapValue}")); Log.Debug("Items to sell: " + items); Log.ConfirmSound(); } public static List Knapsack(int[] nums, int target) { Log.Debug("Knapsack: " + string.Join(", ", nums)); int num = nums.Length; int num2 = nums.Sum(); int[,] array = new int[num + 1, num2 + 1]; int i; int j; for (i = 0; i <= num; i++) { for (j = 0; j <= num2; j++) { array[i, j] = 1000000000; } } array[0, 0] = 0; for (i = 1; i <= num; i++) { if (!InProgress) { return new List(); } for (j = 0; j <= num2; j++) { array[i, j] = array[i - 1, j]; if (j >= nums[i - 1]) { array[i, j] = Mathf.Min(array[i, j], array[i - 1, j - nums[i - 1]] + nums[i - 1]); } } } int num3 = 1000000000; int num4 = -1; for (j = target; j <= num2; j++) { if (array[num, j] >= target && array[num, j] < num3) { num3 = array[num, j]; num4 = j; } } List list = new List(); i = num; j = num4; while (i > 0 && j > 0) { if (array[i, j] != array[i - 1, j]) { list.Add(i - 1); j -= nums[i - 1]; } i--; } return list; } public bool IsSkipped(string s) { return skippedItems.Value.ContainsItem(s); } private void OnDestroy() { Ship.OnShipDescent = (Action)Delegate.Remove(Ship.OnShipDescent, new Action(FindDesk)); } } public class Sorter : MonoBehaviour { public ConfigEntry doorframeItems; public ConfigEntry summonCircleItems; public ConfigEntry skippedItems; public ConfigEntry cupboardTop; public ConfigEntry cupboardShelfA; public ConfigEntry cupboardShelfB; public ConfigEntry cupboardShelfC; public ConfigEntry cupboardShelfD; public ConfigEntry fixedLayout; private List allScrap; private List scrap; private List cupboardT; private List cupboardA; private List cupboardB; private List cupboardC; private List cupboardD; private List summonCircle; private List doorframe; public static bool inProgress; private bool force = false; private Vector3 originPosition = new Vector3(-5.8f, 0.5f, -5f); private Vector3 duckCenter = new Vector3(7f, 0.01f, -6.78f); private Vector3 shelfMinPosition = new Vector3(-6.78f, 4.23f, -8.62f); private Vector3 shelfMaxPosition = new Vector3(-6.82f, 4.354f, -4.97f); private bool CanSort => Ship.InOrbit || Ship.Stationary; private void Awake() { fixedLayout = Plugin.config.Bind("Sorter", "fixedLayout", false, "Whether each item will have a designated spot on the wall. This can reduce the amount of resorting that needs to be done upon obtaining a new item type"); skippedItems = Plugin.config.Bind("Sorter", "skippedItems", "body, clipboard, sticky_note, boombox", "Which items should be skipped when organizing"); doorframeItems = Plugin.config.Bind("Sorter", "doorframeItems", "apparatus, soccer_ball, whoopie_cushion", "Which items should be put on the doorframe"); summonCircleItems = Plugin.config.Bind("Sorter", "summonCircleItems", "", "Which items should be put in the summoning circle"); cupboardTop = Plugin.config.Bind("Sorter", "cupboardTop", "extension_ladder, radar_booster, jetpack", "Which items should be put on top of the cupboard"); cupboardShelfA = Plugin.config.Bind("Sorter", "cupboardShelfA", "kitchen_knife, shotgun, shovel", "Which items should be put on the cupboard's 1st shelf"); cupboardShelfB = Plugin.config.Bind("Sorter", "cupboardShelfB", "key, ammo, flashlight, pro_flashlight", "Which items should be put on the cupboard's 2st shelf"); cupboardShelfC = Plugin.config.Bind("Sorter", "cupboardShelfC", "belt_bag, stun_grenade, walkie_talkie, spray_paint", "Which items should be put on the cupboard's 3st shelf"); cupboardShelfD = Plugin.config.Bind("Sorter", "cupboardShelfD", "lockpicker, tzp_inhalant, weed_killer, zap_gun", "Which items should be put on the cupboard's 4st shelf"); ChatCommand.New("sort", "Usage: /sort [-r|-redo] [help]", Organize); ChatCommand.New("pile", "Usage: /pile ", Pile); } private void Start() { GrabbableObject[] array = Object.FindObjectsOfType(); foreach (GrabbableObject val in array) { val.isInShipRoom = true; } } private void Update() { if (inProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { inProgress = false; Log.ErrorSound(); } } private void Configure(ChatArgs args) { if (args.Empty || args.Length == 1) { Log.Chat("Usage: /sort ", "FFFF00"); Log.NeutralSound(); } else if (args[0] == "skip") { Skip(args[1]); } else if (args[0] == "unskip") { Unskip(args[1]); } } private void Skip(string name) { if (skippedItems.Value.ContainsItem(name)) { Log.Chat("Item " + name + " already skipped", "FFFF00"); Log.NeutralSound(); } else { skippedItems.Value = skippedItems.Value.AddToList(name); Log.Chat("Skipping " + name, "FFFF00"); Log.ConfirmSound(); } } private void Unskip(string name) { if (!skippedItems.Value.ContainsItem(name)) { Log.Chat("Item " + name + " not skipped", "FFFF00"); Log.NeutralSound(); } else { skippedItems.Value = skippedItems.Value.RemoveFromList(name); Log.Chat("Unskipping " + name, "FFFF00"); Log.ConfirmSound(); } } private void Organize(ChatArgs args) { if (args[0] == "skip" || args[0] == "unskip") { Configure(args); return; } if (!CanSort) { Log.NotifyPlayer("Sorter Error", "Must be in orbit or stationary at company", isWarning: true); return; } if (inProgress) { Log.NotifyPlayer("Sorter Error", "Operation in progress", isWarning: true); return; } CategorizeItems(); Log.ConfirmSound(); if (args[0] == "help") { Log.Chat(args.help, "FFFF00"); return; } if (args[0] == "fixed" || args[0] == "compact") { fixedLayout.Value = args[0] == "fixed"; if (fixedLayout.Value) { Log.Chat("Fixed layout enabled", "FFFF00"); } else { Log.Chat("Compact layout enabled", "FFFF00"); } return; } force = args["r"] || args["redo"]; bool flag = args[0] == null || args[0] == "scrap"; bool flag2 = args[0] == null || args[0] == "cupboard"; bool flag3 = args[0] == null || args[0] == "circle"; bool flag4 = args[0] == null || args[0] == "doorframe"; Log.Debug($"Organizing - Scrap: {flag}, Cupboard: {flag2}, Circle: {flag3}, Doorframe: {flag4}"); ((MonoBehaviour)this).StartCoroutine(Organize(flag, flag2, flag3, flag4)); } private IEnumerator Organize(bool scrap, bool cupboard, bool circle, bool doorframe) { inProgress = true; Log.Chat("Press [Escape] to cancel sorting", "FFFF00"); if (scrap && inProgress) { yield return Scrap(); } if (doorframe && inProgress) { yield return Doorframe(); } if (circle && inProgress) { yield return Circle(); } if (cupboard && inProgress) { yield return Cupboard(); } Log.NeutralSound(); inProgress = false; } private IEnumerator Scrap() { inProgress = true; Transform parent = StartOfRound.Instance.elevatorTransform; Vector3 start = Vector3.zero; Vector3 shipOffset = parent.TransformPoint(originPosition) - ((Component)parent).transform.position; if (Player.Local.FirstEmptyItemSlot((GrabbableObject)null) < 0) { Log.NotifyPlayer("Sorter Error", "Inventory full", isWarning: true); inProgress = false; yield break; } new Vector2(0.8f, 0.8f); Dictionary itemCounts = new Dictionary(); List propList = (fixedLayout.Value ? allScrap : scrap.Select((GrabbableObject i) => i.itemProperties).ToList()); Dictionary layout = CreateScrapLayout(propList); foreach (GrabbableObject item in scrap) { if (ShouldBreak(item)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; yield break; } if (!layout.ContainsKey(item.Name())) { Log.Debug("No position for " + item.Name()); continue; } itemCounts[item.Name()] = (itemCounts.ContainsKey(item.Name()) ? itemCounts[item.Name()] : 0); Vector2 offset = layout[item.Name()]; int gy = itemCounts[item.Name()] / 5; int gz = itemCounts[item.Name()] % 5; Vector3 position2 = start + new Vector3(offset.x, offset.y); position2 += new Vector3(0f, (float)gy * 0.05f, (float)gz * 0.05f); itemCounts[item.Name()]++; Vector3 worldPos = position2 + shipOffset + ((Component)parent).transform.position; if (!force && Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.25f) { continue; } yield return GrabbableRetry(item); if (!ShouldSkip(item)) { yield return Player.StartMovingObject(item, worldPos); int retry = 15; while (!Player.CanGrabObject(item) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } } } private IEnumerator Circle() { foreach (GrabbableObject item in summonCircle) { if (ShouldBreak(item)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; yield break; } if (!Player.CanGrabObject(item) || !item.isInShipRoom) { continue; } float p = 0.5f; if (summonCircle.Count > 1) { p = (float)summonCircle.IndexOf(item) / (float)summonCircle.Count; } float diameter = 1f; float x = Mathf.Cos(p * MathF.PI * 2f) * diameter; float z = Mathf.Sin(p * MathF.PI * 2f) * diameter; Vector3 duckPos = duckCenter + new Vector3(x, 0f, z); Log.Debug($"Putting duck in a row. {duckPos}"); Transform parent = StartOfRound.Instance.elevatorTransform; Vector3 worldPos = parent.TransformPoint(duckPos); if (force || !(Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.05f)) { yield return Player.StartMovingObject(item, worldPos); int retry = 15; while (!Player.CanGrabObject(item) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } } } private IEnumerator Doorframe() { Dictionary counts = CountGroups(doorframe); int seen = 0; string lastName = ""; foreach (GrabbableObject item in doorframe) { if (ShouldBreak(item)) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; yield break; } if (ShouldSkip(item)) { continue; } if (item.Name() != lastName) { seen = 0; item.Name(); } else { seen++; } lastName = item.Name(); float p = 0.5f; int count = counts[item.Name()]; if (count > 1) { p = (float)seen / (float)(count - 1); } Vector3 shelfPos = Vector3.Lerp(shelfMinPosition, shelfMaxPosition, p); Log.Debug($"Moving {((Object)item).name} to doorframe. {shelfPos}"); Transform parent = StartOfRound.Instance.elevatorTransform; Vector3 worldPos = parent.TransformPoint(shelfPos); if (force || !(Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.05f)) { yield return Player.StartMovingObject(item, worldPos); int retry = 15; while (!Player.CanGrabObject(item) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } } } public static Dictionary CountGroups(List items) { Dictionary dictionary = new Dictionary(); foreach (GrabbableObject item in items) { if (dictionary.ContainsKey(item.Name())) { dictionary[item.Name()]++; } else { dictionary[item.Name()] = 1; } } return dictionary; } private IEnumerator Cupboard(bool standalone = false) { if (standalone) { inProgress = true; Log.Chat("Storing items in cupboard"); } GameObject closet = GameObject.Find("Environment/HangarShip/StorageCloset"); if (StartOfRound.Instance.unlockablesList.unlockables[7].inStorage) { if (standalone) { Log.Chat("No cupboard found. Return it from storage from the terminal.", "FFFF00"); } yield break; } PlaceableObjectsSurface[] surfaces = closet.GetComponentsInChildren(); Array.Sort(surfaces, (PlaceableObjectsSurface a, PlaceableObjectsSurface b) => ((Component)b).transform.position.y.CompareTo(((Component)a).transform.position.y)); Log.Debug($"Found {surfaces.Length} shelves"); yield return PlaceOnShelf(cupboardT, surfaces[0], Vector3.up * 0.6f); yield return PlaceOnShelf(cupboardA, surfaces[0]); yield return PlaceOnShelf(cupboardB, surfaces[1]); yield return PlaceOnShelf(cupboardC, surfaces[2]); yield return PlaceOnShelf(cupboardD, surfaces[3]); if (standalone) { inProgress = false; } } private IEnumerator PlaceOnShelf(List items, PlaceableObjectsSurface shelf, Vector3 offset = default(Vector3)) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (items.Count == 0) { yield break; } Vector3 min = ((Component)shelf).transform.TransformPoint(Vector3.left * 0.6f) + offset; Vector3 max = ((Component)shelf).transform.TransformPoint(Vector3.right * 0.6f) + offset; int unique = items.Select((GrabbableObject i) => i.Name()).Distinct().Count(); Vector3 margin = (max - min) / (float)(unique + 1); Vector3 forward = ((Component)shelf).transform.TransformDirection(Vector3.forward); string lastName = ""; int x = 0; int y = 0; int z = 0; for (int j = 0; j < items.Count; j++) { GrabbableObject item = items[j]; if (ShouldBreak(items[j])) { Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true); inProgress = false; break; } if (item.Name() != lastName) { x++; y = 0; z = 0; } else { y++; if (y > 5) { y = 0; z++; } } lastName = item.Name(); Vector3 worldPos2 = min + margin * (float)x + Vector3.up * ((float)y * 0.05f) + forward * ((float)z * 0.05f); worldPos2 += ((Component)shelf).transform.TransformDirection(item.PlacementOffset()); if (force || !(Vector3.Distance(worldPos2, ((Component)item).transform.position) < 0.05f)) { Vector3 parentPos = ((Component)shelf.parentTo).transform.InverseTransformPoint(worldPos2); Vector3 rotation = new Vector3(0f, ((Component)shelf).transform.localEulerAngles.y - 45f, 0f); if (rotation.y < 0f) { rotation.y += 360f; } yield return Player.StartMovingObject(item, parentPos, shelf.parentTo); yield return GrabbableRetry(item); } } } private void Pile(ChatArgs args) { if (args[0] == "help") { Log.Chat(args.help, "FFFF00"); return; } if (!CanSort) { Log.NotifyPlayer("Sorter Error", "Must be in orbit or stationary at company", isWarning: true); return; } if (inProgress) { Log.NotifyPlayer("Sorter Error", "Operation in progress. Press escape to cancel.", isWarning: true); return; } ((MonoBehaviour)this).StartCoroutine(Pile(args[0])); Log.ConfirmSound(); } private IEnumerator Pile(string target) { inProgress = true; Log.Chat("Press Escape to cancel", "FFFF00"); if (target == null) { GrabbableObject first = Player.Local.currentlyHeldObjectServer; target = first.Name(); Player.Local.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true); } IEnumerable objs = ((!(target == "scrap")) ? (from item in Object.FindObjectsOfType() where item.Name() == target && item.isInShipRoom select item) : (from item in Object.FindObjectsOfType() where item.isInShipRoom && item.itemProperties.isScrap select item)); Log.Chat($"{target} x{objs.Count()}", "00FFFF"); foreach (GrabbableObject item2 in objs) { int retry = 10; while (!Player.CanGrabObject(item2) && retry > 0) { retry--; yield return (object)new WaitForSeconds(0.1f); } if (retry > 0) { yield return Player.StartGrabbingObject(item2); Player.Local.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true); if (!inProgress) { break; } } } inProgress = false; } private void CategorizeItems() { scrap = new List(); cupboardT = new List(); cupboardA = new List(); cupboardB = new List(); cupboardC = new List(); cupboardD = new List(); summonCircle = new List(); doorframe = new List(); allScrap = StartOfRound.Instance.allItemsList.itemsList.ToList(); GrabbableObject[] array = Object.FindObjectsOfType(); Array.Sort(array, (GrabbableObject a, GrabbableObject b) => a.scrapValue.CompareTo(b.scrapValue)); Array.Sort(array, delegate(GrabbableObject a, GrabbableObject b) { if (a.itemProperties.twoHanded != b.itemProperties.twoHanded) { return -a.itemProperties.twoHanded.CompareTo(b.itemProperties.twoHanded); } if (a.itemProperties.isDefensiveWeapon != b.itemProperties.isDefensiveWeapon) { return -a.itemProperties.isDefensiveWeapon.CompareTo(b.itemProperties.isDefensiveWeapon); } return (a.AvgValue() != b.AvgValue()) ? a.AvgValue().CompareTo(b.AvgValue()) : a.Name().CompareTo(b.Name()); }); GrabbableObject[] array2 = array; foreach (GrabbableObject val in array2) { if (!ShouldSkip(val)) { string t = val.Name(); if (skippedItems.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); } else if (summonCircleItems.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); summonCircle.Add(val); } else if (cupboardTop.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); cupboardT.Add(val); } else if (cupboardShelfA.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); cupboardA.Add(val); } else if (cupboardShelfB.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); cupboardB.Add(val); } else if (cupboardShelfC.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); cupboardC.Add(val); } else if (cupboardShelfD.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); cupboardD.Add(val); } else if (doorframeItems.Value.ContainsItem(t)) { allScrap.Remove(val.itemProperties); doorframe.Add(val); } else { scrap.Add(val); } } } } private IEnumerator GrabbableRetry(GrabbableObject item) { int retry = 15; while (!Player.CanGrabObject(item) && retry > 0) { yield return (object)new WaitForEndOfFrame(); retry--; } } private bool ShouldBreak(GrabbableObject item) { return !inProgress || !Ship.Stationary || Player.Local.beamOutParticle.isPlaying || Player.Local.beamUpParticle.isPlaying; } private bool ShouldSkip(GrabbableObject item) { if (!Player.CanGrabObject(item) || item.Name() == "body") { return true; } if (!item.isInShipRoom) { return true; } return false; } private Dictionary CreateScrapLayout(List items) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); IEnumerable source = (from i in items where i.isScrap orderby i.Name() orderby (float)(i.minValue + i.maxValue) / 2f select i).Distinct(); Item[] array = source.Where((Item i) => i.twoHanded).ToArray(); Item[] array2 = source.Where((Item i) => !i.twoHanded).ToArray(); for (int j = 0; j < array.Length; j++) { dictionary[array[j].Name()] = new Vector2((float)j * 0.7f, 3f); } Vector3 val = Vector2.op_Implicit(new Vector2(0.6f, 0.7f)); for (int k = 0; k < array2.Length; k++) { int num = k / 4; int num2 = k % 4; float num3 = ((num % 2 == 0) ? 0f : 0.5f); dictionary[array2[k].Name()] = new Vector2((float)num, (float)num2 + num3) * Vector2.op_Implicit(val); } return dictionary; } private void OnDestroy() { ChatCommand.Commands.Clear(); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "kylethescientist.scrapmagic"; public const string PLUGIN_NAME = "ScrapMagic"; public const string PLUGIN_VERSION = "1.0.3"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }