Decompiled source of ScanTweaks v1.5.3

ScanTweaks.dll

Decompiled a month ago
using 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 BepInEx;
using GameNetcodeStuff;
using HarmonyLib;
using LethalMDK;
using ScanTweaks.World;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityMDK.Config;
using UnityMDK.Injection;
using UnityMDK.Logging;
using UnityMDK.Reflection;

[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("ScanTweaks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ScanTweaks")]
[assembly: AssemblyTitle("ScanTweaks")]
[assembly: AssemblyVersion("1.2.1.0")]
namespace ScanTweaks
{
	public static class Player
	{
		public static bool IsServerOrHost
		{
			get
			{
				if (!NetworkManager.Singleton.IsHost)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return true;
			}
		}
	}
	public class CustomScanNodeProperties : ScanNodeProperties
	{
		[ConfigSection("PingScan")]
		[ConfigDescription("Adds a scan node to every item that didn't already have one.")]
		public static ConfigData<bool> EnableScanNodeForTools = new ConfigData<bool>(true);

		private GrabbableObject _grabbableObject;

		private static readonly string NoBatteryMsg = "No Battery";

		private static readonly string[] BatteryPercentMsg = new string[2] { "Battery: ", "%" };

		private void Awake()
		{
			_grabbableObject = ((Component)this).GetComponentInParent<GrabbableObject>(true);
		}

		private void Update()
		{
			if (!((Object)(object)_grabbableObject == (Object)null) && _grabbableObject.itemProperties.requiresBattery)
			{
				if (_grabbableObject.insertedBattery == null)
				{
					base.subText = NoBatteryMsg;
					return;
				}
				string separator = (_grabbableObject.insertedBattery.empty ? "0" : ((int)Math.Ceiling(Math.Clamp(_grabbableObject.insertedBattery.charge * 100f, 0f, 100f))).ToString());
				base.subText = string.Join(separator, BatteryPercentMsg);
			}
		}
	}
	[InjectToComponent(typeof(IngamePlayerSettings), false)]
	public class PingScan : MonoBehaviour
	{
		[ConfigSection("PingScan")]
		[ConfigDescription("Enable/Disable the ping scan tweaks")]
		public static readonly ConfigData<bool> PingScanDoPatch = new ConfigData<bool>(true);

		[ConfigDescription("The delay between each ping scan step. A larger value will make the scan take longer to reach great distances.")]
		private static readonly ConfigData<float> PingScanStepDuration = new ConfigData<float>(0.01f);

		[ConfigDescription("Should dynamic objects (e.g. doors) block the ping scan")]
		private static readonly ConfigData<bool> DynamicObjectsBlockRaycast = new ConfigData<bool>(true);

		[SerializeField]
		private float _range = 80f;

		[SerializeField]
		private float _outOfFrustumPaddingHorizontal = 0.2f;

		private readonly List<Collider> _currentScannedColliders = new List<Collider>();

		private readonly List<ScanNodeProperties> _currentScannedNodes = new List<ScanNodeProperties>();

		private readonly List<int> _currentScannedValues = new List<int>();

		private readonly List<GrabbableObject> _currentScannedGrabbables = new List<GrabbableObject>();

		private readonly Collider[] _hitAlloc = (Collider[])(object)new Collider[30];

		private static readonly int _physicsMask = LayerMasks.Room;

		private static readonly int _doorMask = LayerMasks.InteractableObject;

		private float _outOfFrustumPaddingVertical => _outOfFrustumPaddingHorizontal * ((float)Screen.width / (float)Screen.height);

		public static int PingedScrapValue { get; private set; }

		public static int NodeUtility => 0;

		public static int NodeCreature => 1;

		public static int NodeScrap => 2;

		public static event Action<ScanNodeProperties> ScanNodeAdded;

		public static event Action<ScanNodeProperties> ScanNodeRemoved;

		public static event Action DoPing;

		internal static void TriggerPingScan()
		{
			PingScan.DoPing?.Invoke();
		}

		private void Update()
		{
			PlayerControllerB localPlayer = Player.LocalPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			if (localPlayer.inSpecialInteractAnimation && _currentScannedNodes.Count > 0)
			{
				RemoveAll();
			}
			float outOfFrustumPaddingHorizontal = _outOfFrustumPaddingHorizontal;
			float outOfFrustumPaddingVertical = _outOfFrustumPaddingVertical;
			for (int num = _currentScannedNodes.Count - 1; num >= 0; num--)
			{
				ScanNodeProperties scanNode = _currentScannedNodes[num];
				Collider nodeCollider = _currentScannedColliders[num];
				GrabbableObject val = _currentScannedGrabbables[num];
				if (Object.op_Implicit((Object)(object)val) && val.isHeld && !val.isHeldByEnemy)
				{
					RemoveScanNodeAt(num);
				}
				else if (!IsNodeVisible(scanNode, nodeCollider, localPlayer.gameplayCamera, outOfFrustumPaddingHorizontal, outOfFrustumPaddingVertical))
				{
					RemoveScanNodeAt(num);
				}
			}
		}

