Please disclose if your mod was created primarily 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 MergeScrap v1.1.1
MergeScrap.dll
Decompiled an hour agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using GameNetcodeStuff; using HarmonyLib; using InteractiveTerminalAPI.UI; using InteractiveTerminalAPI.UI.Application; using InteractiveTerminalAPI.UI.Cursor; using InteractiveTerminalAPI.UI.Screen; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MergeScrap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("MergeScrap")] [assembly: AssemblyTitle("MergeScrap")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MergeScrap { internal static class TokMerge { internal static bool LooksMerge(string[] parts) { if (parts.Length == 0) { return false; } List<string> list = new List<string>(parts.Length); for (int i = 0; i < parts.Length; i++) { list.Add(parts[i]); } while (list.Count > 0) { if (!int.TryParse(list[list.Count - 1], out var _)) { break; } list.RemoveAt(list.Count - 1); } if (list.Count > 0) { return VerbOk(list); } return false; } internal static bool VerbOk(List<string> words) { if (words.Count == 1) { string text = words[0]; if (text.Equals("merge", StringComparison.Ordinal)) { return true; } if (text.Equals("merge-scrap", StringComparison.Ordinal)) { return true; } if (text.Equals("scrap-merge", StringComparison.Ordinal)) { return true; } } if (words.Count == 2 && words[0].Equals("merge", StringComparison.Ordinal) && words[1].Equals("scrap", StringComparison.Ordinal)) { return true; } return false; } } internal static class DoneGroups { private static readonly HashSet<string> completed = new HashSet<string>(StringComparer.Ordinal); internal static void Wipe() { completed.Clear(); } internal static void MarkGroupMerged(string groupKey) { if (!string.IsNullOrEmpty(groupKey)) { completed.Add(groupKey); } } internal static bool IsGroupMerged(string groupKey) { if (string.IsNullOrEmpty(groupKey)) { return false; } return completed.Contains(groupKey); } } internal static class EnterOnce { private static int lastMergeConfirmFrame = -1; internal static bool AlreadyHandledThisFrame() { return Time.frameCount == lastMergeConfirmFrame; } internal static void MarkHandledThisFrame() { lastMergeConfirmFrame = Time.frameCount; } } internal static class EnterOk { internal static bool WantsConfirm(object terminal) { if (TerminalReflection.GetTextAdded(terminal) <= 0) { return true; } if (MergeMenu.UserHasNavigatedSinceOpen) { if (!TerminalReflection.TryGetInputChunk(terminal, out string chunk)) { return true; } string text = NormCmd.Strip(chunk).Trim(); if (TokMerge.LooksMerge(text.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries))) { return true; } if (text.Length == 0) { return true; } return false; } if (!TerminalReflection.TryGetInputChunk(terminal, out string chunk2)) { return false; } TokMerge.LooksMerge(NormCmd.Strip(chunk2).Trim().Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries)); return false; } } internal static class ShutHudChat { private static bool snap; private static float savedTargetAlpha; private static float savedCgAlpha; private static bool savedBlocksRaycasts; private static bool savedInteractable; internal static void GoDark() { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { return; } if ((Object)(object)instance.Chat?.canvasGroup != (Object)null) { if (!snap) { savedTargetAlpha = instance.Chat.targetAlpha; savedCgAlpha = instance.Chat.canvasGroup.alpha; savedBlocksRaycasts = instance.Chat.canvasGroup.blocksRaycasts; savedInteractable = instance.Chat.canvasGroup.interactable; snap = true; } if (instance.Chat.fadeCoroutine != null) { ((MonoBehaviour)instance).StopCoroutine(instance.Chat.fadeCoroutine); instance.Chat.fadeCoroutine = null; } instance.Chat.targetAlpha = 0f; instance.Chat.canvasGroup.alpha = 0f; instance.Chat.canvasGroup.blocksRaycasts = false; instance.Chat.canvasGroup.interactable = false; } if (!((Object)(object)instance.chatTextField == (Object)null)) { ((Selectable)instance.chatTextField).interactable = false; instance.chatTextField.readOnly = true; instance.chatTextField.text = string.Empty; instance.chatTextField.DeactivateInputField(false); if ((Object)(object)instance.chatText != (Object)null) { ((Behaviour)instance.chatText).enabled = false; } if ((Object)(object)instance.typingIndicator != (Object)null) { ((Behaviour)instance.typingIndicator).enabled = false; } if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) { GameNetworkManager.Instance.localPlayerController.isTypingChat = false; } } } internal static void UnDim() { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance?.Chat?.canvasGroup != (Object)null && snap) { instance.Chat.targetAlpha = savedTargetAlpha; instance.Chat.canvasGroup.alpha = savedCgAlpha; instance.Chat.canvasGroup.blocksRaycasts = savedBlocksRaycasts; instance.Chat.canvasGroup.interactable = savedInteractable; } snap = false; if (!((Object)(object)instance?.chatTextField == (Object)null)) { instance.chatTextField.readOnly = false; ((Selectable)instance.chatTextField).interactable = true; if ((Object)(object)instance.chatText != (Object)null) { ((Behaviour)instance.chatText).enabled = true; } } } } internal static class LateChatNuke { internal static void Tick() { if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null) { return; } HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { return; } if ((Object)(object)instance.Chat?.canvasGroup != (Object)null) { instance.Chat.targetAlpha = 0f; instance.Chat.canvasGroup.alpha = 0f; instance.Chat.canvasGroup.blocksRaycasts = false; instance.Chat.canvasGroup.interactable = false; } if ((Object)(object)instance.chatText != (Object)null) { ((Behaviour)instance.chatText).enabled = false; } if ((Object)(object)instance.typingIndicator != (Object)null) { ((Behaviour)instance.typingIndicator).enabled = false; } if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) { GameNetworkManager.Instance.localPlayerController.isTypingChat = false; } TMP_InputField chatTextField = instance.chatTextField; if ((Object)(object)chatTextField == (Object)null) { return; } if (chatTextField.text.Length > 0) { chatTextField.text = string.Empty; chatTextField.stringPosition = 0; } EventSystem current = EventSystem.current; if ((Object)(object)current == (Object)null) { return; } GameObject currentSelectedGameObject = current.currentSelectedGameObject; Transform transform = ((Component)chatTextField).transform; if ((Object)(object)currentSelectedGameObject != (Object)null && ((Object)(object)currentSelectedGameObject.transform == (Object)(object)transform || currentSelectedGameObject.transform.IsChildOf(transform))) { chatTextField.DeactivateInputField(false); current.SetSelectedGameObject((GameObject)null); if (MergeMenu.BoundTerminal != null) { TerminalReflection.LockTermField(MergeMenu.BoundTerminal, on: true); } } } } internal static class MergeMenu { private static TerminalNode? pendingNode; private static object? pendingTerm; private static List<ScrapPile>? rows; internal static bool IsActive { get; private set; } internal static object? BoundTerminal { get; private set; } internal static int SelectedIndex { get; private set; } internal static bool OpenedViaItapi { get; private set; } internal static bool UserHasNavigatedSinceOpen { get; private set; } internal static List<ScrapPile>? GetRowsSnapshot() { return rows; } internal static void Open(object term, List<ScrapPile> rowList) { Arm(term, rowList, itapi: false); } internal static void OpenForItapi(object term, List<ScrapPile> rowList) { Arm(term, rowList, itapi: true); } private static void Arm(object term, List<ScrapPile> rowList, bool itapi) { SelectedIndex = 0; UserHasNavigatedSinceOpen = false; if (rowList == null || rowList.Count == 0) { IsActive = false; BoundTerminal = null; rows = null; OpenedViaItapi = false; return; } OpenedViaItapi = itapi; BoundTerminal = term; rows = rowList; IsActive = true; if (!itapi) { RefreshDisplay(term); TerminalReflection.LockTermField(term, on: true); } ShutHudChat.GoDark(); } internal static void SetSelectedIndex(int i) { if (rows != null && rows.Count > 0) { SelectedIndex = Mathf.Clamp(i, 0, rows.Count - 1); } } internal static void SetPendingFinalNodeAfterItapi(object term, TerminalNode node) { pendingTerm = term; pendingNode = node; } internal static void ApplyPendingNodeAfterItapiDestroyed() { if (pendingTerm != null && (Object)(object)pendingNode != (Object)null) { TerminalReflection.LoadNewNode(pendingTerm, pendingNode); } pendingTerm = null; pendingNode = null; } internal static void Close() { if (IsActive || BoundTerminal != null || OpenedViaItapi) { ShutHudChat.UnDim(); if (BoundTerminal != null) { TerminalReflection.LockTermField(BoundTerminal, on: false); } IsActive = false; BoundTerminal = null; rows = null; SelectedIndex = 0; UserHasNavigatedSinceOpen = false; OpenedViaItapi = false; } } private static List<ScrapPile> RebuildFiltered() { List<ScrapPile> list = GrabPiles.ListForMenu(); List<ScrapPile> list2 = new List<ScrapPile>(); for (int i = 0; i < list.Count; i++) { ScrapPile scrapPile = list[i]; if (!DoneGroups.IsGroupMerged(scrapPile.GroupKey)) { list2.Add(scrapPile); } } return list2; } internal static void RefreshDisplay(object term) { if (IsActive && term == BoundTerminal && rows != null) { TerminalReflection.SetScreenBody(term, "\n\n\n" + GrabPiles.FormatMenu(rows, SelectedIndex)); } } internal static bool TryConfirmSelection(out TerminalNode node) { node = null; if (!IsActive || BoundTerminal == null || rows == null || rows.Count == 0) { return false; } SelectedIndex = Mathf.Clamp(SelectedIndex, 0, rows.Count - 1); ScrapPile scrapPile = rows[SelectedIndex]; string displayName = scrapPile.DisplayName; int totalValue = scrapPile.TotalValue; if (!SquashGroup.TryMergeRow(scrapPile, out string error, out int destroyed)) { rows = RebuildFiltered(); SelectedIndex = Mathf.Clamp(SelectedIndex, 0, Mathf.Max(0, rows.Count - 1)); node = ScreenNode.Make(error + "\n\n" + GrabPiles.FormatMenu(rows, SelectedIndex)); return true; } DoneGroups.MarkGroupMerged(scrapPile.GroupKey); Plugin.PlayMergeSound(); rows = RebuildFiltered(); SelectedIndex = Mathf.Clamp(SelectedIndex, 0, Mathf.Max(0, rows.Count - 1)); string text = displayName + " merged " + destroyed + " stack $" + totalValue + " item in ship middle\n\n"; if (rows.Count == 0) { Close(); node = ScreenNode.Make(text + "nothing left\n\n"); return true; } UserHasNavigatedSinceOpen = true; node = ScreenNode.Make(text + GrabPiles.FormatMenu(rows, SelectedIndex)); return true; } internal static void MoveSelection(int delta, object term) { if (rows != null && rows.Count != 0) { int count = rows.Count; SelectedIndex = ((Mathf.Clamp(SelectedIndex, 0, count - 1) + delta) % count + count) % count; UserHasNavigatedSinceOpen = true; RefreshDisplay(term); } } } internal enum PileSpot { Ship, Vehicle, Facility } internal sealed class ScrapPile { public string GroupKey { get; set; } = ""; public string DisplayName { get; set; } = ""; public List<GrabbableObject> PhysicalItems { get; } = new List<GrabbableObject>(); public int InventoryCount { get; set; } public int InventoryValueSum { get; set; } public int PhysicalCount => PhysicalItems.Count; public int TotalPieces => PhysicalCount + InventoryCount; public int PhysicalValueSum { get { int num = 0; for (int i = 0; i < PhysicalItems.Count; i++) { GrabbableObject val = PhysicalItems[i]; if ((Object)(object)val != (Object)null) { num += val.scrapValue; } } return num; } } public int TotalValue => PhysicalValueSum + InventoryValueSum; public int CountAt(PileSpot place) { int num = 0; for (int i = 0; i < PhysicalItems.Count; i++) { GrabbableObject val = PhysicalItems[i]; if ((Object)(object)val != (Object)null && GrabPiles.PlaceOf(val) == place) { num++; } } return num; } public string FormatLocationCounts() { List<string> list = new List<string>(); int num = CountAt(PileSpot.Ship); int num2 = CountAt(PileSpot.Facility); int num3 = CountAt(PileSpot.Vehicle); if (num > 0) { list.Add($"Ship:{num}"); } if (num2 > 0) { list.Add($"Floor:{num2}"); } if (num3 > 0) { list.Add($"Cruiser:{num3}"); } if (InventoryCount > 0) { list.Add($"Inv:{InventoryCount}"); } if (list.Count <= 0) { return ""; } return " [" + string.Join(" ", list) + "]"; } } internal static class GrabPiles { internal static string PrefabKeyOf(GrabbableObject g) { return ((Object)g).name.Replace("(Clone)", "", StringComparison.Ordinal); } internal static PileSpot PlaceOf(GrabbableObject g) { StartOfRound instance = StartOfRound.Instance; object obj; if (instance == null) { obj = null; } else { VehicleController attachedVehicle = instance.attachedVehicle; obj = ((attachedVehicle != null) ? ((Component)attachedVehicle).transform : null); } Transform val = (Transform)obj; if ((Object)(object)val != (Object)null) { Transform val2 = ((Component)g).transform; while ((Object)(object)val2 != (Object)null) { if ((Object)(object)val2 == (Object)(object)val) { return PileSpot.Vehicle; } val2 = val2.parent; } } if (!g.isInShipRoom) { return PileSpot.Facility; } return PileSpot.Ship; } internal static List<GrabbableObject> CollectPhysicalScrap() { List<GrabbableObject> list = new List<GrabbableObject>(); GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0); foreach (GrabbableObject val in array) { if (Plugin.ScrapListable(val)) { list.Add(val); } } return list; } internal static List<ScrapPile> BuildRows() { Dictionary<string, ScrapPile> dictionary = new Dictionary<string, ScrapPile>(StringComparer.Ordinal); foreach (GrabbableObject item in CollectPhysicalScrap()) { string text = PrefabKeyOf(item); if (!string.IsNullOrEmpty(text)) { if (!dictionary.TryGetValue(text, out var value)) { ScrapPile obj = new ScrapPile { GroupKey = text, DisplayName = (item.itemProperties?.itemName ?? text) }; value = obj; dictionary[text] = obj; } value.PhysicalItems.Add(item); } } foreach (var (text2, displayName, num) in ShipInventoryMerge.EnumerateInventoryScrapRows()) { if (!string.IsNullOrEmpty(text2)) { if (!dictionary.TryGetValue(text2, out var value2)) { ScrapPile obj2 = new ScrapPile { GroupKey = text2, DisplayName = displayName }; value2 = obj2; dictionary[text2] = obj2; } value2.InventoryCount++; value2.InventoryValueSum += num; } } List<ScrapPile> list = new List<ScrapPile>(dictionary.Values); list.Sort(ComparePilesForDisplay); return list; } private static int ComparePilesForDisplay(ScrapPile a, ScrapPile b) { int num = b.TotalPieces.CompareTo(a.TotalPieces); if (num == 0) { return b.TotalValue.CompareTo(a.TotalValue); } return num; } internal static List<ScrapPile> ListForMenu() { List<ScrapPile> list = BuildRows(); List<ScrapPile> list2 = new List<ScrapPile>(); for (int i = 0; i < list.Count; i++) { if (list[i].TotalPieces >= 2) { list2.Add(list[i]); } } return list2; } internal static string FormatMenu(IReadOnlyList<ScrapPile> rows, int sel = -1) { if (rows.Count == 0) { return "empty\n\n"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("arrows, enter"); stringBuilder.AppendLine(); for (int i = 0; i < rows.Count; i++) { ScrapPile scrapPile = rows[i]; stringBuilder.AppendLine(string.Format("{0}{1}. {2} ×{3} → ${4}{5}", (sel == i) ? "* " : " ", i + 1, scrapPile.DisplayName, scrapPile.TotalPieces, scrapPile.TotalValue, scrapPile.FormatLocationCounts())); } return stringBuilder.ToString(); } } internal static class SquashGroup { private const BindingFlags PublicInstance = BindingFlags.Instance | BindingFlags.Public; private const float ShipJitter = 0.15f; private static int GrabbableLocationSortRank(GrabbableObject g) { return GrabPiles.PlaceOf(g) switch { PileSpot.Ship => 2, PileSpot.Facility => 1, _ => 0, }; } internal static bool TryMergeRow(ScrapPile row, out string error, out int destroyed) { error = ""; destroyed = 0; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { error = "no player"; return false; } if (!((NetworkBehaviour)val).IsHost) { error = "host only"; return false; } List<GrabbableObject> list = new List<GrabbableObject>(); for (int i = 0; i < row.PhysicalItems.Count; i++) { GrabbableObject val2 = row.PhysicalItems[i]; if ((Object)(object)val2 != (Object)null && !val2.isHeld) { list.Add(val2); } } List<object> list2 = ShipInventoryMerge.CollectInventoryEntriesMatchingGroup(row.GroupKey); if (list.Count + list2.Count < 2) { error = "need 2+"; return false; } int num = ShipInventoryMerge.SumInventoryScrapValues(list2); object obj = ((list2.Count > 0) ? list2[0] : null); List<GrabbableObject> list3; if (list.Count >= 2) { list3 = new List<GrabbableObject>(list); list3.Sort(delegate(GrabbableObject a, GrabbableObject b) { int value = GrabbableLocationSortRank(a); int num4 = GrabbableLocationSortRank(b).CompareTo(value); return (num4 == 0) ? a.scrapValue.CompareTo(b.scrapValue) : num4; }); } else { list3 = list; } GrabbableObject val3 = ((list3.Count > 0) ? list3[0] : null); int num2 = 0; for (int j = 0; j < list3.Count; j++) { num2 += list3[j].scrapValue; } int num3 = (((Object)(object)val3 != (Object)null && list3.Count >= 2) ? (num2 + num) : ((!((Object)(object)val3 != (Object)null)) ? num : (val3.scrapValue + num))); if (list2.Count > 0 && !ShipInventoryMerge.TryRemoveInventoryEntries(list2, out error)) { return false; } destroyed = list.Count + list2.Count - 1; if ((Object)(object)val3 != (Object)null) { for (int k = 1; k < list3.Count; k++) { Object.Destroy((Object)(object)((Component)list3[k]).gameObject); } val3.SetScrapValue(num3); PlaceAtShipCenter(val3); return true; } if (obj == null || !TrySpawnMergedFromInv(obj, num3, out error)) { if (list2.Count > 0) { ShipInventoryMerge.TryAddInventoryEntries(list2, out string _, verifyCount: false); } return false; } return true; } private static void PlaceAtShipCenter(GrabbableObject g) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null)) { GameObject obj = GameObject.Find("/Environment/HangarShip"); Transform val = ((obj != null) ? obj.transform : null) ?? instance.elevatorTransform; Transform[] insideShipPositions = instance.insideShipPositions; Vector3 val2 = ((insideShipPositions != null && insideShipPositions.Length > 10 && (Object)(object)insideShipPositions[10] != (Object)null) ? (insideShipPositions[10].position + new Vector3(0f, 0f, 1.5f)) : (((Object)(object)instance.elevatorTransform != (Object)null) ? instance.elevatorTransform.position : ((Component)g).transform.position)); val2.x += Random.Range(-0.15f, 0.15f); val2.z += Random.Range(-0.15f, 0.15f); g.parentObject = null; ((Component)g).transform.SetParent(val, true); ((Component)g).transform.position = val2; g.isInShipRoom = (g.isInElevator = true); g.EnablePhysics(true); g.EnableItemMeshes(true); g.fallTime = 0f; g.hasHitGround = true; g.floorYRot = -1; ((Component)g).transform.rotation = Quaternion.Euler(g.itemProperties.restingRotation); g.targetFloorPosition = val.InverseTransformPoint(val2); ((Component)g).transform.localPosition = g.targetFloorPosition; g.startFallingPosition = g.targetFloorPosition; } } private static object? GetPropertyOrField(Type declaringType, object instance, string memberName) { PropertyInfo property = declaringType.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public); if (property != null) { return property.GetValue(instance); } return declaringType.GetField(memberName, BindingFlags.Instance | BindingFlags.Public)?.GetValue(instance); } private static bool TrySpawnMergedFromInv(object data, int scrap, out string err) { err = ""; if (!ShipInventoryMerge.EnsureItemDataApiReady()) { err = "no ShipInv API"; return false; } object obj = ShipInventoryMerge.ResolveGameItemFromItemDataBoxed(data); if (obj == null) { err = "bad inv row"; return false; } Type type = obj.GetType(); object? propertyOrField = GetPropertyOrField(type, obj, "spawnPrefab"); GameObject val = (GameObject)((propertyOrField is GameObject) ? propertyOrField : null); if (val == null) { err = "no prefab"; return false; } Type type2 = data.GetType(); object propertyOrField2 = GetPropertyOrField(type, obj, "saveItemVariable"); bool flag = default(bool); int num; if (propertyOrField2 is bool) { flag = (bool)propertyOrField2; num = 1; } else { num = 0; } bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0; propertyOrField2 = GetPropertyOrField(type2, data, "PERSISTED_THROUGH_ROUNDS"); bool flag3 = default(bool); int num2; if (propertyOrField2 is bool) { flag3 = (bool)propertyOrField2; num2 = 1; } else { num2 = 0; } bool scrapPersistedThroughRounds = (byte)((uint)num2 & (flag3 ? 1u : 0u)) != 0; int num4 = ((GetPropertyOrField(type2, data, "SAVE_DATA") is int num3) ? num3 : 0); GameObject val2 = Object.Instantiate<GameObject>(val); NetworkObject componentInChildren = default(NetworkObject); if (!val2.TryGetComponent<NetworkObject>(ref componentInChildren)) { componentInChildren = val2.GetComponentInChildren<NetworkObject>(true); } GrabbableObject componentInChildren2 = default(GrabbableObject); if (!val2.TryGetComponent<GrabbableObject>(ref componentInChildren2)) { componentInChildren2 = val2.GetComponentInChildren<GrabbableObject>(true); } if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { Object.Destroy((Object)(object)val2); err = "spawn busted"; return false; } try { componentInChildren.Spawn(true); } catch (Exception ex) { Object.Destroy((Object)(object)val2); err = ex.InnerException?.Message ?? ex.Message; return false; } componentInChildren2.scrapPersistedThroughRounds = scrapPersistedThroughRounds; if (flag2) { componentInChildren2.LoadItemSaveData(num4); } componentInChildren2.SetScrapValue(scrap); PlaceAtShipCenter(componentInChildren2); return true; } } [HarmonyPatch] internal static class MergeHooks_TerminalParse { private static readonly Lazy<MethodInfo> Target = new Lazy<MethodInfo>(() => AccessTools.Method(TerminalType.Value, "ParsePlayerSentence", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no ParsePlayerSentence")); private static MethodBase TargetMethod() { return Target.Value; } [HarmonyPriority(800)] private static bool Prefix(object __instance, ref TerminalNode __result) { if (!TerminalReflection.TryGetInputChunk(__instance, out string chunk)) { return true; } chunk = NormCmd.Strip(chunk); string[] array = chunk.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries); if (MergeMenu.IsActive && array.Length != 0 && !TokMerge.LooksMerge(array)) { MergeMenu.Close(); } if (!TokMerge.LooksMerge(array)) { return true; } List<string> list = new List<string>(array.Length); for (int i = 0; i < array.Length; i++) { list.Add(array[i]); } while (list.Count > 0) { if (!int.TryParse(list[list.Count - 1], out var _)) { break; } list.RemoveAt(list.Count - 1); } if (!TokMerge.VerbOk(list)) { return true; } DoneGroups.Wipe(); List<ScrapPile> list2 = GrabPiles.ListForMenu(); bool flag = InteractiveTerminalManager.ContainsApplication(TerminalReflection.GetRawInputSuffixForItapi(__instance).Trim()); if (list2.Count == 0) { __result = ScreenNode.Make("\n\n\n" + GrabPiles.FormatMenu(list2)); return false; } if (flag) { MergeMenu.OpenForItapi(__instance, list2); return true; } MergeMenu.Open(__instance, list2); string text = GrabPiles.FormatMenu(list2, MergeMenu.IsActive ? MergeMenu.SelectedIndex : (-1)); __result = ScreenNode.Make("\n\n\n" + text); return false; } } [HarmonyPatch] internal static class MergeHooks_TerminalSubmit { private static MethodBase TargetMethod() { return AccessTools.Method(TerminalType.Value, "OnSubmit", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no OnSubmit"); } [HarmonyPriority(800)] private static bool Prefix(object __instance) { if (!TerminalReflection.GetTerminalInUse(__instance)) { return true; } if (!MergeMenu.IsActive || MergeMenu.BoundTerminal != __instance) { return true; } if (MergeMenu.OpenedViaItapi) { return true; } if (!EnterOk.WantsConfirm(__instance)) { return true; } if (EnterOnce.AlreadyHandledThisFrame()) { return false; } if (!MergeMenu.TryConfirmSelection(out TerminalNode node) || (Object)(object)node == (Object)null) { return true; } TerminalReflection.LoadNewNode(__instance, node); EnterOnce.MarkHandledThisFrame(); if (!MergeMenu.IsActive) { TerminalReflection.ActivateInput(__instance); } return false; } } [HarmonyPatch] internal static class MergeHooks_TerminalQuit { private static MethodBase TargetMethod() { return AccessTools.Method(TerminalType.Value, "QuitTerminal", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no QuitTerminal"); } private static void Postfix() { MergeMenu.Close(); } } [HarmonyPatch] internal static class MergeHooks_AfterLoadNode { private static MethodBase TargetMethod() { return AccessTools.Method(TerminalType.Value, "LoadNewNode", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no LoadNewNode"); } private static void Postfix(object __instance) { if (MergeMenu.IsActive && MergeMenu.BoundTerminal != null && MergeMenu.BoundTerminal == __instance && !MergeMenu.OpenedViaItapi) { if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) { GameNetworkManager.Instance.localPlayerController.isTypingChat = false; } ShutHudChat.GoDark(); MergeMenu.RefreshDisplay(__instance); TerminalReflection.LockTermField(__instance, on: true); } } } [HarmonyPatch(typeof(TerminalApplication), "UpdateText")] internal static class MergeHooks_ItapiUpdateText { private static readonly FieldInfo? CurrentScreenField = AccessTools.Field(typeof(TerminalApplication), "currentScreen"); private static bool Prefix(TerminalApplication __instance) { if (CurrentScreenField == null) { return true; } return CurrentScreenField.GetValue(__instance) != null; } } [HarmonyPatch(typeof(InteractiveTerminalManager), "OnDestroy")] internal static class MergeHooks_ItapiDestroy { private static void Postfix() { MergeMenu.ApplyPendingNodeAfterItapiDestroyed(); MergeMenu.Close(); } } [HarmonyPatch] internal static class MergeHooks_TerminalUpdate { private static MethodBase TargetMethod() { return AccessTools.Method(TerminalType.Value, "Update", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no Terminal.Update"); } [HarmonyPostfix] [HarmonyPriority(int.MaxValue)] private static void Postfix(object __instance) { if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null || MergeMenu.BoundTerminal != __instance || MergeMenu.OpenedViaItapi) { return; } bool flag = false; bool flag2 = false; Keyboard current = Keyboard.current; if (current != null) { flag = ((ButtonControl)current.upArrowKey).wasPressedThisFrame; flag2 = ((ButtonControl)current.downArrowKey).wasPressedThisFrame; } if (!flag && !flag2) { flag = Input.GetKeyDown((KeyCode)273); flag2 = Input.GetKeyDown((KeyCode)274); } if (flag) { MergeMenu.MoveSelection(-1, __instance); } else if (flag2) { MergeMenu.MoveSelection(1, __instance); } bool flag3 = current != null && (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame); if (!flag3) { flag3 = Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271); } if (!flag3) { return; } Terminal val = (Terminal)((__instance is Terminal) ? __instance : null); if (val == null || EnterOnce.AlreadyHandledThisFrame()) { return; } TerminalNode node; if (!EnterOk.WantsConfirm(__instance)) { val.OnSubmit(); } else if (MergeMenu.TryConfirmSelection(out node) && (Object)(object)node != (Object)null) { TerminalReflection.LoadNewNode(__instance, node); EnterOnce.MarkHandledThisFrame(); if (!MergeMenu.IsActive) { TerminalReflection.ActivateInput(__instance); } } else { val.OnSubmit(); } } } [HarmonyPatch(typeof(TMP_InputField), "ProcessEvent")] [HarmonyPriority(800)] internal static class MergeHooks_ChatField { private static bool Prefix(TMP_InputField __instance, Event e) { if (!MergeMenu.IsActive) { return true; } TMP_InputField val = HUDManager.Instance?.chatTextField; if (val == null || __instance != val) { return true; } e.Use(); return false; } } [HarmonyPatch(typeof(TMP_InputField), "ProcessEvent")] [HarmonyPriority(800)] internal static class MergeHooks_TerminalArrows { private static bool Prefix(TMP_InputField __instance, Event e) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null) { return true; } if (TerminalReflection.GetScreenTextObject(MergeMenu.BoundTerminal) != __instance) { return true; } if ((int)e.type != 4) { return true; } KeyCode keyCode = e.keyCode; if (keyCode - 273 > 1) { return true; } e.Use(); return false; } } [HarmonyPatch] internal static class MergeHooks_HudEnableChat { private static MethodBase TargetMethod() { return AccessTools.DeclaredMethod(typeof(HUDManager), "EnableChat_performed", new Type[1] { typeof(CallbackContext) }, (Type[])null) ?? throw new InvalidOperationException("no EnableChat_performed"); } private static bool Prefix() { return !MergeMenu.IsActive; } } [HarmonyPatch] internal static class MergeHooks_HudSubmitChat { private static MethodBase TargetMethod() { return AccessTools.DeclaredMethod(typeof(HUDManager), "SubmitChat_performed", new Type[1] { typeof(CallbackContext) }, (Type[])null) ?? throw new InvalidOperationException("no SubmitChat_performed"); } private static bool Prefix() { return !MergeMenu.IsActive; } } [HarmonyPatch(typeof(HUDManager), "PingHUDElement")] internal static class MergeHooks_HudPing { private static bool Prefix(HUDElement element) { if (MergeMenu.IsActive && !((Object)(object)HUDManager.Instance == (Object)null)) { return HUDManager.Instance.Chat != element; } return true; } } [HarmonyPatch] internal static class MergeHooks_ChuteRpc { private static bool Prepare() { return (object)TargetMethod() != null; } private static MethodBase? TargetMethod() { Type type = AccessTools.TypeByName("ShipInventoryUpdated.Scripts.ChuteRetrieve"); if ((object)type == null) { return null; } MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "RetrieveItemsServerRpc")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsArray) { return methodInfo; } } } return null; } [HarmonyPrefix] private static bool Prefix() { return !ShipInventoryMergeSuppression.IsSuppressed; } } [BepInPlugin("mine.MergeScrap", "MergeScrap", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "mine.MergeScrap"; public const string PluginName = "MergeScrap"; public const string PluginVersion = "1.1.1"; public const int MergeSoundClipIndex = 0; private Harmony harmony; public static Plugin Instance { get; private set; } public static ConfigEntry<bool> SoundOnMerge { get; private set; } private void Awake() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Instance = this; SoundOnMerge = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SoundOnMerge", true, "merge sound"); InteractiveTerminalManager.RegisterApplication<ItapiPicker>(new string[4] { "merge", "merge scrap", "merge-scrap", "scrap-merge" }, false); harmony = new Harmony("mine9289.mergescrap"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"mine9289 / MergeScrap 1.1.1"); } private void LateUpdate() { LateChatNuke.Tick(); } private void OnDestroy() { MergeMenu.Close(); } internal static void PlayMergeSound() { if (!SoundOnMerge.Value) { return; } Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null) { return; } try { val.PlayTerminalAudioServerRpc(0); } catch { } } public static bool ScrapListable(GrabbableObject g) { if ((Object)(object)g == (Object)null) { return false; } if ((Object)(object)g.itemProperties == (Object)null) { return false; } if (!g.itemProperties.isScrap) { return false; } if (g.isHeld) { return false; } if (!g.grabbable) { return false; } return true; } } public sealed class ItapiPicker : InteractiveTerminalApplication<CursorElement> { private string banner = "Pick an item"; public override void Initialization() { Rebuild(MergeMenu.GetRowsSnapshot() ?? new List<ScrapPile>()); } protected override Action PreviousScreen() { return delegate { PopItapi(); }; } private static void PopItapi() { InteractiveTerminalManager instance = InteractiveTerminalManager.Instance; if ((Object)(object)instance != (Object)null) { Object.Destroy((Object)(object)((Component)instance).gameObject); } } private void Rebuild(List<ScrapPile> rows) { if (rows.Count == 0) { CursorElement val = CursorElement.Create("close", "", (Action)delegate { PopItapi(); }, (Func<CursorElement, bool>)null, true); CursorMenu<CursorElement> val2 = (CursorMenu<CursorElement>)(object)(((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = (BaseCursorMenu<CursorElement>)(object)CursorMenu<CursorElement>.Create(0, '>', (CursorElement[])(object)new CursorElement[1] { val }, (Func<CursorElement, CursorElement, int>[])null)); ((TerminalApplication)this).currentScreen = (IScreen)(object)BoxedScreen.Create("merge", (ITextElement[])(object)new ITextElement[3] { (ITextElement)TextElement.Create("empty"), (ITextElement)TextElement.Create(" "), (ITextElement)val2 }); return; } CursorElement[] array = (CursorElement[])(object)new CursorElement[rows.Count]; for (int i = 0; i < rows.Count; i++) { ScrapPile scrapPile = rows[i]; string gk = scrapPile.GroupKey; array[i] = CursorElement.Create($"{scrapPile.DisplayName}{scrapPile.FormatLocationCounts()} ×{scrapPile.TotalPieces} → ${scrapPile.TotalValue}", "", (Action)delegate { OnPick(gk); }, (Func<CursorElement, bool>)null, true); } CursorMenu<CursorElement> val3 = (CursorMenu<CursorElement>)(object)(((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = (BaseCursorMenu<CursorElement>)(object)CursorMenu<CursorElement>.Create(0, '>', array, (Func<CursorElement, CursorElement, int>[])null)); ((TerminalApplication)this).currentScreen = (IScreen)(object)BoxedScreen.Create("merge", (ITextElement[])(object)new ITextElement[5] { (ITextElement)TextElement.Create(banner), (ITextElement)TextElement.Create(" "), (ITextElement)TextElement.Create("enter = merge / esc = cancel"), (ITextElement)TextElement.Create(" "), (ITextElement)val3 }); } private static string Truncate(string? t) { if (string.IsNullOrEmpty(t)) { return "—"; } string[] array = t.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string text = ((array.Length != 0) ? array[0] : t); if (text.Length <= 220) { return text; } return text.Substring(0, 217) + "..."; } private void OnPick(string groupKey) { string groupKey2 = groupKey; object boundTerminal = MergeMenu.BoundTerminal; int num = MergeMenu.GetRowsSnapshot()?.FindIndex((ScrapPile r) => r.GroupKey == groupKey2) ?? (-1); if (num < 0) { return; } MergeMenu.SetSelectedIndex(num); if (!MergeMenu.TryConfirmSelection(out TerminalNode node) || (Object)(object)node == (Object)null) { return; } if (!MergeMenu.IsActive) { if (boundTerminal != null) { MergeMenu.SetPendingFinalNodeAfterItapi(boundTerminal, node); } PopItapi(); } else { banner = Truncate(node.displayText); Rebuild(MergeMenu.GetRowsSnapshot() ?? new List<ScrapPile>()); } } } internal static class ShipInventoryMergeSuppression { private static int depth; internal static bool IsSuppressed => depth > 0; internal static void Enter() { depth++; } internal static void Exit() { if (depth > 0) { depth--; } } } internal static class ShipInventoryMerge { [CompilerGenerated] private sealed class <EnumerateInventoryScrapRows>d__23 : IEnumerable<(string groupKey, string displayName, int scrapValue)>, IEnumerable, IEnumerator<(string groupKey, string displayName, int scrapValue)>, IEnumerator, IDisposable { private int <>1__state; private (string groupKey, string displayName, int scrapValue) <>2__current; private int <>l__initialThreadId; private IEnumerator <>7__wrap1; (string, string, int) IEnumerator<(string, string, int)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateInventoryScrapRows>d__23(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; if (!EnsureLoaded()) { return false; } IEnumerable enumerable = InvItems(); if (enumerable == null) { return false; } <>7__wrap1 = enumerable.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } while (<>7__wrap1.MoveNext()) { object current = <>7__wrap1.Current; if (current != null && TryInspect(current, out string groupKey, out string displayName, out int scrapValue)) { <>2__current = (groupKey, displayName, scrapValue); <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(string groupKey, string displayName, int scrapValue)> IEnumerable<(string, string, int)>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <EnumerateInventoryScrapRows>d__23(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(string, string, int)>)this).GetEnumerator(); } } private const BindingFlags PublicInstance = BindingFlags.Instance | BindingFlags.Public; private const BindingFlags PublicStatic = BindingFlags.Static | BindingFlags.Public; private static bool ok; private static Assembly? asm; private static Type? itemDataType; private static Type? inventoryType; private static MethodInfo? getItem; private static MethodInfo? remove; private static MethodInfo? add; private static MethodInfo? addRpc; private static FieldInfo? invInstanceField; private static Func<object, int>? scrap; internal static bool EnsureItemDataApiReady() { return EnsureLoaded(); } internal static object? ResolveGameItemFromItemDataBoxed(object boxed) { if (!EnsureLoaded() || getItem == null) { return null; } try { return getItem.Invoke(boxed, null); } catch { return null; } } private static object? GetPropertyOrField(Type declaringType, object instance, string memberName) { PropertyInfo property = declaringType.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public); if (property != null) { return property.GetValue(instance); } return declaringType.GetField(memberName, BindingFlags.Instance | BindingFlags.Public)?.GetValue(instance); } private static bool IsScrap(Type declaringType, object instance) { object propertyOrField = GetPropertyOrField(declaringType, instance, "isScrap"); bool flag = default(bool); int num; if (propertyOrField is bool) { flag = (bool)propertyOrField; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static IEnumerable? InvItems() { if (inventoryType?.GetProperty("Items", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is IEnumerable result) { return result; } return null; } internal static int GetInventoryItemCount() { if (!EnsureLoaded() || inventoryType == null) { return -1; } object obj = inventoryType.GetProperty("Count", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj is int) { return (int)obj; } return -1; } private static object? InvBehaviour() { if (inventoryType == null) { return null; } if ((object)invInstanceField == null) { invInstanceField = inventoryType.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic); } object obj = invInstanceField?.GetValue(null); if (obj == null) { Object[] array = Object.FindObjectsByType(inventoryType, (FindObjectsSortMode)0); if (array == null || array.Length <= 0) { return null; } obj = array[0]; } return obj; } internal static List<object> CollectInventoryEntriesMatchingGroup(string key) { List<object> list = new List<object>(); if (string.IsNullOrEmpty(key) || !EnsureLoaded()) { return list; } IEnumerable enumerable = InvItems(); if (enumerable == null) { return list; } foreach (object item in enumerable) { if (item != null && TryInspect(item, out string groupKey, out string _, out int _) && string.Equals(groupKey, key, StringComparison.Ordinal)) { list.Add(item); } } return list; } internal static int SumInventoryScrapValues(IReadOnlyList<object> list) { if (scrap == null || list.Count == 0) { return 0; } int num = 0; for (int i = 0; i < list.Count; i++) { if (list[i] != null) { num += scrap(list[i]); } } return num; } internal static bool TryRemoveInventoryEntries(IReadOnlyList<object> entries, out string error) { error = ""; if (entries == null || entries.Count == 0) { return true; } if (!EnsureLoaded() || itemDataType == null || remove == null) { error = "no ShipInv Remove"; return false; } ShipInventoryMergeSuppression.Enter(); try { Array array = Array.CreateInstance(itemDataType, entries.Count); for (int i = 0; i < entries.Count; i++) { array.SetValue(entries[i], i); } remove.Invoke(null, new object[1] { array }); return true; } catch (Exception ex) { error = ex.InnerException?.Message ?? ex.Message; return false; } finally { ShipInventoryMergeSuppression.Exit(); } } internal static bool TryAddInventoryEntries(IReadOnlyList<object> entries, out string error, bool verifyCount = true) { error = ""; if (entries == null || entries.Count == 0) { return true; } if (!EnsureLoaded() || itemDataType == null || add == null) { error = "no ShipInv Add"; return false; } int num = (verifyCount ? GetInventoryItemCount() : (-1)); try { Array array = Array.CreateInstance(itemDataType, entries.Count); for (int i = 0; i < entries.Count; i++) { array.SetValue(entries[i], i); } add.Invoke(null, new object[1] { array }); if (verifyCount && num >= 0) { int num2 = num + entries.Count; int inventoryItemCount = GetInventoryItemCount(); if (inventoryItemCount != num2) { object obj = InvBehaviour(); if (obj != null && addRpc != null) { RpcDefaults(addRpc, obj, array); inventoryItemCount = GetInventoryItemCount(); } } if (inventoryItemCount != num2) { error = $"inv count {num}->{inventoryItemCount}"; return false; } } return true; } catch (Exception ex) { error = ex.InnerException?.Message ?? ex.Message; return false; } } [IteratorStateMachine(typeof(<EnumerateInventoryScrapRows>d__23))] internal static IEnumerable<(string groupKey, string displayName, int scrapValue)> EnumerateInventoryScrapRows() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateInventoryScrapRows>d__23(-2); } private static bool TryInspect(object boxed, out string groupKey, out string displayName, out int scrapValue) { groupKey = (displayName = ""); scrapValue = 0; if (!EnsureLoaded() || itemDataType == null || scrap == null || getItem == null) { return false; } if (!itemDataType.IsInstanceOfType(boxed)) { return false; } object obj; try { obj = getItem.Invoke(boxed, null); } catch { return false; } if (obj == null) { return false; } Type type = obj.GetType(); if (!IsScrap(type, obj)) { return false; } int num = scrap(boxed); displayName = (GetPropertyOrField(type, obj, "itemName") as string) ?? "?"; object? propertyOrField = GetPropertyOrField(type, obj, "spawnPrefab"); GameObject val = (GameObject)((propertyOrField is GameObject) ? propertyOrField : null); groupKey = ((val != null) ? ((Object)val).name.Replace("(Clone)", "", StringComparison.Ordinal) : displayName); if (string.IsNullOrEmpty(groupKey)) { groupKey = displayName; } scrapValue = num; return true; } private static bool EnsureLoaded() { if (ok) { return true; } try { if (asm == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "ShipInventoryUpdated") { asm = assembly; break; } } } if (asm == null) { return false; } if ((object)itemDataType == null) { itemDataType = asm.GetType("ShipInventoryUpdated.Objects.ItemData"); } if ((object)inventoryType == null) { inventoryType = asm.GetType("ShipInventoryUpdated.Scripts.Inventory"); } if (itemDataType == null || (getItem = itemDataType.GetMethod("GetItem", Type.EmptyTypes)) == null) { return false; } if (inventoryType != null) { Type type = itemDataType.MakeArrayType(); if ((object)remove == null) { remove = inventoryType.GetMethod("Remove", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { type }, null); } if ((object)add == null) { add = inventoryType.GetMethod("Add", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { type }, null); } if ((object)addRpc == null) { addRpc = FindRpc(inventoryType, "AddServerRpc", type); } } if (scrap == null) { Type type2 = itemDataType; PropertyInfo prop = type2.GetProperty("SCRAP_VALUE", BindingFlags.Instance | BindingFlags.Public); FieldInfo fld = ((prop == null) ? type2.GetField("SCRAP_VALUE", BindingFlags.Instance | BindingFlags.Public) : null); if (prop != null) { scrap = (object b) => Convert.ToInt32(prop.GetValue(b) ?? ((object)0)); } else { if (!(fld != null)) { return false; } scrap = (object b) => Convert.ToInt32(fld.GetValue(b) ?? ((object)0)); } } ok = true; return true; } catch { return false; } } private static MethodInfo? FindRpc(Type t, string name, Type arg0) { MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != name)) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != 0 && parameters[0].ParameterType == arg0) { return methodInfo; } } } return null; } private static void RpcDefaults(MethodInfo m, object target, object first) { ParameterInfo[] parameters = m.GetParameters(); object[] array = new object[parameters.Length]; array[0] = first; for (int i = 1; i < parameters.Length; i++) { array[i] = (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : Activator.CreateInstance(parameters[i].ParameterType)); } m.Invoke(target, array); } } internal static class TerminalType { private static readonly Lazy<Type> Lazy = new Lazy<Type>(delegate { Type? type = typeof(StartOfRound).Assembly.GetType("Terminal"); if (type == null) { throw new InvalidOperationException("no Terminal type"); } return type; }); internal static Type Value => Lazy.Value; } internal static class NormCmd { internal static string Strip(string s) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in s) { if (!char.IsPunctuation(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString().ToLowerInvariant(); } } internal static class ScreenNode { internal static TerminalNode Make(string text, int maxChars = 80) { TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>(); obj.displayText = text; obj.clearPreviousText = true; obj.maxCharactersToType = maxChars; return obj; } } internal static class TerminalReflection { private static readonly FieldInfo? ScreenTextField; private static readonly PropertyInfo? ScreenTextTextProperty; private static readonly FieldInfo? TextAddedField; private static readonly FieldInfo? CurrentTextField; private static readonly FieldInfo? ModifyingTextField; private static int inputLockDepth; private static bool lockCaretSaved; private static Color lockCaret; private static Type? terminalNodeType; private static Type TerminalNodeType { get { object obj = terminalNodeType; if (obj == null) { obj = typeof(StartOfRound).Assembly.GetType("TerminalNode") ?? throw new InvalidOperationException("no TerminalNode"); terminalNodeType = (Type?)obj; } return (Type)obj; } } static TerminalReflection() { Type value = TerminalType.Value; ScreenTextField = value.GetField("screenText", BindingFlags.Instance | BindingFlags.Public); TextAddedField = value.GetField("textAdded", BindingFlags.Instance | BindingFlags.Public); CurrentTextField = value.GetField("currentText", BindingFlags.Instance | BindingFlags.Public); ModifyingTextField = value.GetField("modifyingText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Type type = ScreenTextField?.FieldType; if (type != null) { ScreenTextTextProperty = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public); } } private static object? ScreenOf(object term) { return ScreenTextField?.GetValue(term); } internal static object? GetScreenTextObject(object term) { return ScreenTextField?.GetValue(term); } internal static void SetScreenBody(object term, string body) { ModifyingTextField?.SetValue(term, true); object obj = ScreenOf(term); if (obj != null && !(ScreenTextTextProperty == null)) { ScreenTextTextProperty.SetValue(obj, body); CurrentTextField?.SetValue(term, body); TextAddedField?.SetValue(term, 0); } } internal static void LockTermField(object term, bool on) { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) object obj = ScreenOf(term); if (obj == null) { return; } Type type = obj.GetType(); PropertyInfo property = type.GetProperty("readOnly", BindingFlags.Instance | BindingFlags.Public); PropertyInfo property2 = type.GetProperty("interactable", BindingFlags.Instance | BindingFlags.Public); PropertyInfo property3 = type.GetProperty("caretColor", BindingFlags.Instance | BindingFlags.Public); if (on) { if (inputLockDepth == 0) { if (property3?.GetValue(obj) is Color val) { lockCaret = val; lockCaretSaved = true; } else { lockCaretSaved = false; } } inputLockDepth++; type.GetMethod("ReleaseSelection", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null); type.GetMethod("DeactivateInputField", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null); property?.SetValue(obj, true); property2?.SetValue(obj, false); property3?.SetValue(obj, (object)new Color(0f, 0f, 0f, 0f)); return; } if (inputLockDepth > 0) { inputLockDepth--; } if (inputLockDepth == 0) { if (lockCaretSaved) { property3?.SetValue(obj, lockCaret); } lockCaretSaved = false; property?.SetValue(obj, false); property2?.SetValue(obj, true); } } internal static void LoadNewNode(object term, object node) { MethodInfo methodInfo = null; MethodInfo[] methods = TerminalType.Value.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "LoadNewNode") && methodInfo2.GetParameters().Length == 1 && methodInfo2.GetParameters()[0].ParameterType.IsAssignableFrom(TerminalNodeType)) { methodInfo = methodInfo2; break; } } methodInfo?.Invoke(term, new object[1] { node }); } internal static bool GetTerminalInUse(object term) { Type value = TerminalType.Value; object obj = value.GetField("terminalInUse", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term); if (obj is bool) { return (bool)obj; } obj = value.GetProperty("terminalInUse", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } internal static void ActivateInput(object term) { object obj = ScreenOf(term); if (obj != null) { Type type = obj.GetType(); type.GetMethod("ActivateInputField", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null); type.GetMethod("Select", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null); } } internal static int GetTextAdded(object term) { object obj = TextAddedField?.GetValue(term); if (obj is int) { return (int)obj; } obj = TerminalType.Value.GetProperty("textAdded", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term); if (obj is int) { return (int)obj; } return 0; } internal static string GetRawInputSuffixForItapi(object term) { object obj = ScreenOf(term); if (obj == null || !(ScreenTextTextProperty?.GetValue(obj) is string text)) { return ""; } int textAdded = GetTextAdded(term); if (textAdded > 0 && textAdded <= text.Length) { return text.Substring(text.Length - textAdded); } return ""; } internal static bool TryGetInputChunk(object term, out string chunk) { chunk = ""; object obj = ScreenOf(term); if (obj == null || !(ScreenTextTextProperty?.GetValue(obj) is string text)) { return false; } int textAdded = GetTextAdded(term); if (textAdded <= 0 || textAdded > text.Length) { return false; } chunk = text.Substring(text.Length - textAdded); return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "MergeScrap"; public const string PLUGIN_NAME = "MergeScrap"; public const string PLUGIN_VERSION = "1.1.1"; } }