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 CruiserScan v1.0.1
CruiserScan.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; 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("ved-gaur.cruiserscan")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+dc21fea1f827d4bd9d9b160ebdc8e976036b5ca4")] [assembly: AssemblyProduct("CruiserScan")] [assembly: AssemblyTitle("ved-gaur.cruiserscan")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CruiserScan { internal static class CruiserHud { [CompilerGenerated] private sealed class <ValueCoroutine>d__11 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ValueCoroutine>d__11(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(CruiserScan.DisplayDuration.Value); <>1__state = 1; return true; case 1: { <>1__state = -1; GameObject? counterObject = _counterObject; if (counterObject != null) { counterObject.SetActive(false); } _displayCoroutine = null; return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string HudCanvasPath = "/Systems/UI/Canvas/IngamePlayerHUD"; private const string VanillaValueCounterPath = "/Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/ValueCounter"; private static GameObject? _counterObject; private static TextMeshProUGUI? _text; private static Coroutine? _displayCoroutine; public static void Show(CruiserStats stats) { EnsureCreated(); ApplyPosition(); if (!((Object)(object)_counterObject == (Object)null) && !((Object)(object)_text == (Object)null)) { ((TMP_Text)_text).fontSize = CruiserScan.FontSize.Value; ((TMP_Text)_text).text = $"Cruiser: ${stats.TotalValue} | {stats.ItemCount} items"; _counterObject.SetActive(true); if (_displayCoroutine != null) { ((MonoBehaviour)GameNetworkManager.Instance).StopCoroutine(_displayCoroutine); } _displayCoroutine = ((MonoBehaviour)GameNetworkManager.Instance).StartCoroutine(ValueCoroutine()); } } public static void Hide() { GameObject? counterObject = _counterObject; if (counterObject != null) { counterObject.SetActive(false); } } private static void EnsureCreated() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown if ((Object)(object)_counterObject != (Object)null && (Object)(object)_text != (Object)null) { return; } GameObject val = GameObject.Find("/Systems/UI/Canvas/IngamePlayerHUD"); GameObject val2 = GameObject.Find("/Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/ValueCounter"); if ((Object)(object)val == (Object)null) { CruiserScan.Logger.LogError((object)"Failed to find IngamePlayerHUD."); return; } if ((Object)(object)val2 == (Object)null) { CruiserScan.Logger.LogError((object)"Failed to find vanilla ValueCounter."); return; } TextMeshProUGUI componentInChildren = val2.GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren == (Object)null) { CruiserScan.Logger.LogError((object)"Failed to find ValueCounter TMP text."); return; } _counterObject = new GameObject("CruiserScanCounter", new Type[1] { typeof(RectTransform) }); _counterObject.transform.SetParent(val.transform, false); _text = _counterObject.AddComponent<TextMeshProUGUI>(); CopyTextStyle(componentInChildren, _text); ConfigureLayout(); ApplyPosition(); _counterObject.SetActive(false); } private static void ConfigureLayout() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_counterObject == (Object)null) && !((Object)(object)_text == (Object)null)) { RectTransform component = _counterObject.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(0f, 1f); component.pivot = new Vector2(0f, 1f); component.sizeDelta = new Vector2(100f, 100f); RectTransform rectTransform = ((TMP_Text)_text).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Transform)rectTransform).localPosition = Vector3.zero; ((Transform)rectTransform).localRotation = Quaternion.identity; ((Transform)rectTransform).localScale = Vector3.one; } } private static void CopyTextStyle(TextMeshProUGUI source, TextMeshProUGUI target) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)target).font = ((TMP_Text)source).font; ((TMP_Text)target).fontSharedMaterial = ((TMP_Text)source).fontSharedMaterial; ((TMP_Text)target).spriteAsset = ((TMP_Text)source).spriteAsset; ((Graphic)target).color = ((Graphic)source).color; ((TMP_Text)target).outlineColor = ((TMP_Text)source).outlineColor; ((TMP_Text)target).outlineWidth = ((TMP_Text)source).outlineWidth; ((TMP_Text)target).fontStyle = ((TMP_Text)source).fontStyle; ((TMP_Text)target).characterSpacing = ((TMP_Text)source).characterSpacing; ((TMP_Text)target).wordSpacing = ((TMP_Text)source).wordSpacing; ((TMP_Text)target).lineSpacing = ((TMP_Text)source).lineSpacing; ((TMP_Text)target).paragraphSpacing = ((TMP_Text)source).paragraphSpacing; ((TMP_Text)target).enableAutoSizing = false; ((TMP_Text)target).fontSize = CruiserScan.FontSize.Value; ((TMP_Text)target).fontSizeMin = ((TMP_Text)source).fontSizeMin; ((TMP_Text)target).fontSizeMax = ((TMP_Text)source).fontSizeMax; ((TMP_Text)target).alignment = (TextAlignmentOptions)513; ((TMP_Text)target).enableWordWrapping = false; ((TMP_Text)target).overflowMode = (TextOverflowModes)0; ((Graphic)target).raycastTarget = false; } private static void ApplyPosition() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_counterObject == (Object)null)) { RectTransform component = _counterObject.GetComponent<RectTransform>(); component.anchoredPosition = new Vector2(CruiserScan.OffsetX.Value, CruiserScan.OffsetY.Value); } } [IteratorStateMachine(typeof(<ValueCoroutine>d__11))] private static IEnumerator ValueCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ValueCoroutine>d__11(0); } } internal static class CruiserPersistence { private const string CruiserObjectName = "CompanyCruiser(Clone)"; private const string ShipObjectPath = "Environment/HangarShip"; private static readonly HashSet<ulong> PersistedShipItems = new HashSet<ulong>(); public static void MarkCurrentShipItemsAsPersisted() { PersistedShipItems.Clear(); HashSet<GrabbableObject> hashSet = new HashSet<GrabbableObject>(); AddDescendantGrabbables(GameObject.Find("Environment/HangarShip"), hashSet); AddDescendantGrabbables(GameObject.Find("CompanyCruiser(Clone)"), hashSet); foreach (GrabbableObject item in hashSet) { if (!((Object)(object)item.itemProperties == (Object)null) && item.itemProperties.isScrap && !((Object)(object)((NetworkBehaviour)item).NetworkObject == (Object)null) && ((NetworkBehaviour)item).NetworkObject.IsSpawned) { PersistedShipItems.Add(((NetworkBehaviour)item).NetworkObjectId); } } } public static bool IsPersisted(GrabbableObject grabbable) { if ((Object)(object)((NetworkBehaviour)grabbable).NetworkObject == (Object)null || !((NetworkBehaviour)grabbable).NetworkObject.IsSpawned) { return false; } return PersistedShipItems.Contains(((NetworkBehaviour)grabbable).NetworkObjectId); } public static void Clear() { PersistedShipItems.Clear(); } private static void AddDescendantGrabbables(GameObject? root, HashSet<GrabbableObject> foundItems) { if ((Object)(object)root == (Object)null) { return; } GrabbableObject[] componentsInChildren = root.GetComponentsInChildren<GrabbableObject>(true); foreach (GrabbableObject val in componentsInChildren) { if ((Object)(object)val != (Object)null) { foundItems.Add(val); } } } } [HarmonyPatch(typeof(RoundManager))] internal static class RoundManagerPatch { [HarmonyPostfix] [HarmonyPatch("DespawnPropsAtEndOfRound")] private static void AfterDespawnPropsAtEndOfRound() { CruiserPersistence.MarkCurrentShipItemsAsPersisted(); } } [HarmonyPatch(typeof(GameNetworkManager))] internal static class GameNetworkManagerPatch { [HarmonyPostfix] [HarmonyPatch("StartDisconnect")] private static void AfterStartDisconnect() { CruiserPersistence.Clear(); } } [BepInPlugin("ved-gaur.cruiserscan", "CruiserScan", "1.0.1")] public class CruiserScan : BaseUnityPlugin { public static CruiserScan Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static Harmony? Harmony { get; set; } internal static ConfigEntry<float> OffsetX { get; private set; } internal static ConfigEntry<float> OffsetY { get; private set; } internal static ConfigEntry<float> FontSize { get; private set; } internal static ConfigEntry<float> DisplayDuration { get; private set; } internal static ConfigEntry<bool> ExcludeKnifeItem { get; private set; } internal static ConfigEntry<bool> ExcludeShotgunItem { get; private set; } internal static ConfigEntry<bool> ExcludePersistedItems { get; private set; } private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; Instance = this; OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "OffsetX", 20f, "Extra horizontal offset from the default position."); OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "OffsetY", 50f, "Extra vertical offset from the default position."); FontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "FontSize", 18f, "Font size for the text."); DisplayDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "DisplayDuration", 3f, "How long the text stays visible for after scanning."); ExcludeKnifeItem = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "ExcludeKnifeItem", true, "Whether to exclude butcher knife from the scan."); ExcludeShotgunItem = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "ExcludeShotgunItem", true, "Whether to exclude shotgun from the scan."); ExcludePersistedItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "ExcludePersistedItems", true, "Whether to exclude items that were picked up in previous rounds."); Patch(); Logger.LogInfo((object)"ved-gaur.cruiserscan v1.0.1 has loaded!"); } internal static void Patch() { //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_0017: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony("ved-gaur.cruiserscan"); } Logger.LogDebug((object)"Patching..."); Harmony.PatchAll(); Logger.LogDebug((object)"Finished patching!"); } internal static void Unpatch() { Logger.LogDebug((object)"Unpatching..."); Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } Logger.LogDebug((object)"Finished unpatching!"); } } [HarmonyPatch(typeof(HUDManager))] internal static class HUDManagerPatch { private static FieldInfo? _playerPingingScanField; private static float _pingBeforeScan; private static bool _candidateScanInput; [HarmonyPostfix] [HarmonyPatch("Awake")] private static void OnHudAwake() { _playerPingingScanField = AccessTools.Field(typeof(HUDManager), "playerPingingScan"); if (_playerPingingScanField == null) { CruiserScan.Logger.LogError((object)"Failed to find HUDManager.playerPingingScan."); } } [HarmonyPrefix] [HarmonyPatch("PingScan_performed")] private static void BeforePingScan(HUDManager __instance, in CallbackContext context) { //IL_0032: 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) _candidateScanInput = false; _pingBeforeScan = 0f; if (_playerPingingScanField == null || (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return; } CallbackContext val = context; if (((CallbackContext)(ref val)).performed) { object value = _playerPingingScanField.GetValue(__instance); if (value is float) { float pingBeforeScan = (float)value; _pingBeforeScan = pingBeforeScan; _candidateScanInput = true; } } } [HarmonyPostfix] [HarmonyPatch("PingScan_performed")] private static void AfterPingScan(HUDManager __instance) { if (!_candidateScanInput || _playerPingingScanField == null) { return; } object value = _playerPingingScanField.GetValue(__instance); if (value is float) { float num = (float)value; if (_pingBeforeScan <= -1f && num > _pingBeforeScan) { CruiserStats stats = CruiserValueCalculator.CalculateStats(); CruiserHud.Show(stats); } } } } internal readonly struct CruiserStats { public int TotalValue { get; } public int ItemCount { get; } public CruiserStats(int totalValue, int itemCount) { TotalValue = totalValue; ItemCount = itemCount; } } internal static class CruiserValueCalculator { private const string CruiserPath = "CompanyCruiser(Clone)"; private const string KitchenKnifeName = "Kitchen knife"; private const string ShotgunName = "Double-barrel"; public static CruiserStats CalculateStats() { GameObject val = GameObject.Find("CompanyCruiser(Clone)"); if ((Object)(object)val == (Object)null) { return new CruiserStats(0, 0); } ScanNodeProperties[] array = (from node in val.GetComponentsInChildren<GrabbableObject>(true).Where(IsCountableGrabbable).Select(GetScanNode) .Where(IsCountableScrapNode) select (node)).ToArray(); int totalValue = array.Sum((ScanNodeProperties node) => node.scrapValue); int itemCount = array.Length; return new CruiserStats(totalValue, itemCount); } private static ScanNodeProperties? GetScanNode(GrabbableObject grabbable) { return ((Component)grabbable).GetComponentInChildren<ScanNodeProperties>(true); } private static bool IsCountableGrabbable(GrabbableObject grabbable) { if (CruiserScan.ExcludePersistedItems.Value && (grabbable.scrapPersistedThroughRounds || CruiserPersistence.IsPersisted(grabbable))) { return false; } GiftBoxItem val = (GiftBoxItem)(object)((grabbable is GiftBoxItem) ? grabbable : null); if (val != null && val.hasUsedGift) { return false; } return true; } private static bool IsCountableScrapNode(ScanNodeProperties? node) { if ((Object)(object)node == (Object)null) { return false; } if (node.scrapValue <= 0) { return false; } if (CruiserScan.ExcludeKnifeItem.Value && node.headerText == "Kitchen knife") { return false; } if (CruiserScan.ExcludeShotgunItem.Value && node.headerText == "Double-barrel") { return false; } return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "ved-gaur.cruiserscan"; public const string PLUGIN_NAME = "CruiserScan"; public const string PLUGIN_VERSION = "1.0.1"; } }