		private void OnEnable()
		{
			DoPing += OnPingScan;
		}

		private void OnDisable()
		{
			DoPing -= OnPingScan;
			RemoveAll();
		}

		private void RemoveAll()
		{
			for (int num = _currentScannedNodes.Count - 1; num >= 0; num--)
			{
				RemoveScanNodeAt(num);
			}
		}

		private void OnPingScan()
		{
			Camera gameplayCamera = Player.LocalPlayer.gameplayCamera;
			((MonoBehaviour)this).StartCoroutine(Pinging(gameplayCamera));
		}

		private IEnumerator Pinging(Camera camera)
		{
			Transform transform = ((Component)camera).transform;
			Vector3 currentTestPos = transform.position;
			Vector3 direction = transform.forward;
			Quaternion boxOrientation = Quaternion.LookRotation(direction);
			Vector3 screenPos = Vector3.one;
			Vector3 extents = Vector3.one;
			float currentLength = 0f;
			float nextStep = Time.time;
			while (currentLength < _range)
			{
				yield return null;
				for (; Time.time >= nextStep; nextStep += ConfigData<float>.op_Implicit(PingScanStepDuration))
				{
					UpdateExtents();
					DoPing();
					currentTestPos += direction * 0.5f;
					currentLength += 0.5f;
				}
			}
			void DoPing()
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				int num = Physics.OverlapBoxNonAlloc(currentTestPos, extents, _hitAlloc, boxOrientation, LayerMasks.ScanNode, (QueryTriggerInteraction)1);
				ScanNodeProperties val2 = default(ScanNodeProperties);
				for (int i = 0; i < num; i++)
				{
					Collider val = _hitAlloc[i];
					if (((Component)val).TryGetComponent<ScanNodeProperties>(ref val2) && IsNodeVisible(val2, val, camera, 0f, 0f))
					{
						AddScanNode(val2);
					}
				}
			}
			void UpdateExtents()
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: 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_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				screenPos.z = currentLength;
				Vector3 val3 = ((Component)camera).transform.InverseTransformPoint(camera.ViewportToWorldPoint(screenPos));
				extents = val3;
				extents.z = 0.25f;
			}
		}

		private static bool PointOutOfViewport(float point, float padding)
		{
			if (!(point > 1f + padding))
			{
				return point < 0f - padding;
			}
			return true;
		}

		private static bool IsNodeVisible(ScanNodeProperties scanNode, Collider nodeCollider, Camera camera, float paddingX, float paddingY)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)scanNode == (Object)null)
			{
				return false;
			}
			if (!((Behaviour)scanNode).enabled)
			{
				return false;
			}
			if (scanNode is CustomScanNodeProperties && !ConfigData<bool>.op_Implicit(CustomScanNodeProperties.EnableScanNodeForTools))
			{
				return false;
			}
			if ((Object)(object)nodeCollider == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)camera).transform.position;
			Bounds bounds = nodeCollider.bounds;
			Vector3 val = position - ((Bounds)(ref bounds)).center;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < (float)scanNode.minRange)
			{
				return false;
			}
			if (magnitude > (float)scanNode.maxRange)
			{
				return false;
			}
			bounds = nodeCollider.bounds;
			Vector3 val2 = camera.WorldToViewportPoint(((Bounds)(ref bounds)).center);
			if (PointOutOfViewport(val2.x, paddingX) || PointOutOfViewport(val2.y, paddingY) || val2.z < 0f)
			{
				return false;
			}
			int num = _physicsMask;
			if (ConfigData<bool>.op_Implicit(DynamicObjectsBlockRaycast))
			{
				num |= _doorMask;
			}
			if (scanNode.requiresLineOfSight)
			{
				bounds = nodeCollider.bounds;
				return !Linecast(position, ((Bounds)(ref bounds)).center, num);
			}
			return true;
		}

		private static bool Linecast(Vector3 start, Vector3 end, int layerMask)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = end - start;
			if (Math.Abs(val.y) > 0.1f)
			{
				end = ((!(val.y > 0f)) ? (end + Vector3.up * 0.1f) : (end - Vector3.up * 0.1f));
			}
			RaycastHit val2 = default(RaycastHit);
			if (!Physics.Linecast(start, end, ref val2, layerMask, (QueryTriggerInteraction)1))
			{
				return false;
			}
			if (((Component)((RaycastHit)(ref val2)).collider).gameObject.CompareTag(Tags.InteractTrigger))
			{
				return Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponent<AnimatedObjectTrigger>());
			}
			return true;
		}

		private void AttemptScanNewCreature(int creatureID)
		{
			Terminal val = default(Terminal);
			if (creatureID >= 0 && Object.op_Implicit((Object)(object)HUDManager.Instance) && ReflectionUtility.TryGetField<Terminal>((object)HUDManager.Instance, "terminalScript", ref val) && !val.scannedEnemyIDs.Contains(creatureID))
			{
				HUDManager.Instance.ScanNewCreatureServerRpc(creatureID);
			}
		}

		private void AddScanNode(ScanNodeProperties scanNodeProperties)
		{
			if (Object.op_Implicit((Object)(object)scanNodeProperties) && !_currentScannedNodes.Contains(scanNodeProperties))
			{
				GrabbableObject componentInParent = ((Component)scanNodeProperties).GetComponentInParent<GrabbableObject>();
				if (!Object.op_Implicit((Object)(object)componentInParent) || !componentInParent.isHeld || componentInParent.isHeldByEnemy)
				{
					Collider component = ((Component)scanNodeProperties).GetComponent<Collider>();
					component.enabled = false;
					AttemptScanNewCreature(scanNodeProperties.creatureScanID);
					_currentScannedValues.Add((scanNodeProperties.nodeType == NodeScrap) ? scanNodeProperties.scrapValue : 0);
					_currentScannedColliders.Add(component);
					_currentScannedNodes.Add(scanNodeProperties);
					_currentScannedGrabbables.Add(componentInParent);
					CalculateTotalValue();
					PingScan.ScanNodeAdded?.Invoke(scanNodeProperties);
				}
			}
		}

		private void RemoveScanNodeAt(int index)
		{
			if (index >= 0 && index < _currentScannedNodes.Count)
			{
				Collider val = _currentScannedColliders[index];
				if (Object.op_Implicit((Object)(object)val))
				{
					val.enabled = true;
				}
				_currentScannedValues.RemoveAt(index);
				CalculateTotalValue();
				PingScan.ScanNodeRemoved?.Invoke(_currentScannedNodes[index]);
				_currentScannedColliders.RemoveAt(index);
				_currentScannedNodes.RemoveAt(index);
				_currentScannedGrabbables.RemoveAt(index);
			}
		}

		private void CalculateTotalValue()
		{
			PingedScrapValue = 0;
			foreach (int currentScannedValue in _currentScannedValues)
			{
				PingedScrapValue += currentScannedValue;
			}
		}
	}
	[Initializer(0)]
	public class ScanNodeCreator : ComponentInjector<GrabbableObject>
	{
		private static readonly string[] excludes = new string[1] { "sticky note" };

		[Initializer(0)]
		private static void Init()
		{
			SceneInjection.AddComponentInjector<GrabbableObject>((ComponentInjector<GrabbableObject>)new ScanNodeCreator(), true);
		}

		protected override bool CanBeInjected(GrabbableObject component)
		{
			if (Object.op_Implicit((Object)(object)((Component)component).GetComponentInChildren<ScanNodeProperties>(true)))
			{
				return false;
			}
			if (component is RagdollGrabbableObject)
			{
				return false;
			}
			if (!Object.op_Implicit((Object)(object)component.itemProperties))
			{
				return false;
			}
			if (string.IsNullOrEmpty(component.itemProperties.itemName))
			{
				return true;
			}
			string[] array = excludes;
			foreach (string value in array)
			{
				if (component.itemProperties.itemName.Equals(value, StringComparison.InvariantCultureIgnoreCase))
				{
					return false;
				}
			}
			return true;
		}

		protected override void Inject(GrabbableObject component)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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)
			Transform transform = ((Component)component).transform;
			bool isScrap = component.itemProperties.isScrap;
			GameObject val = new GameObject("Scan Node")
			{
				layer = Layers.ScanNode
			};
			val.transform.SetParent(transform);
			val.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
			val.AddComponent<BoxCollider>().size = Vector3.one * 0.2f;
			CustomScanNodeProperties customScanNodeProperties = val.AddComponent<CustomScanNodeProperties>();
			((ScanNodeProperties)customScanNodeProperties).headerText = component.itemProperties.itemName;
			((ScanNodeProperties)customScanNodeProperties).subText = (isScrap ? $"Value: ${component.scrapValue}" : "???");
			((ScanNodeProperties)customScanNodeProperties).minRange = 1;
			((ScanNodeProperties)customScanNodeProperties).maxRange = 13;
			((ScanNodeProperties)customScanNodeProperties).creatureScanID = -1;
			((ScanNodeProperties)customScanNodeProperties).nodeType = (isScrap ? PingScan.NodeScrap : PingScan.NodeUtility);
			((ScanNodeProperties)customScanNodeProperties).requiresLineOfSight = true;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Saradora.ScanTweaks", "Scan Tweaks", "1.5.3")]
	public class PluginInitializer : BaseUnityPlugin
	{
		private readonly Harmony _harmonyInstance = new Harmony("Saradora.ScanTweaks");

		private void Awake()
		{
			ConfigBinder.BindAll(((BaseUnityPlugin)this).Config);
			_harmonyInstance.PatchAll();
		}
	}
	public static class ScanTweaks
	{
		public const string ModGuid = "Saradora.ScanTweaks";

		public const string ModVersion = "1.5.3";

		public const string ModName = "Scan Tweaks";
	}
}
namespace ScanTweaks.World
{
	public class ApparaticeScrapValue : MonoBehaviour
	{
		[ConfigDescription("Minimum possible scrap value of the apparatice. This value has a x0.4 multiplier in patch 47")]
		private static readonly ConfigData<int> ApparaticeMinValue = new ConfigData<int>(125);

