Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Scrap Magic v1.0.4
kylethescientist.scrapmagic.dll
Decompiled a month agousing 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<T> : Argument { public T value; public Argument(string name, T value) { base.name = name; this.value = value; } public static implicit operator T(Argument<T> arg) { return arg.value; } } public struct ChatArgs { public Argument[] arguments; public string help; public int Length => arguments.Length; public bool Empty => arguments.Length == 0; public bool this[string name] => Get<bool>(name); public string this[int name] => Get<string>(name); public T Get<T>(string name) { Argument[] array = arguments; foreach (Argument argument in array) { if (argument.name == name) { return ((Argument<T>)argument).value; } } return default(T); } public T Get<T>(int name) { Argument[] array = arguments; foreach (Argument argument in array) { if (argument.name == name.ToString()) { return ((Argument<T>)argument).value; } } return default(T); } } public class ChatCommand { public static List<ChatCommand> Commands = new List<ChatCommand>(); public string keyword; public Action<ChatArgs> action; public string help; public ChatCommand(string keyword, string help, Action<ChatArgs> action) { this.keyword = keyword; this.help = help; this.action = action; } public static ChatCommand New(string keyword, string help, Action<ChatArgs> action) { ChatCommand chatCommand = new ChatCommand(keyword, help, action); Commands.Add(chatCommand); return chatCommand; } public ChatArgs GetArgs(string raw) { List<Argument> list = new List<Argument>(); string[] array = raw.Split(' '); keyword = array[0]; for (int i = 1; i < array.Length; i++) { if (array[i].StartsWith("-") && array[i].Length > 1) { string name = array[i].Substring(1); list.Add(new Argument<bool>(name, value: true)); } else { list.Add(new Argument<string>((i - 1).ToString(), array[i])); } } ChatArgs result = default(ChatArgs); result.arguments = list.ToArray(); result.help = help; return result; } public override string ToString() { return keyword; } } public static class Extensions { private static Dictionary<string, Vector3> itemOffsets = new Dictionary<string, Vector3> { { "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<string> 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<string> 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<string> 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<GrabbableObject>(); } } 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("<color=#" + color + ">[SM] " + body + "</color>", "", -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<Sorter>(); scrapMagic.AddComponent<Seller>(); } [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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGenerator) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_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<CodeInstruction> list = new List<CodeInstruction>(instructions); CodeMatcher val = new CodeMatcher((IEnumerable<CodeInstruction>)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<GrabbableObject> 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<StunGrenadeItem>()); 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<string> skippedItems; public static DepositItemsDesk desk; private InteractTrigger bell; private List<GrabbableObject> toSell; public static bool InProgress { get; private set; } private void FindDesk() { if (!Object.op_Implicit((Object)(object)desk)) { desk = Object.FindObjectOfType<DepositItemsDesk>(); } } private void Awake() { Log.Info("Initializing Seller"); skippedItems = Plugin.config.Bind<string>("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<InteractTrigger>() : 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<StunGrenadeItem>()); desk.AddObjectToDeskServerRpc(NetworkObjectReference.op_Implicit(((Component)item).gameObject.GetComponent<NetworkObject>())); 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<StunGrenadeItem>()); 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<GrabbableObject>(); 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>(); GiftBoxItem[] array = boxes; foreach (GiftBoxItem box in array) { if (!box.hasUsedGift && Object.op_Implicit((Object)(object)((Component)box).GetComponent<Renderer>())) { yield return Player.StartGrabbingObject((GrabbableObject)(object)box); ((GrabbableObject)box).ItemActivate(true, true); yield return (object)new WaitForSeconds(0.2f); } } List<GrabbableObject> sellableItems = new List<GrabbableObject>(); GrabbableObject[] onCounter = desk.ItemsOnCounter(); GrabbableObject[] array2 = Object.FindObjectsOfType<GrabbableObject>(); foreach (GrabbableObject obj in array2) { if (obj.itemProperties.isScrap && !obj.isHeld && !obj.deactivated && !((IEnumerable<GrabbableObject>)(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<int> 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<bool>)(() => 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<int> 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<int>(); } 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<int> list = new List<int>(); 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<string> doorframeItems; public ConfigEntry<string> summonCircleItems; public ConfigEntry<string> skippedItems; public ConfigEntry<string> cupboardTop; public ConfigEntry<string> cupboardShelfA; public ConfigEntry<string> cupboardShelfB; public ConfigEntry<string> cupboardShelfC; public ConfigEntry<string> cupboardShelfD; public ConfigEntry<bool> fixedLayout; private List<Item> allScrap; private List<GrabbableObject> scrap; private List<GrabbableObject> cupboardT; private List<GrabbableObject> cupboardA; private List<GrabbableObject> cupboardB; private List<GrabbableObject> cupboardC; private List<GrabbableObject> cupboardD; private List<GrabbableObject> summonCircle; private List<GrabbableObject> 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<bool>("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<string>("Sorter", "skippedItems", "body, clipboard, sticky_note, boombox", "Which items should be skipped when organizing"); doorframeItems = Plugin.config.Bind<string>("Sorter", "doorframeItems", "apparatus, soccer_ball, whoopie_cushion", "Which items should be put on the doorframe"); summonCircleItems = Plugin.config.Bind<string>("Sorter", "summonCircleItems", "", "Which items should be put in the summoning circle"); cupboardTop = Plugin.config.Bind<string>("Sorter", "cupboardTop", "extension_ladder, radar_booster, jetpack", "Which items should be put on top of the cupboard"); cupboardShelfA = Plugin.config.Bind<string>("Sorter", "cupboardShelfA", "kitchen_knife, shotgun, shovel", "Which items should be put on the cupboard's 1st shelf"); cupboardShelfB = Plugin.config.Bind<string>("Sorter", "cupboardShelfB", "key, ammo, flashlight, pro_flashlight", "Which items should be put on the cupboard's 2st shelf"); cupboardShelfC = Plugin.config.Bind<string>("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<string>("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 <scrap|cupboard|circle|doorframe|fixed|compact|skip|unskip> [-r|-redo] [help]", Organize); ChatCommand.New("pile", "Usage: /pile <item_name>", Pile); } private void Start() { GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); 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 <skip|unskip> <item_name>", "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<string, int> itemCounts = new Dictionary<string, int>(); List<Item> propList = (fixedLayout.Value ? allScrap : scrap.Select((GrabbableObject i) => i.itemProperties).ToList()); Dictionary<string, Vector2> 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<string, int> 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<string, int> CountGroups(List<GrabbableObject> items) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); 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<PlaceableObjectsSurface>(); 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<GrabbableObject> 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<GrabbableObject> objs = ((!(target == "scrap")) ? (from item in Object.FindObjectsOfType<GrabbableObject>() where item.Name() == target && item.isInShipRoom select item) : (from item in Object.FindObjectsOfType<GrabbableObject>() 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<GrabbableObject>(); cupboardT = new List<GrabbableObject>(); cupboardA = new List<GrabbableObject>(); cupboardB = new List<GrabbableObject>(); cupboardC = new List<GrabbableObject>(); cupboardD = new List<GrabbableObject>(); summonCircle = new List<GrabbableObject>(); doorframe = new List<GrabbableObject>(); allScrap = StartOfRound.Instance.allItemsList.itemsList.ToList(); GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); 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<string, Vector2> CreateScrapLayout(List<Item> 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<string, Vector2> dictionary = new Dictionary<string, Vector2>(); IEnumerable<Item> 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) { } } }