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 MissedScrap v1.1.2
plugins/MissedScrap.dll
Decompiled 5 months 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.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using MissedScrap.Patches; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [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: AssemblyCompany("MissedScrap")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Shows missed scrap items at end of day")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2")] [assembly: AssemblyProduct("MissedScrap")] [assembly: AssemblyTitle("MissedScrap")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.2.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 MissedScrap { public class MissedScrapUI : MonoBehaviour { private GameObject uiPanel; private Text titleText; private Text itemsText; private Text totalText; private Image backgroundImage; private bool isShowing = false; private void Update() { if (isShowing && Keyboard.current != null && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { HideUI(); } } public void ShowMissedScrap(List<(string name, int value)> missedItems, int totalValue) { if (!isShowing) { CreateUI(); PopulateUI(missedItems, totalValue); isShowing = true; ((MonoBehaviour)this).Invoke("HideUI", 15f); } } private void CreateUI() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Expected O, but got Unknown //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)uiPanel != (Object)null) { Object.Destroy((Object)(object)uiPanel); } uiPanel = new GameObject("MissedScrapPanel"); Canvas val = uiPanel.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = 1000; CanvasScaler val2 = uiPanel.AddComponent<CanvasScaler>(); val2.uiScaleMode = (ScaleMode)1; val2.referenceResolution = new Vector2(1920f, 1080f); GameObject val3 = new GameObject("Background"); val3.transform.SetParent(uiPanel.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = new Vector2(1f, 0f); val4.anchorMax = new Vector2(1f, 1f); val4.pivot = new Vector2(1f, 0.5f); val4.sizeDelta = new Vector2(384f, 0f); backgroundImage = val3.AddComponent<Image>(); ((Graphic)backgroundImage).color = new Color(0.1f, 0.1f, 0.1f, 0.92f); GameObject val5 = new GameObject("Title"); val5.transform.SetParent(val3.transform, false); RectTransform val6 = val5.AddComponent<RectTransform>(); val6.anchorMin = new Vector2(0f, 1f); val6.anchorMax = new Vector2(1f, 1f); val6.pivot = new Vector2(0.5f, 1f); val6.sizeDelta = new Vector2(-20f, 45f); val6.anchoredPosition = new Vector2(0f, -10f); titleText = val5.AddComponent<Text>(); titleText.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); titleText.fontSize = 24; titleText.fontStyle = (FontStyle)1; titleText.alignment = (TextAnchor)4; ((Graphic)titleText).color = Color.white; GameObject val7 = new GameObject("Total"); val7.transform.SetParent(val3.transform, false); RectTransform val8 = val7.AddComponent<RectTransform>(); val8.anchorMin = new Vector2(0f, 1f); val8.anchorMax = new Vector2(1f, 1f); val8.pivot = new Vector2(0.5f, 1f); val8.sizeDelta = new Vector2(-20f, 55f); val8.anchoredPosition = new Vector2(0f, -60f); totalText = val7.AddComponent<Text>(); totalText.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); totalText.fontSize = 22; totalText.fontStyle = (FontStyle)1; totalText.alignment = (TextAnchor)4; ((Graphic)totalText).color = Color.yellow; GameObject val9 = new GameObject("ItemsList"); val9.transform.SetParent(val3.transform, false); RectTransform val10 = val9.AddComponent<RectTransform>(); val10.anchorMin = new Vector2(0f, 0f); val10.anchorMax = new Vector2(1f, 1f); val10.pivot = new Vector2(0.5f, 0.5f); val10.offsetMin = new Vector2(20f, 20f); val10.offsetMax = new Vector2(-20f, -135f); itemsText = val9.AddComponent<Text>(); itemsText.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); itemsText.fontSize = 18; itemsText.alignment = (TextAnchor)0; ((Graphic)itemsText).color = new Color(1f, 0.9f, 0.7f); itemsText.horizontalOverflow = (HorizontalWrapMode)0; itemsText.verticalOverflow = (VerticalWrapMode)1; itemsText.supportRichText = true; Object.DontDestroyOnLoad((Object)(object)uiPanel); } private void PopulateUI(List<(string name, int value)> missedItems, int totalValue) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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) Color color = default(Color); string text; if (totalValue == 0) { ((Color)(ref color))..ctor(0.1f, 0.4f, 0.1f, 0.92f); text = "PERFECT!"; ((Graphic)titleText).color = new Color(0.3f, 1f, 0.3f); } else if (totalValue < 50) { ((Color)(ref color))..ctor(0.4f, 0.4f, 0.1f, 0.92f); text = "MINOR LOSS"; ((Graphic)titleText).color = new Color(1f, 1f, 0.3f); } else if (totalValue < 150) { ((Color)(ref color))..ctor(0.5f, 0.3f, 0.1f, 0.92f); text = "SIGNIFICANT LOSS"; ((Graphic)titleText).color = new Color(1f, 0.6f, 0.2f); } else { ((Color)(ref color))..ctor(0.4f, 0.1f, 0.1f, 0.92f); text = "MAJOR LOSS!"; ((Graphic)titleText).color = new Color(1f, 0.3f, 0.3f); } ((Graphic)backgroundImage).color = color; if (missedItems.Count == 0) { titleText.text = "NO SCRAP MISSED!"; totalText.text = ""; itemsText.text = "\n\nPerfect collection!\n\nYou got everything!"; return; } titleText.text = text; totalText.text = $"{missedItems.Count} items missed worth ${totalValue}"; string text2 = ""; foreach (var missedItem in missedItems) { text2 += $"• ${missedItem.value} - {missedItem.name}\n"; } itemsText.text = text2; Plugin.Logger.LogInfo((object)$"Displayed {missedItems.Count} missed items worth ${totalValue}"); } public void HideUI() { if ((Object)(object)uiPanel != (Object)null) { Object.Destroy((Object)(object)uiPanel); uiPanel = null; } isShowing = false; } private void OnDestroy() { HideUI(); } } [BepInPlugin("com.psyko.missedscrap", "MissedScrap", "1.1.2")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private readonly Harmony harmony = new Harmony("com.psyko.missedscrap"); private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; harmony.PatchAll(typeof(Plugin)); harmony.PatchAll(typeof(EndOfDayPatch)); harmony.PatchAll(typeof(GameResetPatch)); harmony.PatchAll(typeof(EnemyDropPatch)); harmony.PatchAll(typeof(GiftBoxPatch)); Logger.LogInfo((object)"Plugin com.psyko.missedscrap is loaded!"); } } public static class PluginInfo { public const string PLUGIN_GUID = "com.psyko.missedscrap"; public const string PLUGIN_NAME = "MissedScrap"; public const string PLUGIN_VERSION = "1.1.2"; } } namespace MissedScrap.Patches { [HarmonyPatch(typeof(RoundManager))] internal class EndOfDayPatch { private static List<GrabbableObject> currentRoundScrap = new List<GrabbableObject>(); private static HashSet<int> previousShipItemIDs = new HashSet<int>(); private static HashSet<int> trackedItemIDs = new HashSet<int>(); private static HashSet<int> removedItemIDs = new HashSet<int>(); private static MissedScrapUI uiInstance; private static bool isTrackingActive = false; private static readonly List<VehicleController> vehicleCache = new List<VehicleController>(); private static float lastVehicleCacheTime = 0f; private static Coroutine continuousTrackingCoroutine = null; [HarmonyPatch(typeof(StartOfRound), "openingDoorsSequence")] [HarmonyPostfix] private static void BeforeLanding() { RecordPreExistingItems(); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] private static void OnGameStart() { RecordPreExistingItems(); } private static void RecordPreExistingItems() { if (isTrackingActive || currentRoundScrap.Count != 0) { return; } StopTrackingCoroutine(); currentRoundScrap.Clear(); trackedItemIDs.Clear(); removedItemIDs.Clear(); isTrackingActive = false; previousShipItemIDs.Clear(); GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); GrabbableObject[] array2 = array; foreach (GrabbableObject val in array2) { if ((Object)(object)val != (Object)null && (Object)(object)val.itemProperties != (Object)null && val.itemProperties.isScrap && (val.isInShipRoom || val.isInElevator)) { previousShipItemIDs.Add(((Object)val).GetInstanceID()); } } Plugin.Logger.LogInfo((object)$"Tracked {previousShipItemIDs.Count} items from previous quotas"); } [HarmonyPatch("SpawnScrapInLevel")] [HarmonyPostfix] private static void OnScrapSpawned(RoundManager __instance) { ((MonoBehaviour)__instance).StartCoroutine(CaptureScrapDelayed()); } private static IEnumerator CaptureScrapDelayed() { yield return (object)new WaitForSeconds(2f); CaptureNewScrap(); isTrackingActive = true; if ((Object)(object)RoundManager.Instance != (Object)null) { continuousTrackingCoroutine = ((MonoBehaviour)RoundManager.Instance).StartCoroutine(ContinuousScrapTracking()); } } private static void StopTrackingCoroutine() { if (continuousTrackingCoroutine != null && (Object)(object)RoundManager.Instance != (Object)null) { try { ((MonoBehaviour)RoundManager.Instance).StopCoroutine(continuousTrackingCoroutine); } catch { } continuousTrackingCoroutine = null; } } internal static void StopTracking() { StopTrackingCoroutine(); } private static IEnumerator ContinuousScrapTracking() { float checkInterval = 5f; while (isTrackingActive) { yield return (object)new WaitForSeconds(checkInterval); CaptureNewScrap(); } } internal static void CaptureNewScrap() { try { GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); GrabbableObject[] array2 = array; foreach (GrabbableObject val in array2) { if ((Object)(object)val == (Object)null || (Object)(object)val.itemProperties == (Object)null) { continue; } try { if (!((Component)val).gameObject.activeInHierarchy) { continue; } } catch { continue; } if (val.itemProperties.isScrap) { int instanceID = ((Object)val).GetInstanceID(); if (!previousShipItemIDs.Contains(instanceID) && !trackedItemIDs.Contains(instanceID)) { currentRoundScrap.Add(val); trackedItemIDs.Add(instanceID); } } } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error capturing scrap: " + ex.Message)); } } internal static void RemoveTrackedItem(int itemID) { try { removedItemIDs.Add(itemID); trackedItemIDs.Remove(itemID); for (int num = currentRoundScrap.Count - 1; num >= 0; num--) { if ((Object)(object)currentRoundScrap[num] != (Object)null && ((Object)currentRoundScrap[num]).GetInstanceID() == itemID) { currentRoundScrap.RemoveAt(num); break; } } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error removing tracked item: " + ex.Message)); } } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] [HarmonyPostfix] private static void OnShipLeave() { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(CheckMissedScrapDelayed()); } private static IEnumerator CheckMissedScrapDelayed() { isTrackingActive = false; StopTrackingCoroutine(); yield return (object)new WaitForSeconds(3f); try { if ((Object)(object)StartOfRound.Instance == (Object)null) { Plugin.Logger.LogWarning((object)"StartOfRound instance is null - aborting check"); yield break; } if (((Object)StartOfRound.Instance.currentLevel).name.Contains("CompanyBuilding")) { Plugin.Logger.LogInfo((object)"Selling at company - skipping missed scrap check"); yield break; } Plugin.Logger.LogInfo((object)$"Analyzing {currentRoundScrap.Count} items from this round..."); HashSet<int> collectedItems = new HashSet<int>(currentRoundScrap.Count); foreach (GrabbableObject trackedItem in currentRoundScrap) { if ((Object)(object)trackedItem == (Object)null) { continue; } try { if (trackedItem.isInShipRoom || trackedItem.isInElevator) { collectedItems.Add(((Object)trackedItem).GetInstanceID()); } } catch { } } float currentTime = Time.time; if (vehicleCache.Count == 0 || currentTime - lastVehicleCacheTime > 10f) { vehicleCache.Clear(); VehicleController[] vehicles = Object.FindObjectsOfType<VehicleController>(); VehicleController[] array = vehicles; foreach (VehicleController v in array) { vehicleCache.Add(v); } lastVehicleCacheTime = currentTime; } if (vehicleCache.Count > 0) { foreach (GrabbableObject trackedItem2 in currentRoundScrap) { if ((Object)(object)trackedItem2 == (Object)null) { continue; } int itemID2 = ((Object)trackedItem2).GetInstanceID(); if (collectedItems.Contains(itemID2)) { continue; } try { if (IsInVehicle(trackedItem2)) { collectedItems.Add(itemID2); } } catch { } } } Plugin.Logger.LogInfo((object)$"Collected: {collectedItems.Count} items (ship + vehicles)"); List<(string name, int value)> missedScrap = new List<(string, int)>(); int totalMissedValue = 0; if (currentRoundScrap.Count > 0) { missedScrap.Capacity = currentRoundScrap.Count; } for (int i = 0; i < currentRoundScrap.Count; i++) { GrabbableObject spawnedItem = currentRoundScrap[i]; if ((Object)(object)spawnedItem == (Object)null) { continue; } int itemID = ((Object)spawnedItem).GetInstanceID(); if (removedItemIDs.Contains(itemID) || collectedItems.Contains(itemID)) { continue; } try { if ((Object)(object)spawnedItem.itemProperties != (Object)null) { string itemName = spawnedItem.itemProperties.itemName; int value = spawnedItem.scrapValue; missedScrap.Add((itemName, value)); totalMissedValue += value; } } catch { } } missedScrap.Sort(((string name, int value) a, (string name, int value) b) => b.value.CompareTo(a.value)); if (missedScrap.Count > 0) { Plugin.Logger.LogWarning((object)"╔════ MISSED SCRAP ════╗"); foreach (var item in missedScrap) { Plugin.Logger.LogWarning((object)$" {item.name}: ${item.value}"); } Plugin.Logger.LogWarning((object)$" Total: ${totalMissedValue} ({missedScrap.Count} items)"); Plugin.Logger.LogWarning((object)"╚══════════════════════╝"); } else { Plugin.Logger.LogInfo((object)"✓ Perfect run - no scrap missed!"); } ShowMissedScrapUI(missedScrap, totalMissedValue); } catch (Exception ex) { Plugin.Logger.LogError((object)("Error in MissedScrap: " + ex.Message)); Plugin.Logger.LogError((object)("Stack trace: " + ex.StackTrace)); } yield return (object)new WaitForSeconds(1f); CleanupOldData(); } private static bool IsInVehicle(GrabbableObject item) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (vehicleCache.Count == 0) { return false; } try { Vector3 position = ((Component)item).transform.position; bool flag = false; for (int i = 0; i < vehicleCache.Count; i++) { VehicleController val = vehicleCache[i]; if ((Object)(object)val != (Object)null) { Vector3 val2 = position - ((Component)val).transform.position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < 100f) { flag = true; break; } } } if (!flag) { return false; } Transform parent = ((Component)item).transform.parent; int num = 5; int num2 = 0; while ((Object)(object)parent != (Object)null && num2 < num) { string name = ((Object)parent).name; if (name.Contains("Cruiser") || name.Contains("Vehicle")) { return true; } parent = parent.parent; num2++; } return flag; } catch { } return false; } private static void ShowMissedScrapUI(List<(string name, int value)> missedScrap, int totalValue) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown try { if ((Object)(object)uiInstance == (Object)null) { GameObject val = new GameObject("MissedScrapUIManager"); uiInstance = val.AddComponent<MissedScrapUI>(); Object.DontDestroyOnLoad((Object)(object)val); } uiInstance.ShowMissedScrap(missedScrap, totalValue); } catch (Exception ex) { Plugin.Logger.LogError((object)("Failed to display UI: " + ex.Message)); } } private static void CleanupOldData() { try { currentRoundScrap.Clear(); trackedItemIDs.Clear(); removedItemIDs.Clear(); if (currentRoundScrap.Capacity > 100) { currentRoundScrap.Capacity = 100; } vehicleCache.Clear(); lastVehicleCacheTime = 0f; if (previousShipItemIDs.Count > 500) { Plugin.Logger.LogWarning((object)"Large number of ship items detected (500+). Performance may be impacted."); } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error during cleanup: " + ex.Message)); } } internal static void EmergencyReset() { try { Plugin.Logger.LogInfo((object)"Emergency reset triggered"); isTrackingActive = false; StopTrackingCoroutine(); currentRoundScrap.Clear(); previousShipItemIDs.Clear(); trackedItemIDs.Clear(); removedItemIDs.Clear(); vehicleCache.Clear(); } catch { } } } [HarmonyPatch(typeof(StartOfRound))] internal class GameResetPatch { [HarmonyPatch("ReviveDeadPlayers")] [HarmonyPostfix] private static void OnPlayersRevived() { EndOfDayPatch.EmergencyReset(); } [HarmonyPatch("EndOfGame")] [HarmonyPrefix] private static void BeforeEndOfGame() { EndOfDayPatch.StopTracking(); } } [HarmonyPatch(typeof(EnemyAI))] internal class EnemyDropPatch { [HarmonyPatch("KillEnemy")] [HarmonyPostfix] private static void OnEnemyKilled(EnemyAI __instance) { ((MonoBehaviour)__instance).StartCoroutine(CheckForDroppedLoot()); } private static IEnumerator CheckForDroppedLoot() { yield return (object)new WaitForSeconds(0.5f); EndOfDayPatch.CaptureNewScrap(); } } [HarmonyPatch(typeof(GiftBoxItem))] internal class GiftBoxPatch { [HarmonyPatch("OpenGiftBoxServerRpc")] [HarmonyPrefix] private static void BeforeOpenGiftBoxServer(GiftBoxItem __instance) { if ((Object)(object)__instance != (Object)null) { EndOfDayPatch.RemoveTrackedItem(((Object)__instance).GetInstanceID()); } } [HarmonyPatch("OpenGiftBoxClientRpc")] [HarmonyPrefix] private static void BeforeOpenGiftBoxClient(GiftBoxItem __instance) { if ((Object)(object)__instance != (Object)null) { EndOfDayPatch.RemoveTrackedItem(((Object)__instance).GetInstanceID()); } } [HarmonyPatch("OpenGiftBoxServerRpc")] [HarmonyPostfix] private static void AfterOpenGiftBox(GiftBoxItem __instance) { if ((Object)(object)__instance != (Object)null) { ((MonoBehaviour)__instance).StartCoroutine(CaptureGiftItem()); } } private static IEnumerator CaptureGiftItem() { yield return (object)new WaitForSeconds(1f); EndOfDayPatch.CaptureNewScrap(); } } }