		[ConfigDescription("Maximum possible scrap value of the apparatice. This value has a x0.4 multiplier in patch 47")]
		private static readonly ConfigData<int> ApparaticeMaxValue = new ConfigData<int>(350);

		private LungProp _lungProp;

		public static readonly List<LungProp> PatchedApparatices = new List<LungProp>();

		[ConfigSection("Apparatice")]
		[ConfigDescription("Make the apparatice have a random scrap value, also fixes the \"???\" on the scan node, Host-only")]
		public static ConfigData<bool> ApparaticeMakeRandomValue { get; } = new ConfigData<bool>(true);


		private void Awake()
		{
			_lungProp = ((Component)this).GetComponent<LungProp>();
		}

		private static bool IsLungPropValid(LungProp lungProp)
		{
			if (!lungProp.isLungDocked || !lungProp.isLungPowered)
			{
				return false;
			}
			return ((GrabbableObject)lungProp).isInFactory;
		}

		private IEnumerator Start()
		{
			yield return (object)new WaitForSeconds(5f);
			if (IsLungPropValid(_lungProp) && ConfigData<bool>.op_Implicit(ApparaticeMakeRandomValue) && Player.IsServerOrHost)
			{
				int num = ConfigData<int>.op_Implicit(ApparaticeMinValue);
				int num2 = ConfigData<int>.op_Implicit(ApparaticeMaxValue);
				if (num > num2)
				{
					int num3 = num2;
					num2 = num;
					num = num3;
				}
				int scrapValue = (int)((float)RoundManager.Instance.AnomalyRandom.Next(num, num2) * RoundManager.Instance.scrapValueMultiplier);
				((GrabbableObject)_lungProp).SetScrapValue(scrapValue);
				PatchedApparatices.Add(_lungProp);
			}
		}
	}
	[InjectToPrefab("LungApparatus")]
	public class ApparatusScrapValueInjector : IPrefabInjector
	{
		public void OnInject(GameObject obj)
		{
			if (ConfigData<bool>.op_Implicit(ApparaticeScrapValue.ApparaticeMakeRandomValue))
			{
				LungProp componentInChildren = obj.GetComponentInChildren<LungProp>();
				if ((Object)(object)componentInChildren == (Object)null)
				{
					Log.Error((object)"Couldn't find apparatus on prefab.");
				}
				else
				{
					((Component)componentInChildren).gameObject.AddComponent<ApparaticeScrapValue>();
				}
			}
		}
	}
	[InjectToPrefab("BreakerBox")]
	public class BreakerBoxPatcher : IPrefabInjector
	{
		public void OnInject(GameObject obj)
		{
			BreakerBox componentInChildren = obj.GetComponentInChildren<BreakerBox>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Log.Error((object)"Couldn't patch breaker box");
			}
			else
			{
				((Component)componentInChildren).gameObject.AddComponent<BreakerBoxScanNode>();
			}
		}
	}
	public class BreakerBoxScanNode : MonoBehaviour
	{
		[ConfigSection("BreakerBox")]
		[ConfigDescription("Adds a scan node to the breaker box")]
		private static readonly ConfigData<bool> _breakerBoxScanNode = new ConfigData<bool>(true);

		[ConfigDescription("Minimum range of the scan node")]
		private static readonly ConfigData<int> _breakerBoxMinRange = new ConfigData<int>(3);

		[ConfigDescription("Maximum range of the scan node")]
		private static readonly ConfigData<int> _breakerBoxMaxRange = new ConfigData<int>(13);

		private static readonly Vector3 Offset = new Vector3(-1f, -0.8f, -1.2f);

		private ScanNodeProperties _scanNode;

		private void Start()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			_breakerBoxScanNode.ConfigChanged += OnNodeActiveStateChanged;
			_breakerBoxMinRange.ConfigChanged += OnMinRangeChanged;
			_breakerBoxMaxRange.ConfigChanged += OnMaxRangeChanged;
			_scanNode = ((Component)this).GetComponentInChildren<ScanNodeProperties>(true);
			if (!Object.op_Implicit((Object)(object)_scanNode))
			{
				GameObject val = new GameObject("Scan Node")
				{
					layer = Layers.ScanNode
				};
				val.AddComponent<BoxCollider>().size = Vector3.one * 0.2f;
				_scanNode = val.AddComponent<ScanNodeProperties>();
				_scanNode.headerText = "Breaker Box";
				_scanNode.subText = "Flip the switches! Maybe it'll do something?";
				_scanNode.minRange = ConfigData<int>.op_Implicit(_breakerBoxMinRange);
				_scanNode.maxRange = ConfigData<int>.op_Implicit(_breakerBoxMaxRange);
				_scanNode.creatureScanID = -1;
				_scanNode.nodeType = 0;
				_scanNode.requiresLineOfSight = true;
				val.transform.SetParent(((Component)this).transform);
				val.transform.SetLocalPositionAndRotation(Offset, Quaternion.identity);
			}
			((Behaviour)_scanNode).enabled = ConfigData<bool>.op_Implicit(_breakerBoxScanNode);
		}

		private void OnNodeActiveStateChanged(bool value)
		{
			if (Object.op_Implicit((Object)(object)_scanNode))
			{
				((Behaviour)_scanNode).enabled = value;
			}
		}

		private void OnMinRangeChanged(int value)
		{
			if (Object.op_Implicit((Object)(object)_scanNode))
			{
				_scanNode.minRange = value;
			}
		}

		private void OnMaxRangeChanged(int value)
		{
			if (Object.op_Implicit((Object)(object)_scanNode))
			{
				_scanNode.maxRange = value;
			}
		}

		private void OnDestroy()
		{
			_breakerBoxScanNode.ConfigChanged -= OnNodeActiveStateChanged;
			_breakerBoxMinRange.ConfigChanged -= OnMinRangeChanged;
			_breakerBoxMaxRange.ConfigChanged -= OnMaxRangeChanged;
		}
	}
	[InjectToComponent(typeof(StartOfRound), false)]
	public class RadarIcons : MonoBehaviour
	{
		[ConfigSection("Radar")]
		[ConfigDescription("Fixes the item radar so that item icons properly disappear when the item itself is destroyed (e.g. when a player is eaten).")]
		public static readonly ConfigData<bool> RadarPatchRadarIcons = new ConfigData<bool>(true);

		private static readonly ConfigData<bool> ShouldScrapsInShipBeVisible = new ConfigData<bool>(false);

		private static List<(GrabbableObject, MeshRenderer)> _radarIconList = new List<(GrabbableObject, MeshRenderer)>();

		private static readonly Vector3 Offset = Vector3.up * 0.5f;

		private void Update()
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			for (int num = _radarIconList.Count - 1; num >= 0; num--)
			{
				var (val, val2) = _radarIconList[num];
				if ((Object)(object)val == (Object)null)
				{
					if (Object.op_Implicit((Object)(object)val2))
					{
						Object.Destroy((Object)(object)((Component)val2).gameObject);
					}
					_radarIconList.RemoveAt(num);
				}
				else if ((Object)(object)val2 == (Object)null)
				{
					_radarIconList.RemoveAt(num);
				}
				else
				{
					bool flag = !ConfigData<bool>.op_Implicit(ShouldScrapsInShipBeVisible) && val.isInShipRoom;
					if (((Renderer)val2).enabled = ((Component)val).gameObject.activeInHierarchy && !val.deactivated && (val.isHeld || !flag))
					{
						((Component)val2).transform.position = ((Component)val).transform.position + Offset;
					}
				}
			}
		}

		public static void AddRadarIcon(GrabbableObject grabbableObject, MeshRenderer radarIcon)
		{
			if (Object.op_Implicit((Object)(object)grabbableObject) && Object.op_Implicit((Object)(object)radarIcon))
			{
				_radarIconList.Add((grabbableObject, radarIcon));
			}
		}
	}
}
namespace ScanTweaks.UI
{
	[InjectToComponent(typeof(HUDManager), false)]
	public class PingScanUI : MonoBehaviour
	{
		private struct ScannedObject
		{
			public float distance;

