Decompiled source of ZoneScan v1.0.1

plugins/ZoneScan/ZoneScan.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.Json;
using AIGraph;
using Agents;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using ChainedPuzzles;
using GTFO.API;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using MTFO.API;
using Microsoft.CodeAnalysis;
using Player;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ZoneScan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ZoneScan")]
[assembly: AssemblyTitle("ZoneScan")]
[assembly: AssemblyVersion("1.0.0.0")]
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;
		}
	}
}
public static class ZoneScanLog
{
	private static readonly ManualLogSource logger;

	static ZoneScanLog()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Expected O, but got Unknown
		logger = new ManualLogSource("ZoneScan");
		Logger.Sources.Add((ILogSource)(object)logger);
	}

	public static void Message(object msg)
	{
		logger.LogMessage(msg);
	}

	public static void Info(object msg)
	{
		logger.LogInfo(msg);
	}

	public static void Warn(object msg)
	{
		logger.LogWarning(msg);
	}

	public static void Error(object msg)
	{
		logger.LogError(msg);
	}

	public static void Debug(object msg)
	{
		logger.LogDebug(msg);
	}
}
namespace ZoneScan
{
	[HarmonyPatch(typeof(CP_Bioscan_Core), "Activate")]
	public static class GetPersistentID
	{
		public static readonly Dictionary<IntPtr, uint> ScannerPersistentIDs = new Dictionary<IntPtr, uint>();

		public static HashSet<uint> AllowedPersistentIDs => new HashSet<uint>(ZoneScanConfigLoader.Settings.AllowedPersistentIDs.Select((ScanConfigEntry e) => e.ID));

		public static Dictionary<uint, bool> UseZoneInsteadOfNodeByID => ZoneScanConfigLoader.Settings.AllowedPersistentIDs.ToDictionary((ScanConfigEntry e) => e.ID, (ScanConfigEntry e) => e.UseZoneInsteadOfNode);

