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();
};
}
}
}