			public UIElement element;

			public ScannedObject(float distance, UIElement element)
			{
				this.distance = distance;
				this.element = element;
			}
		}

		private class ScannedObjectComparer : IComparer<ScannedObject>
		{
			public int Compare(ScannedObject x, ScannedObject y)
			{
				if (x.distance > y.distance)
				{
					return -1;
				}
				return 1;
			}
		}

		private struct UIElement
		{
			public RectTransform transform;

			public TextMeshProUGUI mainText;

			public TextMeshProUGUI subText;

			public override int GetHashCode()
			{
				return ((object)transform).GetHashCode();
			}

			public override bool Equals(object obj)
			{
				return ((object)transform).Equals(obj);
			}
		}

		[ConfigSection("PingScan")]
		[ConfigDescription("The speed at which the scrap counter updates.")]
		private static ConfigData<int> ScrapCounterUpdateSpeed = new ConfigData<int>(1000);

		[SerializeField]
		private float _counterInterval = 0.03f;

		private float _nextCounterUpdate;

		private HUDManager _hudManager;

		private Canvas _canvas;

		private RectTransform _canvasTransform;

		private RectTransform _nodeContainer;

		private Queue<UIElement> _uiElementsPool = new Queue<UIElement>();

		private Dictionary<ScanNodeProperties, UIElement> _currentScanNodes = new Dictionary<ScanNodeProperties, UIElement>();