		private static void Postfix(CP_Bioscan_Core __instance)
		{
			uint num = 0u;
			ChainedPuzzleInstance val = ((Il2CppObjectBase)__instance.m_owner).TryCast<ChainedPuzzleInstance>();
			if (val != null)
			{
				num = ((GameDataBlockBase<ChainedPuzzleDataBlock>)(object)val.Data).persistentID;
			}
			if (num == 0)
			{
				CP_Cluster_Core val2 = ((Il2CppObjectBase)__instance.m_owner).TryCast<CP_Cluster_Core>();
				if (val2 != null)
				{
					ChainedPuzzleInstance val3 = ((Il2CppObjectBase)val2.m_owner).TryCast<ChainedPuzzleInstance>();
					if (val3 != null)
					{
						num = ((GameDataBlockBase<ChainedPuzzleDataBlock>)(object)val3.Data).persistentID;
					}
				}
			}
			if (num != 0)
			{
				CP_PlayerScanner component = ((Component)__instance).GetComponent<CP_PlayerScanner>();
				if ((Object)(object)component != (Object)null)
				{
					ScannerPersistentIDs[((Il2CppObjectBase)component).Pointer] = num;
				}
			}
		}
	}
	[HarmonyPatch(typeof(CP_Bioscan_Hud), "Update")]
	public static class ItemHudHandler
	{
		public static bool Prefix(CP_Bioscan_Hud __instance)
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Invalid comparison between Unknown and I4
			if (!__instance.m_visible || !__instance.m_reqItemsEnabled)
			{
				return true;
			}
			CP_Bioscan_Core componentInParent = ((Component)__instance).GetComponentInParent<CP_Bioscan_Core>();
			if ((Object)(object)componentInParent == (Object)null || componentInParent.CourseNode == null)
			{
				return true;
			}
			CP_PlayerScanner component = ((Component)componentInParent).GetComponent<CP_PlayerScanner>();
			if ((Object)(object)component == (Object)null || component.m_reqItems == null)
			{
				return true;
			}
			if (!GetPersistentID.ScannerPersistentIDs.TryGetValue(((Il2CppObjectBase)component).Pointer, out var value))
			{
				return true;
			}
			if (!GetPersistentID.AllowedPersistentIDs.Contains(value))
			{
				return true;
			}
			bool value2;
			bool flag = GetPersistentID.UseZoneInsteadOfNodeByID.TryGetValue(value, out value2) && value2;
			AIG_CourseNode courseNode = componentInParent.CourseNode;
			LG_Zone zone = courseNode.m_zone;
			if (__instance.m_reqItemStatus == null || __instance.m_reqItemNames == null)
			{
				return true;
			}
			for (int i = 0; i < ((Il2CppArrayBase<bool>)(object)__instance.m_reqItemStatus).Length; i++)
			{
				((Il2CppArrayBase<bool>)(object)__instance.m_reqItemStatus)[i] = false;
			}
			for (int j = 0; j < ((Il2CppArrayBase<iWardenObjectiveItem>)(object)component.m_reqItems).Length; j++)
			{
				iWardenObjectiveItem val = ((Il2CppArrayBase<iWardenObjectiveItem>)(object)component.m_reqItems)[j];
				if (val == null)
				{
					continue;
				}
				bool flag2 = false;
				if ((int)val.PickupItemStatus == 0)
				{
					ItemInLevel val2 = ((val != null) ? ((Il2CppObjectBase)val).TryCast<ItemInLevel>() : null);
					if ((Object)(object)val2 != (Object)null)
					{
						if (!flag)
						{
							flag2 = courseNode.m_itemsInNode.Contains(val2);
						}
						else
						{
							Enumerator<AIG_CourseNode> enumerator = zone.m_courseNodes.GetEnumerator();
							while (enumerator.MoveNext())
							{
								if (enumerator.Current.m_itemsInNode.Contains(val2))
								{
									flag2 = true;
									break;
								}
							}
						}
					}
				}
				else if ((int)val.PickupItemStatus == 1 && (Object)(object)val.PickedUpByPlayer != (Object)null)
				{
					PlayerAgent pickedUpByPlayer = val.PickedUpByPlayer;
					if (((Agent)pickedUpByPlayer).CourseNode != null)
					{
						flag2 = (flag ? (((Il2CppObjectBase)((Agent)pickedUpByPlayer).CourseNode.m_zone).Pointer == ((Il2CppObjectBase)zone).Pointer) : (((Il2CppObjectBase)((Agent)pickedUpByPlayer).CourseNode).Pointer == ((Il2CppObjectBase)courseNode).Pointer));
					}
				}
				((Il2CppArrayBase<bool>)(object)__instance.m_reqItemStatus)[j] = flag2;
			}
			__instance.m_progressTarget = component.m_scanProgression;
			__instance.m_progressSmooth = component.m_scanProgression;
			__instance.m_localPlayerInScan = true;
			__instance.m_isClosestToPlayer = true;
			return true;
		}
	}
	[HarmonyPatch(typeof(CP_PlayerScanner), "StartScan")]
	public static class PlayerDetection
	{
		[CompilerGenerated]
		private sealed class <OverrideScanPlayerListRoutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public CP_PlayerScanner scanner;

			private bool <useZone>5__2;

			private AIG_CourseNode <node>5__3;

			private LG_Zone <zone>5__4;

			private List<PlayerAgent> <playersInArea>5__5;

			private float <scanProgress>5__6;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <OverrideScanPlayerListRoutine>d__4(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<node>5__3 = null;
				<zone>5__4 = null;
				<playersInArea>5__5 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_019a: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_0216: Unknown result type (might be due to invalid IL or missing references)
				//IL_0296: Unknown result type (might be due to invalid IL or missing references)
				//IL_029c: Invalid comparison between Unknown and I4
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					ZoneScanLog.Info("Scan override routine started");
					if (!GetPersistentID.ScannerPersistentIDs.TryGetValue(((Il2CppObjectBase)scanner).Pointer, out var value))
					{
						return false;
					}
					if (!GetPersistentID.AllowedPersistentIDs.Contains(value))
					{
						return false;
					}
					<useZone>5__2 = GetPersistentID.UseZoneInsteadOfNodeByID.TryGetValue(value, out var value2) && value2;
					CP_Bioscan_Core component = ((Component)scanner).GetComponent<CP_Bioscan_Core>();
					if ((Object)(object)component == (Object)null || component.CourseNode == null)
					{
						ZoneScanLog.Warn("CP_Bioscan_Core or its CourseNode is null.");
						ScannersAlreadyHooked.Remove(((Il2CppObjectBase)scanner).Pointer);
						return false;
					}
					<node>5__3 = component.CourseNode;
					<zone>5__4 = <node>5__3.m_zone;
					<playersInArea>5__5 = new List<PlayerAgent>();
					<scanProgress>5__6 = 0f;
					break;
				}
				case 1:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)scanner != (Object)null && ((Behaviour)scanner).isActiveAndEnabled && ((Component)scanner).gameObject.activeInHierarchy && scanner.m_scanActive)
				{
					<playersInArea>5__5.Clear();
					List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
					Enumerator<PlayerAgent> enumerator = playerAgentsInLevel.GetEnumerator();
					while (enumerator.MoveNext())
					{
						PlayerAgent current = enumerator.Current;
						if (!((Object)(object)current == (Object)null) && ((Agent)current).Alive && ((Agent)current).CourseNode != null && (<useZone>5__2 ? (((Il2CppObjectBase)((Agent)current).CourseNode.m_zone).Pointer == ((Il2CppObjectBase)<zone>5__4).Pointer) : (((Il2CppObjectBase)((Agent)current).CourseNode).Pointer == ((Il2CppObjectBase)<node>5__3).Pointer)))
						{
							<playersInArea>5__5.Add(current);
						}
					}
					int count = playerAgentsInLevel.Count;
					int count2 = <playersInArea>5__5.Count;
					bool flag = (Extensions.RequireAllPlayers(scanner.ScanPlayersRequired) ? (count2 == count) : (Extensions.RequireSoloPlayer(scanner.ScanPlayersRequired) ? (count2 == 1) : (count2 > 0)));
					bool flag2 = true;
					if (scanner.m_reqItemsEnabled && scanner.m_reqItems != null)
					{
						foreach (iWardenObjectiveItem item in (Il2CppArrayBase<iWardenObjectiveItem>)(object)scanner.m_reqItems)
						{
							bool flag3 = false;
							if ((int)item.PickupItemStatus == 0)
							{
								ItemInLevel val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<ItemInLevel>() : null);
								if ((Object)(object)val != (Object)null)
								{
									if (!<useZone>5__2)
									{
										flag3 = <node>5__3.m_itemsInNode.Contains(val);
									}
									else
									{
										Enumerator<AIG_CourseNode> enumerator3 = <zone>5__4.m_courseNodes.GetEnumerator();
										while (enumerator3.MoveNext())
										{
											if (enumerator3.Current.m_itemsInNode.Contains(val))
											{
												flag3 = true;
												break;
											}
										}
									}
								}
							}
							else if ((int)item.PickupItemStatus == 1 && (Object)(object)item.PickedUpByPlayer != (Object)null)
							{
								PlayerAgent pickedUpByPlayer = item.PickedUpByPlayer;
								if (((Agent)pickedUpByPlayer).CourseNode != null)
								{
									flag3 = (<useZone>5__2 ? (((Il2CppObjectBase)((Agent)pickedUpByPlayer).CourseNode.m_zone).Pointer == ((Il2CppObjectBase)<zone>5__4).Pointer) : (((Il2CppObjectBase)((Agent)pickedUpByPlayer).CourseNode).Pointer == ((Il2CppObjectBase)<node>5__3).Pointer));
								}
							}
							if (!flag3)
							{
								flag2 = false;
								break;
							}
						}
					}
					if (flag && count2 > 0 && flag2)
					{
						float num = ((Il2CppArrayBase<float>)(object)scanner.m_scanSpeeds)[Mathf.Clamp(count2 - 1, 0, ((Il2CppArrayBase<float>)(object)scanner.m_scanSpeeds).Length - 1)];
						float num2 = 1f;
						enumerator = <playersInArea>5__5.GetEnumerator();
						while (enumerator.MoveNext())
						{
							PlayerAgent current3 = enumerator.Current;
							num2 += AgentModifierManager.ApplyModifier((Agent)(object)current3, (AgentModifier)157, 1f) - 1f;
						}
						float num3 = num * Mathf.Max(num2, 0.1f);
						<scanProgress>5__6 = Mathf.Min(<scanProgress>5__6 + Clock.Delta * num3, 1f);
						scanner.m_scanProgression = <scanProgress>5__6;
						scanner.m_scanProgressionLast = <scanProgress>5__6;
					}
					else if (scanner.m_reduceWhenNoPlayer)
					{
						<scanProgress>5__6 = Mathf.Max(<scanProgress>5__6 - Clock.Delta * scanner.m_reduceSpeed, 0f);
						scanner.m_scanProgression = <scanProgress>5__6;
						scanner.m_scanProgressionLast = <scanProgress>5__6;
					}
					scanner.OnScanStateChanged?.Invoke(<scanProgress>5__6, <playersInArea>5__5, count, Il2CppStructArray<bool>.op_Implicit(new bool[0]));
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				ScannersAlreadyHooked.Remove(((Il2CppObjectBase)scanner).Pointer);
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <WaitForScanToBecomeActive>d__3 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public CP_PlayerScanner scanner;

			private float <timeout>5__2;

			private float <startTime>5__3;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <WaitForScanToBecomeActive>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					ZoneScanLog.Info("StartScan called. Waiting for m_scanActive == true...");
					<timeout>5__2 = 5f;
					<startTime>5__3 = Time.realtimeSinceStartup;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (Time.realtimeSinceStartup - <startTime>5__3 < <timeout>5__2)
				{
					if (scanner.m_scanActive)
					{
						((MonoBehaviour)scanner).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(OverrideScanPlayerListRoutine(scanner)));
						return false;
					}
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				ZoneScanLog.Warn("Scan never became active within 5 seconds, aborting scan override.");
				ScannersAlreadyHooked.Remove(((Il2CppObjectBase)scanner).Pointer);
				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();
			}
		}

		public const bool debugLogs = false;

		private static readonly HashSet<IntPtr> ScannersAlreadyHooked = new HashSet<IntPtr>();

		[HarmonyPostfix]
		public static void Postfix(CP_PlayerScanner __instance)
		{
			IntPtr pointer = ((Il2CppObjectBase)__instance).Pointer;
			if (ScannersAlreadyHooked.Add(pointer))
			{
				((MonoBehaviour)__instance).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(WaitForScanToBecomeActive(__instance)));
			}
		}

		[IteratorStateMachine(typeof(<WaitForScanToBecomeActive>d__3))]
		private static IEnumerator WaitForScanToBecomeActive(CP_PlayerScanner scanner)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitForScanToBecomeActive>d__3(0)
			{
				scanner = scanner
			};
		}

		[IteratorStateMachine(typeof(<OverrideScanPlayerListRoutine>d__4))]
		private static IEnumerator OverrideScanPlayerListRoutine(CP_PlayerScanner scanner)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OverrideScanPlayerListRoutine>d__4(0)
			{
				scanner = scanner
			};
		}
	}
	public static class ZoneScanConfigLoader
	{
		private const string FolderName = "ZoneScanConfig";

		private const string FileName = "ZoneScanSettings.json";

		public static ZoneScanSettings? Settings { get; private set; }

		public static void Load()
		{
			if (!MTFOPathAPI.HasCustomPath)
			{
				ZoneScanLog.Warn("MTFO CustomPath is unavailable. Falling back to plugin folder.");
				return;
			}
			string text = Path.Combine(MTFOPathAPI.CustomPath, "ZoneScanConfig");
			string text2 = Path.Combine(text, "ZoneScanSettings.json");
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			if (!File.Exists(text2))
			{
				Settings = new ZoneScanSettings
				{
					AllowedPersistentIDs = new List<ScanConfigEntry>
					{
						new ScanConfigEntry
						{
							ID = 8000u,
							UseZoneInsteadOfNode = false
						},
						new ScanConfigEntry
						{
							ID = 8001u,
							UseZoneInsteadOfNode = true
						}
					}
				};
				Save(text2);
				ZoneScanLog.Info("Created default ZoneScan config at " + text2);
				return;
			}
			try
			{
				Settings = JsonSerializer.Deserialize<ZoneScanSettings>(File.ReadAllText(text2, Encoding.UTF8));
				ZoneScanLog.Info("ZoneScan config loaded.");
			}
			catch (Exception ex)
			{
				ZoneScanLog.Warn("Failed to load config: " + ex.Message);
				Settings = new ZoneScanSettings();
			}
		}

		public static void Save(string configPath)
		{
			string contents = JsonSerializer.Serialize(Settings, new JsonSerializerOptions
			{
				WriteIndented = true
			});
			File.WriteAllText(configPath, contents, Encoding.UTF8);
		}
	}
	public class ZoneScanSettings
	{
		public List<ScanConfigEntry> AllowedPersistentIDs { get; set; } = new List<ScanConfigEntry>();

	}
	public class ScanConfigEntry
	{
		public uint ID { get; set; }

		public bool UseZoneInsteadOfNode { get; set; }
	}
	[BepInPlugin("randomguy0753.ZoneScan", "ZoneScan", "1.0.0")]
	public class Plugin : BasePlugin
	{
		private Harmony? _harmony;

		public override void Load()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			ZoneScanLog.Info("ZoneScan loaded");
			_harmony = new Harmony("randomguy0753.ZoneScan");
			EventAPI.OnManagersSetup += delegate
			{
				ZoneScanConfigLoader.Load();
				ZoneScanLog.Info("Applying patches");
				_harmony.PatchAll();
			};
		}
	}
}