		private static readonly int AColorNumber = Animator.StringToHash("colorNumber");

		private static readonly int ADisplayBool = Animator.StringToHash("display");

		private static readonly string DollarSign = "$";

		private int _currentScrapValue;

		private readonly List<ScannedObject> _scannedObjects = new List<ScannedObject>();

		private readonly List<ScanNodeProperties> _toDelete = new List<ScanNodeProperties>();

		private readonly ScannedObjectComparer _distanceComparer = new ScannedObjectComparer();

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			_hudManager = ((Component)this).GetComponent<HUDManager>();
			_canvas = ((Component)_hudManager.scanElements[0]).GetComponentInParent<Canvas>(true);
			_canvasTransform = (RectTransform)((Component)_canvas).transform;
			_nodeContainer = (RectTransform)((Component)((Transform)_hudManager.scanElements[0]).parent).transform;
			for (int i = 1; i < _hudManager.scanElements.Length; i++)
			{
				_uiElementsPool.Enqueue(GetUIElementFromRectTransform(_hudManager.scanElements[i]));
			}
		}

		private void OnEnable()
		{
			PingScan.ScanNodeAdded += OnScanNodeAdded;
			PingScan.ScanNodeRemoved += OnScanNodeRemoved;
		}

		private void OnDisable()
		{
			PingScan.ScanNodeAdded -= OnScanNodeAdded;
			PingScan.ScanNodeRemoved -= OnScanNodeRemoved;
		}

		private void LateUpdate()
		{
			UpdateNodeContainer();
			UpdateScanNodes();
			UpdateScrapValueCounter();
		}

		private void UpdateNodeContainer()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			_nodeContainer.sizeDelta = Vector2.zero;
			((Transform)_nodeContainer).localPosition = Vector3.zero;
		}

		private void UpdateScanNodes()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB localPlayer = Player.LocalPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			Camera gameplayCamera = localPlayer.gameplayCamera;
			Vector3 position = ((Component)gameplayCamera).transform.position;
			_toDelete.Clear();
			_scannedObjects.Clear();
			Vector2 sizeDelta = _canvasTransform.sizeDelta;
			Vector2 val = Vector2.one * 0.5f;
			foreach (var (val3, element) in _currentScanNodes)
			{
				if (!Object.op_Implicit((Object)(object)val3))
				{
					_toDelete.Add(val3);
					continue;
				}
				((TMP_Text)element.mainText).text = val3.headerText;
				((TMP_Text)element.subText).text = val3.subText;
				Vector3 position2 = ((Component)val3).transform.position;
				float distance = Vector3.SqrMagnitude(position2 - position);
				_scannedObjects.Add(new ScannedObject(distance, element));
				Vector3 val4 = gameplayCamera.WorldToViewportPoint(position2);
				val4 = Vector2.op_Implicit((Vector2.op_Implicit(val4) - val) * sizeDelta);
				((Transform)element.transform).localPosition = val4;
			}
			_scannedObjects.Sort(_distanceComparer);
			foreach (ScanNodeProperties item in _toDelete)
			{
				OnScanNodeRemoved(item);
			}
			int count = _scannedObjects.Count;
			for (int i = 0; i < count; i++)
			{
				((Transform)_scannedObjects[i].element.transform).SetSiblingIndex(i);
			}
		}

		private void UpdateScrapValueCounter()
		{
			if (PingScan.PingedScrapValue != _currentScrapValue && !(Time.time < _nextCounterUpdate))
			{
				_nextCounterUpdate = Time.time + _counterInterval;
				if (!_hudManager.scanInfoAnimator.GetBool(ADisplayBool))
				{
					_hudManager.scanInfoAnimator.SetBool(ADisplayBool, true);
				}
				if (_currentScrapValue < PingScan.PingedScrapValue)
				{
					float num = ConfigData<int>.op_Implicit(ScrapCounterUpdateSpeed);
					int val = Math.Abs(PingScan.PingedScrapValue - _currentScrapValue);
					val = Math.Max(100, val);
					num *= (float)val * 0.01f;
					_currentScrapValue = Mathf.RoundToInt(Mathf.MoveTowards((float)_currentScrapValue, (float)PingScan.PingedScrapValue, num * Time.deltaTime));
					_hudManager.UIAudio.PlayOneShot(_hudManager.addToScrapTotalSFX);
				}
				else
				{
					_currentScrapValue = PingScan.PingedScrapValue;
				}
				((TMP_Text)_hudManager.totalValueText).text = DollarSign + _currentScrapValue;
				if (_currentScrapValue == 0)
				{
					_hudManager.scanInfoAnimator.SetBool(ADisplayBool, false);
				}
				else if (PingScan.PingedScrapValue == _currentScrapValue)
				{
					_hudManager.UIAudio.PlayOneShot(_hudManager.finishAddingToTotalSFX);
				}
			}
		}

		private UIElement GetUIElementFromRectTransform(RectTransform instance)
		{
			TextMeshProUGUI[] componentsInChildren = ((Component)((Component)instance).transform).GetComponentsInChildren<TextMeshProUGUI>(true);
			UIElement result = default(UIElement);
			result.transform = instance;
			result.mainText = componentsInChildren[0];
			result.subText = componentsInChildren[1];
			return result;
		}

		private UIElement GetFreeUIElement()
		{
			if (_uiElementsPool.Count > 0)
			{
				return _uiElementsPool.Dequeue();
			}
			RectTransform instance = Object.Instantiate<RectTransform>(_hudManager.scanElements[0], ((Component)_hudManager.scanElements[0]).transform.parent);
			return GetUIElementFromRectTransform(instance);
		}

		private void OnScanNodeAdded(ScanNodeProperties scanNode)
		{
			if (!_currentScanNodes.ContainsKey(scanNode))
			{
				UIElement freeUIElement = GetFreeUIElement();
				((Component)freeUIElement.transform).gameObject.SetActive(true);
				((TMP_Text)freeUIElement.mainText).text = scanNode.headerText;
				((TMP_Text)freeUIElement.subText).text = scanNode.subText;
				((Component)freeUIElement.transform).GetComponent<Animator>().SetInteger(AColorNumber, scanNode.nodeType);
				_currentScanNodes.Add(scanNode, freeUIElement);
			}
		}

		private void OnScanNodeRemoved(ScanNodeProperties scanNode)
		{
			if (_currentScanNodes.ContainsKey(scanNode))
			{
				UIElement item = _currentScanNodes[scanNode];
				((Component)item.transform).gameObject.SetActive(false);
				_currentScanNodes.Remove(scanNode);
				_uiElementsPool.Enqueue(item);
			}
		}
	}
}
namespace ScanTweaks.Patches
{
	[HarmonyPatch(typeof(GrabbableObject))]
	internal static class GrabbableObject_Patching
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void Start_PostFix(GrabbableObject __instance)
		{
			if (ConfigData<bool>.op_Implicit(RadarIcons.RadarPatchRadarIcons))
			{
				PatchRadarIcon(__instance);
			}
			PatchPropColliders(__instance);
		}

		private static void PatchRadarIcon(GrabbableObject grabbableObject)
		{
			if (Object.op_Implicit((Object)(object)grabbableObject.radarIcon) && !(grabbableObject is RagdollGrabbableObject))
			{
				RadarIcons.AddRadarIcon(grabbableObject, ((Component)grabbableObject.radarIcon).GetComponent<MeshRenderer>());
				((Component)grabbableObject.radarIcon).transform.SetParent(((Component)StartOfRound.Instance).transform);
				grabbableObject.radarIcon = null;
			}
		}

		private static void PatchPropColliders(GrabbableObject grabbableObject)
		{
			List<Collider> list = new List<Collider>();
			Collider[] propColliders = grabbableObject.propColliders;
			foreach (Collider val in propColliders)
			{
				if (((Component)val).gameObject.layer != Layers.ScanNode)
				{
					list.Add(val);
				}
			}
			grabbableObject.propColliders = list.ToArray();
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	internal static class HUDManager_Patching
	{
		private static bool _insideUpdate;

		[HarmonyPatch("CanPlayerScan")]
		[HarmonyPrefix]
		private static bool CanPlayerScan_Prefix(ref bool __result)
		{
			if (!ConfigData<bool>.op_Implicit(PingScan.PingScanDoPatch))
			{
				return true;
			}
			if (!_insideUpdate)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch("PingScan_performed")]
		[HarmonyPostfix]
		private static void PingScan_performed_Postfix(HUDManager __instance)
		{
			if (!((Object)(object)Player.LocalPlayer == (Object)null) && !(ReflectionUtility.GetField<float>((object)__instance, "playerPingingScan") < 0.3f))
			{
				PingScan.TriggerPingScan();
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Update_Prefix()
		{
			_insideUpdate = true;
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		private static void Update_Postfix()
		{
			_insideUpdate = false;
		}
	}
	[HarmonyPatch(typeof(RoundManager))]
	public static class RoundManager_Patching
	{
		[HarmonyPatch("waitForScrapToSpawnToSync")]
		[HarmonyPrefix]
		private static void waitForScrapToSpawnToSync_Prefix()
		{
			ApparaticeScrapValue.PatchedApparatices.Clear();
		}

		[HarmonyPatch("SyncScrapValuesClientRpc")]
		[HarmonyPrefix]
		private static void SyncScrapValuesClientRpc_Prefix(ref NetworkObjectReference[] spawnedScrap, ref int[] allScrapValue)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!Player.IsServerOrHost)
			{
				return;
			}
			List<NetworkObjectReference> list = spawnedScrap.ToList();
			List<int> list2 = allScrapValue.ToList();
			foreach (LungProp patchedApparatice in ApparaticeScrapValue.PatchedApparatices)
			{
				if (!((Object)(object)patchedApparatice == (Object)null))
				{
					NetworkObjectReference item = NetworkObjectReference.op_Implicit(((NetworkBehaviour)patchedApparatice).NetworkObject);
					if (!list.Contains(item))
					{
						list.Add(item);
						list2.Add(((GrabbableObject)patchedApparatice).scrapValue);
					}
				}
			}
			spawnedScrap = list.ToArray();
			allScrapValue = list2.ToArray();
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	public static class StartOfRound_Patching
	{
		[ConfigSection("Patches")]
		[ConfigDescription("Patches the loot not being properly registered as inside the ship when joining a game as client.")]
		public static readonly ConfigData<bool> PatchLootInShip = new ConfigData<bool>(true);

		[HarmonyPatch("SyncAlreadyHeldObjectsServerRpc")]
		[HarmonyPrefix]
		private static void SyncAlreadyHeldObjectsServerRpc_Prefix(int joiningClientId)
		{
			if ((int)NetworkManager.Singleton.LocalClientId != joiningClientId)
			{
				return;
			}
			try
			{
				GameObject val = GameObject.Find("/Environment/HangarShip");
				((MonoBehaviour)val.GetComponent<ElevatorAnimationEvents>()).StartCoroutine(WaitForSyncItems(val));
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
			}
		}

		private static IEnumerator WaitForSyncItems(GameObject shipObject)
		{
			yield return (object)new WaitForSeconds(2f);
			if (!ConfigData<bool>.op_Implicit(PatchLootInShip))
			{
				yield break;
			}
			GrabbableObject[] componentsInChildren = shipObject.GetComponentsInChildren<GrabbableObject>();
			foreach (GrabbableObject val in componentsInChildren)
			{
				if (!val.isInShipRoom)
				{
					val.scrapPersistedThroughRounds = true;
					GameNetworkManager.Instance.localPlayerController.SetItemInElevator(true, true, val);
				}
			}
		}
	}
}