Decompiled source of CoordinateTriggerEvents v1.6.8

CoordinateTriggerEvents_v1.6.8.dll

Decompiled a month 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.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using AIGraph;
using Agents;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using ChainedPuzzles;
using GTFO.API;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using Localization;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 CoordinateTriggerEvents
{
	internal static class ConfigManager
	{
		internal const string ConfigFolderName = "CoordinateTriggerEvents";

		internal const string TemplateChineseFileName = "Template_CN.json";

		internal const string TemplateEnglishFileName = "Template_EN.json";

		internal static readonly List<ConfigDocument> Configs = new List<ConfigDocument>();

		private static readonly object LockObject = new object();

		private static string _pluginDir = string.Empty;

		private static readonly Dictionary<string, DateTime> LastWriteTimes = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, FileSystemWatcher> ConfigWatchers = new Dictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase);

		private static readonly TimeSpan ReloadDebounceDelay = TimeSpan.FromMilliseconds(350.0);

		private static bool _reloadQueued;

		private static DateTime _reloadNotBeforeUtc = DateTime.MinValue;

		private static string _reloadReason = string.Empty;

		private const float ContinuousTriggerMinimumCooldown = 1f;

		internal static string ConfigPathSummary
		{
			get
			{
				lock (LockObject)
				{
					return (Configs.Count == 0) ? "<none>" : string.Join(" | ", Configs.Select((ConfigDocument c) => c.FilePath));
				}
			}
		}

		internal static void LoadOrCreate(ManualLogSource? log, bool force)
		{
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			lock (LockObject)
			{
				string pluginPath = Paths.PluginPath;
				_pluginDir = GetPluginDirectory();
				Directory.CreateDirectory(pluginPath);
				CleanupLegacyPluginLocalTemplates(log);
				EnsureTemplateConfigs(log);
				List<string> list = GetConfigSearchRoots(log).Where(Directory.Exists).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
				EnsureConfigWatchers(list, log);
				List<string> list2 = (from p in list.SelectMany((string root) => Directory.GetFiles(root, "*.json", SearchOption.AllDirectories))
					where IsCoordinateTriggerConfigPath(p)
					select p).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
				bool flag = RefreshFileSnapshot(list2);
				if (!(force || flag))
				{
					return;
				}
				Configs.Clear();
				bool flag2 = default(bool);
				foreach (string item in list2)
				{
					try
					{
						ConfigDocument configDocument = ParseConfig(item);
						if (configDocument != null)
						{
							Configs.Add(configDocument);
							Runtime.LogVerbose($"Loaded config: {item} | positionTriggers={configDocument.PositionTriggers.Count}, scanTriggers={configDocument.ScanTriggers.Count}, interactTriggers={configDocument.InteractTriggers.Count}");
							LogTriggerEnabledStates(log, configDocument);
						}
					}
					catch (Exception ex)
					{
						if (log != null)
						{
							BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(28, 3, ref flag2);
							if (flag2)
							{
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to load config '");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
							}
							log.LogError(val);
						}
					}
				}
				Runtime.ClearConfigurationResolutionCaches();
				Runtime.MarkActiveTriggerCacheDirty();
			}
		}

		private static void LogTriggerEnabledStates(ManualLogSource? log, ConfigDocument doc)
		{
			foreach (PositionTriggerRule positionTrigger in doc.PositionTriggers)
			{
				Runtime.LogVerbose($"Loaded position trigger '{positionTrigger.ID}' Enabled={positionTrigger.Enabled}{(positionTrigger.Enabled ? string.Empty : " (skipped until enabled)")}");
			}
			foreach (ScanTriggerRule scanTrigger in doc.ScanTriggers)
			{
				Runtime.LogVerbose($"Loaded scan trigger '{scanTrigger.ID}' Enabled={scanTrigger.Enabled}{(scanTrigger.Enabled ? string.Empty : " (skipped until enabled)")}");
			}
			foreach (InteractTriggerRule interactTrigger in doc.InteractTriggers)
			{
				Runtime.LogVerbose($"Loaded interact trigger '{interactTrigger.ID}' Enabled={interactTrigger.Enabled}{(interactTrigger.Enabled ? string.Empty : " (skipped until enabled)")}");
			}
		}

		internal static void ProcessQueuedReload(ManualLogSource? log)
		{
			string reloadReason;
			lock (LockObject)
			{
				if (!_reloadQueued || DateTime.UtcNow < _reloadNotBeforeUtc)
				{
					return;
				}
				_reloadQueued = false;
				reloadReason = _reloadReason;
				_reloadReason = string.Empty;
			}
			Runtime.LogVerbose("Config file save detected; reloading CTE configs. Reason=" + reloadReason);
			LoadOrCreate(log, force: true);
		}

		private static void QueueReloadFromWatcher(string reason)
		{
			lock (LockObject)
			{
				_reloadQueued = true;
				_reloadNotBeforeUtc = DateTime.UtcNow + ReloadDebounceDelay;
				_reloadReason = reason;
			}
		}

		private static bool RefreshFileSnapshot(List<string> files)
		{
			bool result = files.Count != LastWriteTimes.Count;
			HashSet<string> hashSet = new HashSet<string>(files, StringComparer.OrdinalIgnoreCase);
			foreach (string file in files)
			{
				DateTime dateTime = SafeGetLastWriteTimeUtc(file);
				if (!LastWriteTimes.TryGetValue(file, out var value) || value != dateTime)
				{
					result = true;
					LastWriteTimes[file] = dateTime;
				}
			}
			foreach (string item in LastWriteTimes.Keys.ToList())
			{
				if (!hashSet.Contains(item))
				{
					result = true;
					LastWriteTimes.Remove(item);
				}
			}
			return result;
		}

		private static DateTime SafeGetLastWriteTimeUtc(string file)
		{
			try
			{
				return File.Exists(file) ? File.GetLastWriteTimeUtc(file) : DateTime.MinValue;
			}
			catch
			{
				return DateTime.MinValue;
			}
		}

		private static void EnsureConfigWatchers(List<string> customRoots, ManualLogSource? log)
		{
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string customRoot in customRoots)
			{
				string path = Path.Combine(customRoot, "CoordinateTriggerEvents");
				if (Directory.Exists(path))
				{
					hashSet.Add(Path.GetFullPath(path));
				}
			}
			foreach (string item in ConfigWatchers.Keys.ToList())
			{
				if (!hashSet.Contains(item))
				{
					try
					{
						ConfigWatchers[item].EnableRaisingEvents = false;
						ConfigWatchers[item].Dispose();
					}
					catch
					{
					}
					ConfigWatchers.Remove(item);
					Runtime.LogVerbose("Stopped CTE config watcher: " + item);
				}
			}
			bool flag = default(bool);
			foreach (string item2 in hashSet)
			{
				if (ConfigWatchers.ContainsKey(item2))
				{
					continue;
				}
				try
				{
					FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(item2, "*.json")
					{
						IncludeSubdirectories = true,
						NotifyFilter = (NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime)
					};
					fileSystemWatcher.Changed += OnConfigFileChanged;
					fileSystemWatcher.Created += OnConfigFileChanged;
					fileSystemWatcher.Deleted += OnConfigFileChanged;
					fileSystemWatcher.Renamed += OnConfigFileRenamed;
					fileSystemWatcher.Error += OnConfigWatcherError;
					fileSystemWatcher.EnableRaisingEvents = true;
					ConfigWatchers[item2] = fileSystemWatcher;
					Runtime.LogVerbose("Started CTE config watcher: " + item2);
				}
				catch (Exception ex)
				{
					if (log != null)
					{
						BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(45, 3, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to start CTE config watcher for '");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item2);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
						}
						log.LogWarning(val);
					}
				}
			}
		}

		private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
		{
			if (IsCoordinateTriggerConfigPath(e.FullPath))
			{
				QueueReloadFromWatcher($"{e.ChangeType}: {e.FullPath}");
			}
		}

		private static void OnConfigFileRenamed(object sender, RenamedEventArgs e)
		{
			if (IsCoordinateTriggerConfigPath(e.FullPath) || IsCoordinateTriggerConfigPath(e.OldFullPath))
			{
				QueueReloadFromWatcher("Renamed: " + e.OldFullPath + " -> " + e.FullPath);
			}
		}

		private static void OnConfigWatcherError(object sender, ErrorEventArgs e)
		{
			QueueReloadFromWatcher("WatcherError: " + e.GetException().GetType().Name);
		}

		internal static bool ShouldDumpRuntimeIndexes()
		{
			lock (LockObject)
			{
				return Configs.Any((ConfigDocument c) => c.Enabled && c.Debug.Enabled && c.Debug.DumpRuntimeIndexes);
			}
		}

		private static void EnsureTemplateConfigs(ManualLogSource? log)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			try
			{
				if (!TryGetMtfoCustomPath(out string customPath) || string.IsNullOrWhiteSpace(customPath))
				{
					if (log != null)
					{
						log.LogWarning((object)"MTFO CustomPath is not available; CTE template configs were not generated. Load a custom rundown through MTFO first.");
					}
					return;
				}
				string text = Path.Combine(customPath, "CoordinateTriggerEvents");
				Directory.CreateDirectory(text);
				WriteFeatureTemplatePair(Path.Combine(text, "PositionTriggers"), CreateChinesePositionTemplateJson(), CreateEnglishPositionTemplateJson(), log);
				WriteFeatureTemplatePair(Path.Combine(text, "ScanTriggers"), CreateChineseScanTemplateJson(), CreateEnglishScanTemplateJson(), log);
				WriteFeatureTemplatePair(Path.Combine(text, "InteractTriggers", "BigPickup"), CreateChineseBigPickupTemplateJson(), CreateEnglishBigPickupTemplateJson(), log);
				WriteFeatureTemplatePair(Path.Combine(text, "HudInteractTriggers"), CreateChineseHudInteractTemplateJson(), CreateEnglishHudInteractTemplateJson(), log);
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to ensure CTE template configs: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					log.LogWarning(val);
				}
			}
		}

		private static void WriteFeatureTemplatePair(string folder, string chineseTemplate, string englishTemplate, ManualLogSource? log)
		{
			WriteTemplateIfMissing(Path.Combine(folder, "Template_CN.json"), chineseTemplate, log);
			WriteTemplateIfMissing(Path.Combine(folder, "Template_EN.json"), englishTemplate, log);
		}

		private static IEnumerable<string> GetConfigSearchRoots(ManualLogSource? log)
		{
			if (TryGetMtfoCustomPath(out string customPath) && !string.IsNullOrWhiteSpace(customPath))
			{
				yield return customPath;
			}
			else if (log != null)
			{
				log.LogWarning((object)"MTFO CustomPath is not available; CTE will not read plugin-local Custom fallback configs to avoid duplicate configuration loading.");
			}
		}

		private static void CleanupLegacyPluginLocalTemplates(ManualLogSource? log)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			try
			{
				string text = Path.Combine(_pluginDir, "Custom", "CoordinateTriggerEvents");
				if (!Directory.Exists(text))
				{
					return;
				}
				string[] array = new string[2] { "Template_CN.json", "Template_EN.json" };
				foreach (string path in array)
				{
					string text2 = Path.Combine(text, path);
					if (File.Exists(text2))
					{
						File.Delete(text2);
						Runtime.LogVerbose("Deleted legacy plugin-local template config: " + text2);
					}
				}
				if (!Directory.EnumerateFileSystemEntries(text).Any())
				{
					Directory.Delete(text);
					string directoryName = Path.GetDirectoryName(text);
					if (Directory.Exists(directoryName) && !Directory.EnumerateFileSystemEntries(directoryName).Any())
					{
						Directory.Delete(directoryName);
					}
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(53, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to clean legacy plugin-local CTE templates: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					log.LogWarning(val);
				}
			}
		}

		private static bool TryGetMtfoCustomPath(out string customPath)
		{
			customPath = string.Empty;
			try
			{
				if (!((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("com.dak.MTFO", out var value))
				{
					return false;
				}
				Assembly assembly = ((value == null) ? null : value.Instance?.GetType()?.Assembly);
				if (assembly == null)
				{
					return false;
				}
				Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "ConfigManager");
				if (type == null)
				{
					return false;
				}
				FieldInfo field = type.GetField("CustomPath", BindingFlags.Static | BindingFlags.Public);
				FieldInfo field2 = type.GetField("HasCustomContent", BindingFlags.Static | BindingFlags.Public);
				if (field2 != null)
				{
					object value2 = field2.GetValue(null);
					if (value2 is bool && !(bool)value2)
					{
						return false;
					}
				}
				if (field?.GetValue(null) is string text && !string.IsNullOrWhiteSpace(text))
				{
					customPath = text;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void WriteTemplateIfMissing(string path, string content, ManualLogSource? log)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (!ShouldRewriteTemplate(path, out string reason))
			{
				return;
			}
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			File.WriteAllText(path, content, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			if (reason == "missing")
			{
				Runtime.LogVerbose("Created template config: " + path);
			}
			else if (log != null)
			{
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(47, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Replacing invalid or outdated template config: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(path);
				}
				log.LogWarning(val);
			}
		}

		private static bool ShouldRewriteTemplate(string path, out string reason)
		{
			reason = "missing";
			if (!File.Exists(path))
			{
				return true;
			}
			try
			{
				string text = File.ReadAllText(path, Encoding.UTF8);
				if (!IsJsoncParseable(text))
				{
					reason = "invalid";
					return true;
				}
				if (!text.Contains("CoordinateTriggerEvents 1.6.8", StringComparison.Ordinal))
				{
					reason = "outdated";
					return true;
				}
			}
			catch
			{
				reason = "invalid";
				return true;
			}
			reason = "current";
			return false;
		}

		private static bool IsJsoncParseable(string content)
		{
			try
			{
				JsonDocumentOptions options = new JsonDocumentOptions
				{
					CommentHandling = JsonCommentHandling.Skip,
					AllowTrailingCommas = true
				};
				using (JsonDocument.Parse(content, options))
				{
					return true;
				}
			}
			catch
			{
				return false;
			}
		}

		private static bool IsCoordinateTriggerConfigPath(string path)
		{
			string text = path.Replace('\\', '/');
			if (text.Contains("/Custom/CoordinateTriggerEvents/", StringComparison.OrdinalIgnoreCase))
			{
				return text.EndsWith(".json", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static string GetPluginDirectory()
		{
			try
			{
				string location = Assembly.GetExecutingAssembly().Location;
				if (!string.IsNullOrWhiteSpace(location))
				{
					string directoryName = Path.GetDirectoryName(location);
					if (!string.IsNullOrWhiteSpace(directoryName))
					{
						return directoryName;
					}
				}
			}
			catch
			{
			}
			return Path.Combine(Paths.PluginPath, "CoordinateTriggerEvents");
		}

		private static ConfigDocument? ParseConfig(string file)
		{
			using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(file), new JsonDocumentOptions
			{
				AllowTrailingCommas = true,
				CommentHandling = JsonCommentHandling.Skip
			});
			JsonElement rootElement = jsonDocument.RootElement;
			ConfigDocument configDocument = new ConfigDocument
			{
				FilePath = file
			};
			if (rootElement.ValueKind == JsonValueKind.Array)
			{
				configDocument.MainLevelLayoutIDs = new List<JsonElement>();
				configDocument.PositionTriggers = ReadTriggerArray(rootElement);
				ValidateUniqueTriggerIDs(configDocument);
				return configDocument;
			}
			if (rootElement.ValueKind != JsonValueKind.Object)
			{
				return null;
			}
			configDocument.Enabled = GetBool(rootElement, "Enabled", defaultValue: true);
			if (rootElement.TryGetProperty("MainLevelLayoutIDs", out var value))
			{
				configDocument.MainLevelLayoutIDs = ReadSelectorList(value);
			}
			configDocument.Debug = ReadDebugOptions(rootElement);
			JsonElement value3;
			if (rootElement.TryGetProperty("PositionTriggers", out var value2) && value2.ValueKind == JsonValueKind.Array)
			{
				configDocument.PositionTriggers = ReadTriggerArray(value2);
			}
			else if (rootElement.TryGetProperty("Triggers", out value3) && value3.ValueKind == JsonValueKind.Array)
			{
				configDocument.PositionTriggers = ReadTriggerArray(value3);
			}
			if (rootElement.TryGetProperty("ScanTriggers", out var value4) && value4.ValueKind == JsonValueKind.Array)
			{
				configDocument.ScanTriggers = ReadScanTriggerArray(value4);
			}
			JsonElement value6;
			JsonElement value7;
			if (rootElement.TryGetProperty("InteractTriggers", out var value5) && value5.ValueKind == JsonValueKind.Array)
			{
				configDocument.InteractTriggers = ReadInteractTriggerArray(value5);
			}
			else if (rootElement.TryGetProperty("InteractionTriggers", out value6) && value6.ValueKind == JsonValueKind.Array)
			{
				configDocument.InteractTriggers = ReadInteractTriggerArray(value6);
			}
			else if (rootElement.TryGetProperty("ObjectTriggers", out value7) && value7.ValueKind == JsonValueKind.Array)
			{
				configDocument.InteractTriggers = ReadInteractTriggerArray(value7);
			}
			if (rootElement.TryGetProperty("HudInteractTriggers", out var value8) && value8.ValueKind == JsonValueKind.Array)
			{
				configDocument.HudInteractTriggers = ReadHudInteractTriggerArray(value8);
			}
			ValidateUniqueTriggerIDs(configDocument);
			return configDocument;
		}

		private static void ValidateUniqueTriggerIDs(ConfigDocument doc)
		{
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (PositionTriggerRule positionTrigger in doc.PositionTriggers)
			{
				Check("Position", positionTrigger.ID);
			}
			foreach (ScanTriggerRule scanTrigger in doc.ScanTriggers)
			{
				Check("Scan", scanTrigger.ID);
			}
			foreach (InteractTriggerRule interactTrigger in doc.InteractTriggers)
			{
				Check("Interact", interactTrigger.ID);
			}
			foreach (HudInteractTriggerRule hudInteractTrigger in doc.HudInteractTriggers)
			{
				Check("HudInteract", hudInteractTrigger.ID);
			}
			void Check(string category, string id)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				if (!string.IsNullOrWhiteSpace(id) && !seen.Add(id))
				{
					ManualLogSource log = Runtime.Log;
					if (log != null)
					{
						bool flag = default(bool);
						BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(118, 3, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: duplicate trigger ID '");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(id);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'. Trigger IDs must be globally unique within loaded configs. Category=");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(category);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", File=");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(doc.FilePath);
						}
						log.LogError(val);
					}
				}
			}
		}

		private static bool TryApplyCooldownPolicy(JsonElement element, string category, string triggerId, string triggerMode, bool isContinuous, out float cooldown)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			cooldown = GetFloat(element, "Cooldown", 0f);
			if (!isContinuous)
			{
				if (cooldown < 0f)
				{
					cooldown = 0f;
				}
				return true;
			}
			bool flag = default(bool);
			if (!HasProperty(element, "Cooldown"))
			{
				ManualLogSource log = Runtime.Log;
				if (log != null)
				{
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(153, 4, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: continuous ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(category);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" trigger '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(triggerId);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' uses TriggerMode='");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(triggerMode);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' but is missing required Cooldown. Continuous triggers require Cooldown >= ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(1f, "0.0");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Trigger skipped.");
					}
					log.LogError(val);
				}
				return false;
			}
			if (cooldown < 1f)
			{
				ManualLogSource log = Runtime.Log;
				if (log != null)
				{
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(136, 5, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("CTE config warning: continuous ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(category);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" trigger '");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(triggerId);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' uses TriggerMode='");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(triggerMode);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' with Cooldown=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(cooldown, "0.###");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(". Clamped to ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(1f, "0.0");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" to match AWO StartEventLoop safety semantics.");
					}
					log.LogWarning(val2);
				}
				cooldown = 1f;
			}
			return true;
		}

		private static bool HasProperty(JsonElement obj, params string[] names)
		{
			if (obj.ValueKind != JsonValueKind.Object)
			{
				return false;
			}
			foreach (string propertyName in names)
			{
				if (obj.TryGetProperty(propertyName, out var _))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsContinuousPositionTriggerMode(string triggerMode)
		{
			string text = Runtime.NormalizePositionTriggerMode(triggerMode);
			switch (text)
			{
			default:
				return text == "allplayersoutside";
			case "anyplayerinside":
			case "allplayersinside":
			case "anyplayeroutside":
				return true;
			}
		}

		private static bool IsContinuousScanTriggerMode(string triggerMode)
		{
			return Runtime.NormalizeTriggerMode(triggerMode) == "onallplayersinsidescan";
		}

		private static bool IsContinuousInteractTriggerMode(string targetType, string triggerMode)
		{
			string text = Runtime.NormalizeTargetType(targetType);
			string text2 = Runtime.NormalizeInteractionTriggerMode(triggerMode);
			if (text == "bigpickup")
			{
				if (!(text2 == "onbigpickupheld"))
				{
					return text2 == "onbigpickupplaced";
				}
				return true;
			}
			return false;
		}

		private static List<InteractTriggerRule> ReadInteractTriggerArray(JsonElement array)
		{
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Expected O, but got Unknown
			List<InteractTriggerRule> list = new List<InteractTriggerRule>();
			bool flag = default(bool);
			foreach (JsonElement item in array.EnumerateArray())
			{
				if (item.ValueKind != JsonValueKind.Object)
				{
					continue;
				}
				InteractTriggerRule interactTriggerRule = new InteractTriggerRule
				{
					ID = GetString(item, "ID", GetString(item, "Id", GetString(item, "id", string.Empty))),
					Enabled = GetBool(item, "Enabled", defaultValue: true),
					TargetType = GetString(item, "TargetType", GetString(item, "Target", GetString(item, "ObjectType", "Any"))),
					TriggerMode = GetString(item, "TriggerMode", GetString(item, "Trigger", string.Empty)),
					Cooldown = GetFloat(item, "Cooldown", 0f),
					RequireInExpedition = GetBool(item, "RequireInExpedition", defaultValue: true),
					Index = GetInt(item, "Index", -1),
					InstanceID = GetInt(item, "InstanceID", GetInt(item, "ObjectInstanceID", -1)),
					SyncID = GetInt(item, "SyncID", GetInt(item, "SyncId", -1)),
					SerialNumber = GetInt(item, "SerialNumber", GetInt(item, "Serial", -1)),
					ItemKey = GetString(item, "ItemKey", GetString(item, "Key", string.Empty)),
					PublicName = GetString(item, "PublicName", GetString(item, "NameContains", string.Empty)),
					WorldEventObjectFilter = GetString(item, "WorldEventObjectFilter", GetString(item, "Filter", GetString(item, "ObjectFilter", string.Empty))),
					DataBlockID = GetUInt(item, "DataBlockID", GetUInt(item, "ItemDataBlockID", GetUInt(item, "ItemID", 0u))),
					ItemID = GetUInt(item, "ItemID", GetUInt(item, "PickupID", 0u)),
					InternalName = GetString(item, "InternalName", GetString(item, "PrefabName", string.Empty)),
					Radius = GetFloat(item, "Radius", 2f),
					UseTriggerArea = GetBool(item, "UseTriggerArea", defaultValue: false),
					UsePickupDropCycleEvents = GetBool(item, "UsePickupDropCycleEvents", GetBool(item, "UseBehaviorCycleEvents", GetBool(item, "UsePickupDropGroupEvents", defaultValue: false))),
					PickupDropCycleCount = Math.Max(1, GetInt(item, "PickupDropCycleCount", GetInt(item, "BehaviorCycleCount", GetInt(item, "ActionGroupCount", 3))))
				};
				if (item.TryGetProperty("Position", out var value) && value.ValueKind == JsonValueKind.Object)
				{
					interactTriggerRule.Position = new PositionData
					{
						x = GetFloat(value, "x", GetFloat(value, "X", 0f)),
						y = GetFloat(value, "y", GetFloat(value, "Y", 0f)),
						z = GetFloat(value, "z", GetFloat(value, "Z", 0f))
					};
				}
				interactTriggerRule.InteractTriggerArea = (interactTriggerRule.UseTriggerArea ? ReadInteractTriggerArea(item, interactTriggerRule.ID) : null);
				if (item.TryGetProperty("Events", out var value2) && value2.ValueKind == JsonValueKind.Array)
				{
					interactTriggerRule.Events = (from e in value2.EnumerateArray()
						select e.Clone()).ToList();
				}
				if (item.TryGetProperty("WardenEvents", out var value3) && value3.ValueKind == JsonValueKind.Array)
				{
					interactTriggerRule.WardenEvents = (from e in value3.EnumerateArray()
						select e.Clone()).ToList();
				}
				interactTriggerRule.PickupDropCycleEvents = ReadFirstArrayProperty(item, "PickupDropCycleEvents", "PickupDropGroupEvents", "BehaviorCycleEvents", "ActionGroupEvents", "CycleEvents");
				ReadPlayerTriggerEventsObject(item, interactTriggerRule);
				float cooldown;
				if (string.IsNullOrWhiteSpace(interactTriggerRule.ID))
				{
					ManualLogSource log = Runtime.Log;
					if (log != null)
					{
						BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(87, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: trigger is missing required ID. File may be skipped partly. Category=");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(interactTriggerRule.GetType().Name);
						}
						log.LogError(val);
					}
				}
				else if (TryApplyCooldownPolicy(item, "Interact", interactTriggerRule.ID, interactTriggerRule.TriggerMode, IsContinuousInteractTriggerMode(interactTriggerRule.TargetType, interactTriggerRule.TriggerMode), out cooldown))
				{
					interactTriggerRule.Cooldown = cooldown;
					list.Add(interactTriggerRule);
				}
			}
			return list;
		}

		private static List<ScanTriggerRule> ReadScanTriggerArray(JsonElement array)
		{
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Expected O, but got Unknown
			List<ScanTriggerRule> list = new List<ScanTriggerRule>();
			bool flag = default(bool);
			foreach (JsonElement item in array.EnumerateArray())
			{
				if (item.ValueKind != JsonValueKind.Object)
				{
					continue;
				}
				ScanTriggerRule scanTriggerRule = new ScanTriggerRule
				{
					ID = GetString(item, "ID", GetString(item, "Id", GetString(item, "id", string.Empty))),
					Enabled = GetBool(item, "Enabled", defaultValue: true),
					PuzzleOverrideIndex = GetInt(item, "Index", GetInt(item, "PuzzleOverrideIndex", GetInt(item, "PuzzleIndex", -1))),
					TriggerMode = GetString(item, "TriggerMode", GetString(item, "Trigger", "OnScanActivated")),
					UsePlayerCountEvents = GetBool(item, "UsePlayerCountEvents", GetBool(item, "EnablePlayerCountEvents", defaultValue: false)),
					Cooldown = GetFloat(item, "Cooldown", 0f),
					RequireInExpedition = GetBool(item, "RequireInExpedition", defaultValue: true),
					RequireAlivePlayers = GetBool(item, "RequireAlivePlayers", defaultValue: true),
					UseTriggerCycleEvents = GetBool(item, "UseTriggerCycleEvents", GetBool(item, "UseScanCycleEvents", GetBool(item, "UseTriggerGroupEvents", defaultValue: false))),
					TriggerCycleCount = Math.Max(1, GetInt(item, "TriggerCycleCount", GetInt(item, "ScanCycleCount", GetInt(item, "TriggerGroupCount", 3))))
				};
				if (item.TryGetProperty("Events", out var value) && value.ValueKind == JsonValueKind.Array)
				{
					scanTriggerRule.Events = (from e in value.EnumerateArray()
						select e.Clone()).ToList();
				}
				if (item.TryGetProperty("WardenEvents", out var value2) && value2.ValueKind == JsonValueKind.Array)
				{
					scanTriggerRule.WardenEvents = (from e in value2.EnumerateArray()
						select e.Clone()).ToList();
				}
				scanTriggerRule.OnePlayerEvents = ReadFirstArrayProperty(item, "OnePlayerEvents", "EventsOnOnePlayer", "OnePlayerWardenEvents", "WardenEventsOnOnePlayer", "Player1Events", "Events1P", "Events_1P");
				scanTriggerRule.TwoPlayerEvents = ReadFirstArrayProperty(item, "TwoPlayerEvents", "EventsOnTwoPlayers", "TwoPlayerWardenEvents", "WardenEventsOnTwoPlayers", "Player2Events", "Events2P", "Events_2P");
				scanTriggerRule.ThreePlayerEvents = ReadFirstArrayProperty(item, "ThreePlayerEvents", "EventsOnThreePlayers", "ThreePlayerWardenEvents", "WardenEventsOnThreePlayers", "Player3Events", "Events3P", "Events_3P");
				scanTriggerRule.FourPlayerEvents = ReadFirstArrayProperty(item, "FourPlayerEvents", "EventsOnFourPlayers", "FourPlayerWardenEvents", "WardenEventsOnFourPlayers", "Player4Events", "Events4P", "Events_4P");
				ReadPlayerCountEventsObject(item, scanTriggerRule);
				scanTriggerRule.TriggerCycleEvents = ReadFirstArrayProperty(item, "TriggerCycleEvents", "ScanCycleEvents", "TriggerGroupEvents", "CycleEvents");
				float cooldown;
				if (string.IsNullOrWhiteSpace(scanTriggerRule.ID))
				{
					ManualLogSource log = Runtime.Log;
					if (log != null)
					{
						BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(87, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: trigger is missing required ID. File may be skipped partly. Category=");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(scanTriggerRule.GetType().Name);
						}
						log.LogError(val);
					}
				}
				else if (TryApplyCooldownPolicy(item, "Scan", scanTriggerRule.ID, scanTriggerRule.TriggerMode, IsContinuousScanTriggerMode(scanTriggerRule.TriggerMode), out cooldown))
				{
					scanTriggerRule.Cooldown = cooldown;
					list.Add(scanTriggerRule);
				}
			}
			return list;
		}

		private static List<HudInteractTriggerRule> ReadHudInteractTriggerArray(JsonElement array)
		{
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Expected O, but got Unknown
			List<HudInteractTriggerRule> list = new List<HudInteractTriggerRule>();
			bool flag = default(bool);
			foreach (JsonElement item in array.EnumerateArray())
			{
				if (item.ValueKind != JsonValueKind.Object)
				{
					continue;
				}
				HudInteractTriggerRule hudInteractTriggerRule = new HudInteractTriggerRule
				{
					ID = GetString(item, "ID", GetString(item, "Id", GetString(item, "id", string.Empty))),
					Enabled = GetBool(item, "Enabled", defaultValue: true),
					Radius = GetFloat(item, "Radius", 2f),
					HudText = GetString(item, "HudText", GetString(item, "InteractionText", GetString(item, "Text", "Interact"))),
					ProgressText = GetString(item, "ProgressText", string.Empty),
					HoldTime = Math.Max(0f, GetFloat(item, "HoldTime", GetFloat(item, "InteractDuration", GetFloat(item, "Duration", 2f)))),
					Cooldown = Math.Max(0f, GetFloat(item, "Cooldown", 1f)),
					RequireInExpedition = GetBool(item, "RequireInExpedition", defaultValue: true),
					RequireAlivePlayer = GetBool(item, "RequireAlivePlayer", defaultValue: true),
					HostValidateDistance = GetBool(item, "HostValidateDistance", defaultValue: true),
					DebugVisible = GetBool(item, "DebugVisible", defaultValue: false),
					DebugColor = GetString(item, "DebugColor", "#00BFFF"),
					DebugAlpha = Mathf.Clamp01(GetFloat(item, "DebugAlpha", 0.35f)),
					DebugLabel = GetBool(item, "DebugLabel", defaultValue: true)
				};
				if (item.TryGetProperty("Position", out var value) && value.ValueKind == JsonValueKind.Object)
				{
					hudInteractTriggerRule.Position = new PositionData
					{
						x = GetFloat(value, "x", GetFloat(value, "X", 0f)),
						y = GetFloat(value, "y", GetFloat(value, "Y", 0f)),
						z = GetFloat(value, "z", GetFloat(value, "Z", 0f))
					};
				}
				hudInteractTriggerRule.ProgressEvents = ReadHudInteractProgressEvents(item);
				hudInteractTriggerRule.CancelEvents = ReadFirstArrayProperty(item, "CancelEvents", "EventsOnCancel");
				hudInteractTriggerRule.Events = ReadFirstArrayProperty(item, "Events", "CompleteEvents", "EventsOnComplete");
				hudInteractTriggerRule.WardenEvents = ReadFirstArrayProperty(item, "WardenEvents", "CompleteWardenEvents");
				if (string.IsNullOrWhiteSpace(hudInteractTriggerRule.ID))
				{
					ManualLogSource? log = Runtime.Log;
					if (log != null)
					{
						log.LogError((object)"CTE config error: HUD interact trigger is missing required ID.");
					}
				}
				else if (hudInteractTriggerRule.Position == null)
				{
					ManualLogSource log2 = Runtime.Log;
					if (log2 != null)
					{
						BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(62, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: HUD interact trigger '");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(hudInteractTriggerRule.ID);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is missing Position.");
						}
						log2.LogError(val);
					}
				}
				else
				{
					list.Add(hudInteractTriggerRule);
				}
			}
			return list;
		}

		private static List<HudInteractProgressEventRule> ReadHudInteractProgressEvents(JsonElement element)
		{
			List<HudInteractProgressEventRule> list = new List<HudInteractProgressEventRule>();
			if (!element.TryGetProperty("ProgressEvents", out var value))
			{
				return list;
			}
			if (value.ValueKind == JsonValueKind.Object)
			{
				if (value.TryGetProperty("Progress", out var value2) || value.TryGetProperty("Events", out value2) || value.TryGetProperty("WardenEvents", out value2))
				{
					AddHudInteractProgressEvent(list, value);
				}
				else
				{
					foreach (JsonProperty item in value.EnumerateObject())
					{
						if (float.TryParse(item.Name, out var result) && item.Value.ValueKind == JsonValueKind.Array)
						{
							list.Add(new HudInteractProgressEventRule
							{
								Progress = NormalizeHudInteractProgress(result),
								Events = (from e in item.Value.EnumerateArray()
									select e.Clone()).ToList()
							});
						}
					}
				}
			}
			else if (value.ValueKind == JsonValueKind.Array)
			{
				foreach (JsonElement item2 in value.EnumerateArray())
				{
					if (item2.ValueKind == JsonValueKind.Object)
					{
						AddHudInteractProgressEvent(list, item2);
					}
				}
			}
			return list.OrderBy((HudInteractProgressEventRule e) => e.Progress).ToList();
		}

		private static void AddHudInteractProgressEvent(List<HudInteractProgressEventRule> result, JsonElement item)
		{
			result.Add(new HudInteractProgressEventRule
			{
				Progress = NormalizeHudInteractProgress(GetFloat(item, "Progress", -1f)),
				Events = ReadFirstArrayProperty(item, "Events", "WardenEvents")
			});
		}

		private static float NormalizeHudInteractProgress(float progress)
		{
			if (!(progress < 0f))
			{
				return Math.Clamp(progress, 0f, 1f);
			}
			return -1f;
		}

		private static List<PositionTriggerRule> ReadTriggerArray(JsonElement array)
		{
			//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cd: Expected O, but got Unknown
			List<PositionTriggerRule> list = new List<PositionTriggerRule>();
			bool flag = default(bool);
			foreach (JsonElement item in array.EnumerateArray())
			{
				if (item.ValueKind != JsonValueKind.Object)
				{
					continue;
				}
				PositionTriggerRule positionTriggerRule = new PositionTriggerRule
				{
					ID = GetString(item, "ID", GetString(item, "Id", GetString(item, "id", string.Empty))),
					Enabled = GetBool(item, "Enabled", defaultValue: true),
					TriggerAreaMode = GetString(item, "TriggerAreaMode", GetString(item, "AreaMode", GetString(item, "Mode", "Radius"))),
					Radius = GetFloat(item, "Radius", 3f),
					LocalIndex = GetInt(item, "LocalIndex", GetInt(item, "ZoneLocalIndex", -1)),
					Count = GetInt(item, "Count", GetInt(item, "AreaIndex", -1)),
					Layer = GetString(item, "Layer", string.Empty),
					DimensionIndex = GetInt(item, "DimensionIndex", -1),
					TriggerMode = GetString(item, "TriggerMode", "AnyPlayerEnter"),
					UsePlayerCountEvents = GetBool(item, "UsePlayerCountEvents", GetBool(item, "EnablePlayerCountEvents", GetBool(item, "UsePlayerCountEventGroups", GetBool(item, "EnablePlayerCountEventGroups", defaultValue: false)))),
					UseTriggerCycleEvents = GetBool(item, "UseTriggerCycleEvents", GetBool(item, "UsePositionCycleEvents", GetBool(item, "UseTriggerGroupEvents", defaultValue: false))),
					TriggerCycleCount = Math.Max(1, GetInt(item, "TriggerCycleCount", GetInt(item, "PositionCycleCount", GetInt(item, "TriggerGroupCount", 3)))),
					Cooldown = GetFloat(item, "Cooldown", 0f),
					RequireInExpedition = GetBool(item, "RequireInExpedition", defaultValue: true),
					RequireAlivePlayers = GetBool(item, "RequireAlivePlayers", defaultValue: true),
					IncludeBots = GetBool(item, "IncludeBots", defaultValue: true),
					DebugVisible = GetBool(item, "DebugVisible", defaultValue: true),
					DebugColor = GetString(item, "DebugColor", string.Empty)
				};
				JsonElement value2;
				if (item.TryGetProperty("Position", out var value) && value.ValueKind == JsonValueKind.Object)
				{
					positionTriggerRule.Position = new PositionData
					{
						x = GetFloat(value, "x", GetFloat(value, "X", 0f)),
						y = GetFloat(value, "y", GetFloat(value, "Y", 0f)),
						z = GetFloat(value, "z", GetFloat(value, "Z", 0f))
					};
				}
				else if (item.TryGetProperty("x", out value2) || item.TryGetProperty("X", out value2) || item.TryGetProperty("y", out value2) || item.TryGetProperty("Y", out value2) || item.TryGetProperty("z", out value2) || item.TryGetProperty("Z", out value2))
				{
					positionTriggerRule.Position = new PositionData
					{
						x = GetFloat(item, "x", GetFloat(item, "X", 0f)),
						y = GetFloat(item, "y", GetFloat(item, "Y", 0f)),
						z = GetFloat(item, "z", GetFloat(item, "Z", 0f))
					};
				}
				if (item.TryGetProperty("Events", out var value3) && value3.ValueKind == JsonValueKind.Array)
				{
					positionTriggerRule.Events = (from e in value3.EnumerateArray()
						select e.Clone()).ToList();
				}
				if (item.TryGetProperty("WardenEvents", out var value4) && value4.ValueKind == JsonValueKind.Array)
				{
					positionTriggerRule.WardenEvents = (from e in value4.EnumerateArray()
						select e.Clone()).ToList();
				}
				positionTriggerRule.OnePlayerEvents = ReadFirstArrayProperty(item, "OnePlayerEvents", "EventsOnOnePlayer", "OnePlayerWardenEvents", "WardenEventsOnOnePlayer", "Player1Events", "Events1P", "Events_1P");
				positionTriggerRule.TwoPlayerEvents = ReadFirstArrayProperty(item, "TwoPlayerEvents", "EventsOnTwoPlayers", "TwoPlayerWardenEvents", "WardenEventsOnTwoPlayers", "Player2Events", "Events2P", "Events_2P");
				positionTriggerRule.ThreePlayerEvents = ReadFirstArrayProperty(item, "ThreePlayerEvents", "EventsOnThreePlayers", "ThreePlayerWardenEvents", "WardenEventsOnThreePlayers", "Player3Events", "Events3P", "Events_3P");
				positionTriggerRule.FourPlayerEvents = ReadFirstArrayProperty(item, "FourPlayerEvents", "EventsOnFourPlayers", "FourPlayerWardenEvents", "WardenEventsOnFourPlayers", "Player4Events", "Events4P", "Events_4P");
				ReadPlayerCountEventsObject(item, positionTriggerRule);
				positionTriggerRule.TriggerCycleEvents = ReadFirstArrayProperty(item, "TriggerCycleEvents", "PositionCycleEvents", "TriggerGroupEvents", "CycleEvents");
				float cooldown;
				if (string.IsNullOrWhiteSpace(positionTriggerRule.ID))
				{
					ManualLogSource log = Runtime.Log;
					if (log != null)
					{
						BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(87, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CTE config error: trigger is missing required ID. File may be skipped partly. Category=");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(positionTriggerRule.GetType().Name);
						}
						log.LogError(val);
					}
				}
				else if (TryApplyCooldownPolicy(item, "Position", positionTriggerRule.ID, positionTriggerRule.TriggerMode, IsContinuousPositionTriggerMode(positionTriggerRule.TriggerMode), out cooldown))
				{
					positionTriggerRule.Cooldown = cooldown;
					list.Add(positionTriggerRule);
				}
			}
			return list;
		}

		private static List<JsonElement> ReadFirstArrayProperty(JsonElement obj, params string[] names)
		{
			foreach (string propertyName in names)
			{
				if (obj.TryGetProperty(propertyName, out var value) && value.ValueKind == JsonValueKind.Array)
				{
					return (from e in value.EnumerateArray()
						select e.Clone()).ToList();
				}
			}
			return new List<JsonElement>();
		}

		private static PositionTriggerRule? ReadInteractTriggerArea(JsonElement obj, string ownerID)
		{
			if (!TryGetObjectProperty(obj, out var value, "TriggerArea", "InteractTriggerArea", "OverrideArea", "AreaFilter", "TriggerAreaFilter"))
			{
				return null;
			}
			PositionTriggerRule positionTriggerRule = new PositionTriggerRule
			{
				ID = (string.IsNullOrWhiteSpace(ownerID) ? "InteractTriggerArea" : (ownerID + "::InteractTriggerArea")),
				Enabled = GetBool(value, "Enabled", defaultValue: true),
				TriggerAreaMode = ((GetInt(value, "Count", GetInt(value, "AreaIndex", -1)) < 0) ? "OverrideBigZone" : "OverrideArea"),
				Radius = GetFloat(value, "Radius", 2f),
				LocalIndex = GetInt(value, "LocalIndex", GetInt(value, "ZoneLocalIndex", -1)),
				Count = GetInt(value, "Count", GetInt(value, "AreaIndex", -1)),
				Layer = GetString(value, "Layer", string.Empty),
				DimensionIndex = GetInt(value, "DimensionIndex", -1),
				RequireAlivePlayers = false,
				DebugVisible = false
			};
			if (value.TryGetProperty("Position", out var value2) && value2.ValueKind == JsonValueKind.Object)
			{
				positionTriggerRule.Position = new PositionData
				{
					x = GetFloat(value2, "x", GetFloat(value2, "X", 0f)),
					y = GetFloat(value2, "y", GetFloat(value2, "Y", 0f)),
					z = GetFloat(value2, "z", GetFloat(value2, "Z", 0f))
				};
			}
			else if (HasProperty(value, "x", "X", "y", "Y", "z", "Z"))
			{
				positionTriggerRule.Position = new PositionData
				{
					x = GetFloat(value, "x", GetFloat(value, "X", 0f)),
					y = GetFloat(value, "y", GetFloat(value, "Y", 0f)),
					z = GetFloat(value, "z", GetFloat(value, "Z", 0f))
				};
			}
			if (!positionTriggerRule.Enabled)
			{
				return null;
			}
			return positionTriggerRule;
		}

		private static bool TryGetObjectProperty(JsonElement obj, out JsonElement value, params string[] names)
		{
			foreach (string propertyName in names)
			{
				if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(propertyName, out value) && value.ValueKind == JsonValueKind.Object)
				{
					return true;
				}
			}
			value = default(JsonElement);
			return false;
		}

		private static void ReadPlayerTriggerEventsObject(JsonElement obj, InteractTriggerRule rule)
		{
			if (obj.TryGetProperty("PlayerTriggerEvents", out var value) && value.ValueKind == JsonValueKind.Object)
			{
				foreach (JsonProperty item in value.EnumerateObject())
				{
					if (item.Value.ValueKind == JsonValueKind.Array)
					{
						List<JsonElement> list = (from e in item.Value.EnumerateArray()
							select e.Clone()).ToList();
						switch (item.Name.Trim().ToLowerInvariant())
						{
						case "1":
						case "p0":
						case "player1":
						case "slot1":
							rule.OnePlayerTriggerEvents = list;
							break;
						case "2":
						case "p1":
						case "player2":
						case "slot2":
							rule.TwoPlayerTriggerEvents = list;
							break;
						case "3":
						case "p2":
						case "player3":
						case "slot3":
							rule.ThreePlayerTriggerEvents = list;
							break;
						case "4":
						case "p3":
						case "player4":
						case "slot4":
							rule.FourPlayerTriggerEvents = list;
							break;
						}
					}
				}
			}
			if (rule.OnePlayerTriggerEvents.Count == 0)
			{
				rule.OnePlayerTriggerEvents = ReadFirstArrayProperty(obj, "OnePlayerTriggerEvents", "Player1TriggerEvents", "P0TriggerEvents");
			}
			if (rule.TwoPlayerTriggerEvents.Count == 0)
			{
				rule.TwoPlayerTriggerEvents = ReadFirstArrayProperty(obj, "TwoPlayerTriggerEvents", "Player2TriggerEvents", "P1TriggerEvents");
			}
			if (rule.ThreePlayerTriggerEvents.Count == 0)
			{
				rule.ThreePlayerTriggerEvents = ReadFirstArrayProperty(obj, "ThreePlayerTriggerEvents", "Player3TriggerEvents", "P2TriggerEvents");
			}
			if (rule.FourPlayerTriggerEvents.Count == 0)
			{
				rule.FourPlayerTriggerEvents = ReadFirstArrayProperty(obj, "FourPlayerTriggerEvents", "Player4TriggerEvents", "P3TriggerEvents");
			}
		}

		private static void ReadPlayerCountEventsObject(JsonElement obj, PositionTriggerRule rule)
		{
			if (!obj.TryGetProperty("PlayerCountEvents", out var value) || value.ValueKind != JsonValueKind.Object)
			{
				return;
			}
			foreach (JsonProperty item in value.EnumerateObject())
			{
				if (item.Value.ValueKind == JsonValueKind.Array)
				{
					List<JsonElement> list = (from e in item.Value.EnumerateArray()
						select e.Clone()).ToList();
					switch (item.Name.Trim().ToLowerInvariant())
					{
					case "1":
					case "one":
					case "oneplayer":
						rule.OnePlayerEvents = list;
						break;
					case "2":
					case "two":
					case "twoplayers":
						rule.TwoPlayerEvents = list;
						break;
					case "3":
					case "three":
					case "threeplayers":
						rule.ThreePlayerEvents = list;
						break;
					case "4":
					case "four":
					case "fourplayers":
						rule.FourPlayerEvents = list;
						break;
					}
				}
			}
		}

		private static void ReadPlayerCountEventsObject(JsonElement obj, ScanTriggerRule rule)
		{
			if (!obj.TryGetProperty("PlayerCountEvents", out var value) || value.ValueKind != JsonValueKind.Object)
			{
				return;
			}
			foreach (JsonProperty item in value.EnumerateObject())
			{
				if (item.Value.ValueKind == JsonValueKind.Array)
				{
					List<JsonElement> list = (from e in item.Value.EnumerateArray()
						select e.Clone()).ToList();
					switch (item.Name.Trim().ToLowerInvariant())
					{
					case "1":
					case "one":
					case "oneplayer":
						rule.OnePlayerEvents = list;
						break;
					case "2":
					case "two":
					case "twoplayers":
						rule.TwoPlayerEvents = list;
						break;
					case "3":
					case "three":
					case "threeplayers":
						rule.ThreePlayerEvents = list;
						break;
					case "4":
					case "four":
					case "fourplayers":
						rule.FourPlayerEvents = list;
						break;
					}
				}
			}
		}

		private static List<JsonElement> ReadSelectorList(JsonElement value)
		{
			if (value.ValueKind == JsonValueKind.Array)
			{
				return (from e in value.EnumerateArray()
					select e.Clone()).ToList();
			}
			if (value.ValueKind == JsonValueKind.String || value.ValueKind == JsonValueKind.Number || value.ValueKind == JsonValueKind.Object)
			{
				return new List<JsonElement> { value.Clone() };
			}
			return new List<JsonElement>();
		}

		private static DebugOptions ReadDebugOptions(JsonElement root)
		{
			DebugOptions debugOptions = new DebugOptions();
			debugOptions.Enabled = GetBool(root, "EnableDebugScanMarkers", GetBool(root, "DebugScanMarkers", GetBool(root, "DebugEnabled", defaultValue: false)));
			debugOptions.ShowScanMarkers = GetBool(root, "ShowDebugScanMarkers", defaultValue: true);
			debugOptions.ShowNames = GetBool(root, "DebugShowNames", defaultValue: true);
			debugOptions.MarkerColor = GetString(root, "DebugMarkerColor", debugOptions.MarkerColor);
			debugOptions.LabelColor = GetString(root, "DebugLabelColor", debugOptions.LabelColor);
			debugOptions.MarkerAlpha = GetFloat(root, "DebugMarkerAlpha", debugOptions.MarkerAlpha);
			debugOptions.HeightOffset = GetFloat(root, "DebugHeightOffset", debugOptions.HeightOffset);
			debugOptions.MarkerHeight = GetFloat(root, "DebugMarkerHeight", debugOptions.MarkerHeight);
			debugOptions.LabelHeightOffset = GetFloat(root, "DebugLabelHeightOffset", debugOptions.LabelHeightOffset);
			debugOptions.RadiusScale = GetFloat(root, "DebugRadiusScale", debugOptions.RadiusScale);
			debugOptions.MinimumRadius = GetFloat(root, "DebugMinimumRadius", debugOptions.MinimumRadius);
			debugOptions.RefreshInterval = GetFloat(root, "DebugRefreshInterval", debugOptions.RefreshInterval);
			debugOptions.DumpRuntimeIndexes = GetBool(root, "DumpRuntimeIndexes", debugOptions.DumpRuntimeIndexes);
			if (root.TryGetProperty("Debug", out var value) && value.ValueKind == JsonValueKind.Object)
			{
				debugOptions.Enabled = GetBool(value, "Enabled", debugOptions.Enabled);
				debugOptions.ShowScanMarkers = GetBool(value, "ShowScanMarkers", debugOptions.ShowScanMarkers);
				debugOptions.ShowNames = GetBool(value, "ShowNames", debugOptions.ShowNames);
				debugOptions.MarkerColor = GetString(value, "MarkerColor", debugOptions.MarkerColor);
				debugOptions.LabelColor = GetString(value, "LabelColor", debugOptions.LabelColor);
				debugOptions.MarkerAlpha = GetFloat(value, "MarkerAlpha", debugOptions.MarkerAlpha);
				debugOptions.HeightOffset = GetFloat(value, "HeightOffset", debugOptions.HeightOffset);
				debugOptions.MarkerHeight = GetFloat(value, "MarkerHeight", debugOptions.MarkerHeight);
				debugOptions.LabelHeightOffset = GetFloat(value, "LabelHeightOffset", debugOptions.LabelHeightOffset);
				debugOptions.RadiusScale = GetFloat(value, "RadiusScale", debugOptions.RadiusScale);
				debugOptions.MinimumRadius = GetFloat(value, "MinimumRadius", debugOptions.MinimumRadius);
				debugOptions.RefreshInterval = GetFloat(value, "RefreshInterval", debugOptions.RefreshInterval);
				debugOptions.DumpRuntimeIndexes = GetBool(value, "DumpRuntimeIndexes", debugOptions.DumpRuntimeIndexes);
			}
			debugOptions.MarkerAlpha = Mathf.Clamp01(debugOptions.MarkerAlpha);
			debugOptions.RadiusScale = Math.Max(0.01f, debugOptions.RadiusScale);
			debugOptions.MinimumRadius = Math.Max(0.01f, debugOptions.MinimumRadius);
			debugOptions.MarkerHeight = Math.Max(0.001f, debugOptions.MarkerHeight);
			debugOptions.RefreshInterval = Math.Max(0.2f, debugOptions.RefreshInterval);
			return debugOptions;
		}

		private static bool GetBool(JsonElement obj, string name, bool defaultValue)
		{
			if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(name, out var value))
			{
				if (value.ValueKind == JsonValueKind.True)
				{
					return true;
				}
				if (value.ValueKind == JsonValueKind.False)
				{
					return false;
				}
				if (value.ValueKind == JsonValueKind.String && bool.TryParse(value.GetString(), out var result))
				{
					return result;
				}
			}
			return defaultValue;
		}

		private static float GetFloat(JsonElement obj, string name, float defaultValue)
		{
			if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(name, out var value))
			{
				if (value.ValueKind == JsonValueKind.Number && value.TryGetSingle(out var value2))
				{
					return value2;
				}
				if (value.ValueKind == JsonValueKind.String && float.TryParse(value.GetString(), out var result))
				{
					return result;
				}
			}
			return defaultValue;
		}

		private static int GetInt(JsonElement obj, string name, int defaultValue)
		{
			if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(name, out var value))
			{
				if (value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var value2))
				{
					return value2;
				}
				if (value.ValueKind == JsonValueKind.String && int.TryParse(value.GetString(), out var result))
				{
					return result;
				}
			}
			return defaultValue;
		}

		private static uint GetUInt(JsonElement obj, string name, uint defaultValue)
		{
			if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(name, out var value))
			{
				if (value.ValueKind == JsonValueKind.Number && value.TryGetUInt32(out var value2))
				{
					return value2;
				}
				if (value.ValueKind == JsonValueKind.String && uint.TryParse(value.GetString(), out var result))
				{
					return result;
				}
			}
			return defaultValue;
		}

		private static string GetString(JsonElement obj, string name, string defaultValue)
		{
			if (obj.ValueKind == JsonValueKind.Object && obj.TryGetProperty(name, out var value))
			{
				if (value.ValueKind == JsonValueKind.String)
				{
					return value.GetString() ?? defaultValue;
				}
				return value.ToString();
			}
			return defaultValue;
		}

		private static string CreateChinesePositionTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - 位置触发器默认模板。\n  // Enabled:控制整个文件是否生效;false 表示本文件只是示例模板,不会加载其中的触发器。\n  \"Enabled\": false,\n  // MainLevelLayoutIDs:指定生效的关卡 LayoutPersistentID/名称;0 是占位值,实际使用时请改成目标关卡。\n  \"MainLevelLayoutIDs\": 0,\n  // Debug:位置触发器的可视化调试设置。\n  \"Debug\": {\n    // Enabled:true 时启用调试显示。\n    \"Enabled\": true,\n    // ShowScanMarkers:true 时显示触发范围标记。\n    \"ShowScanMarkers\": true,\n    // ShowNames:true 时在调试标记上显示触发器 ID。\n    \"ShowNames\": true\n  },\n  // PositionTriggers:坐标/区域触发器列表。\n  \"PositionTriggers\": [\n    {\n      // ID:触发器唯一 ID,可被 Type 700 的 TargetID 用来开启/关闭。\n      \"ID\": \"position_radius_any_player_enter\",\n      // Enabled:控制此单个触发器是否启用。\n      \"Enabled\": true,\n      // TriggerAreaMode:触发区域模式,可填 Radius / OverrideBigZone / OverrideArea。\n      \"TriggerAreaMode\": \"Radius\",\n      // Position:Radius 模式下的世界坐标中心点。\n      \"Position\": { \"x\": 0, \"y\": 0, \"z\": 0 },\n      // Radius:Radius 模式下的触发半径,单位为游戏世界距离。\n      \"Radius\": 5.0,\n      // DimensionIndex:OverrideBigZone/OverrideArea 使用的维度索引;-1 表示不按此字段筛选。\n      \"DimensionIndex\": -1,\n      // Layer:OverrideBigZone/OverrideArea 使用的层级;空字符串表示不按此字段筛选。\n      \"Layer\": \"\",\n      // LocalIndex:OverrideBigZone/OverrideArea 使用的大区 LocalIndex;-1 表示不按此字段筛选。\n      \"LocalIndex\": -1,\n      // Count:OverrideArea 使用的小区索引;0=Area_a,1=Area_b,-1 表示该大区内所有小区。\n      \"Count\": -1,\n      // TriggerMode:触发模式,可填 AnyPlayerEnter / AnyPlayerInside / AnyPlayerExit / AnyPlayerOutside / AllPlayersEnter / AllPlayersInside / AllPlayersExit / AllPlayersOutside。\n      // AnyPlayerOutside:触发器曾经被玩家进入后,只要任意有效玩家在触发范围外就按 Cooldown 持续触发。\n      // AllPlayersOutside:触发器曾经被玩家进入后,所有有效玩家都在触发范围外才按 Cooldown 持续触发。\n      \"TriggerMode\": \"AnyPlayerEnter\",\n      // Cooldown:触发冷却时间,单位秒;Inside 持续触发类模式建议不低于 1。\n      \"Cooldown\": 1.0,\n      // UsePlayerCountEvents:true 时根据区域内玩家人数触发 PlayerCountEvents。\n      \"UsePlayerCountEvents\": false,\n      // PlayerCountEvents:按区域内玩家人数 1/2/3/4 分流执行的事件组。\n      \"PlayerCountEvents\": {\n        // 1:区域内有 1 名玩家时执行的事件组。\n        \"1\": [],\n        // 2:区域内有 2 名玩家时执行的事件组。\n        \"2\": [],\n        // 3:区域内有 3 名玩家时执行的事件组。\n        \"3\": [],\n        // 4:区域内有 4 名玩家时执行的事件组。\n        \"4\": []\n      },\n      // UseTriggerCycleEvents:true 时按成功触发次数累计触发 TriggerCycleEvents。\n      \"UseTriggerCycleEvents\": false,\n      // TriggerCycleCount:累计多少次成功触发后额外执行 TriggerCycleEvents。\n      \"TriggerCycleCount\": 3,\n      // TriggerCycleEvents:达到 TriggerCycleCount 后额外执行的事件组。\n      \"TriggerCycleEvents\": [],\n      // Events:满足 TriggerMode 条件后执行的主事件组。\n      \"Events\": [\n        // Type:事件类型;700 是 CTE 自定义的触发器开关事件。\n        { \"Type\": 700, \"TargetID\": \"position_radius_any_player_enter\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateEnglishPositionTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - PositionTriggers default template.\n  // Enabled: controls whether this whole file is loaded; false makes this file a disabled example.\n  \"Enabled\": false,\n  // MainLevelLayoutIDs: target LayoutPersistentID/name; 0 is a placeholder for the actual expedition layout.\n  \"MainLevelLayoutIDs\": 0,\n  // Debug: visual debug settings for position triggers.\n  \"Debug\": {\n    // Enabled: true enables debug display.\n    \"Enabled\": true,\n    // ShowScanMarkers: true shows trigger range markers.\n    \"ShowScanMarkers\": true,\n    // ShowNames: true shows trigger IDs above debug markers.\n    \"ShowNames\": true\n  },\n  // PositionTriggers: list of coordinate/area triggers.\n  \"PositionTriggers\": [\n    {\n      // ID: unique trigger ID, also used by Type 700 TargetID.\n      \"ID\": \"position_radius_any_player_enter\",\n      // Enabled: controls this single trigger.\n      \"Enabled\": true,\n      // TriggerAreaMode: area mode, allowed values are Radius / OverrideBigZone / OverrideArea.\n      \"TriggerAreaMode\": \"Radius\",\n      // Position: world-space center used by Radius mode.\n      \"Position\": { \"x\": 0, \"y\": 0, \"z\": 0 },\n      // Radius: trigger radius in world units for Radius mode.\n      \"Radius\": 5.0,\n      // DimensionIndex: dimension filter for OverrideBigZone/OverrideArea; -1 disables this filter.\n      \"DimensionIndex\": -1,\n      // Layer: layer filter for OverrideBigZone/OverrideArea; empty string disables this filter.\n      \"Layer\": \"\",\n      // LocalIndex: zone LocalIndex filter for OverrideBigZone/OverrideArea; -1 disables this filter.\n      \"LocalIndex\": -1,\n      // Count: area index for OverrideArea; 0=Area_a, 1=Area_b, -1 means every area in the zone.\n      \"Count\": -1,\n      // TriggerMode: allowed values are AnyPlayerEnter / AnyPlayerInside / AnyPlayerExit / AnyPlayerOutside / AllPlayersEnter / AllPlayersInside / AllPlayersExit / AllPlayersOutside.\n      // AnyPlayerOutside: after this trigger has been entered once, continues while outside when any eligible player is outside the trigger area.\n      // AllPlayersOutside: after this trigger has been entered once, continues while outside when all eligible players are outside the trigger area.\n      \"TriggerMode\": \"AnyPlayerEnter\",\n      // Cooldown: trigger cooldown in seconds; Inside repeat modes should generally be at least 1.\n      \"Cooldown\": 1.0,\n      // UsePlayerCountEvents: true enables PlayerCountEvents by player count inside the area.\n      \"UsePlayerCountEvents\": false,\n      // PlayerCountEvents: event groups selected by player count 1/2/3/4 inside the area.\n      \"PlayerCountEvents\": {\n        // 1: events for one player inside.\n        \"1\": [],\n        // 2: events for two players inside.\n        \"2\": [],\n        // 3: events for three players inside.\n        \"3\": [],\n        // 4: events for four players inside.\n        \"4\": []\n      },\n      // UseTriggerCycleEvents: true fires TriggerCycleEvents after repeated successful triggers.\n      \"UseTriggerCycleEvents\": false,\n      // TriggerCycleCount: number of successful triggers required before TriggerCycleEvents fires.\n      \"TriggerCycleCount\": 3,\n      // TriggerCycleEvents: extra events fired when TriggerCycleCount is reached.\n      \"TriggerCycleEvents\": [],\n      // Events: main event group fired when TriggerMode conditions are met.\n      \"Events\": [\n        // Type: event type; 700 is CTE's custom trigger enable/disable event.\n        { \"Type\": 700, \"TargetID\": \"position_radius_any_player_enter\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateChineseScanTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - 扫描点触发器默认模板。\n  // Enabled:控制整个文件是否生效;false 表示本文件只是示例模板。\n  \"Enabled\": false,\n  // MainLevelLayoutIDs:指定生效的关卡 LayoutPersistentID/名称;0 是占位值。\n  \"MainLevelLayoutIDs\": 0,\n  // ScanTriggers:附加到安全扫描点上的触发器列表。\n  \"ScanTriggers\": [\n    {\n      // ID:触发器唯一 ID,可被 Type 700 的 TargetID 用来开启/关闭。\n      \"ID\": \"scan_activated\",\n      // Enabled:控制此单个扫描点触发器是否启用。\n      \"Enabled\": true,\n      // Index:扫描点索引,对应 SPO/本插件解析到的扫描点编号。\n      \"Index\": 0,\n      // TriggerMode:可填 OnScanActivated / OnPlayerExitScan / OnAllPlayersEnterScan / OnAllPlayersInsideScan / OnAllPlayersExitScan。\n      \"TriggerMode\": \"OnScanActivated\",\n      // Cooldown:触发冷却时间,单位秒;Inside 持续触发类模式建议不低于 1。\n      \"Cooldown\": 1.0,\n      // UsePlayerCountEvents:true 时根据扫描点内玩家人数触发 PlayerCountEvents。\n      \"UsePlayerCountEvents\": false,\n      // PlayerCountEvents:按扫描点内玩家人数 1/2/3/4 分流执行的事件组。\n      \"PlayerCountEvents\": {\n        // 1:扫描点内有 1 名玩家时执行的事件组。\n        \"1\": [],\n        // 2:扫描点内有 2 名玩家时执行的事件组。\n        \"2\": [],\n        // 3:扫描点内有 3 名玩家时执行的事件组。\n        \"3\": [],\n        // 4:扫描点内有 4 名玩家时执行的事件组。\n        \"4\": []\n      },\n      // UseTriggerCycleEvents:true 时按成功触发次数累计触发 TriggerCycleEvents。\n      \"UseTriggerCycleEvents\": false,\n      // TriggerCycleCount:累计多少次成功触发后额外执行 TriggerCycleEvents。\n      \"TriggerCycleCount\": 3,\n      // TriggerCycleEvents:达到 TriggerCycleCount 后额外执行的事件组。\n      \"TriggerCycleEvents\": [],\n      // Events:满足 TriggerMode 条件后执行的主事件组。\n      \"Events\": [\n        // Type:事件类型;700 是 CTE 自定义的触发器开关事件。\n        { \"Type\": 700, \"TargetID\": \"position_radius_any_player_enter\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateEnglishScanTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - ScanTriggers default template.\n  // Enabled: controls whether this whole file is loaded; false makes this file a disabled example.\n  \"Enabled\": false,\n  // MainLevelLayoutIDs: target LayoutPersistentID/name; 0 is a placeholder.\n  \"MainLevelLayoutIDs\": 0,\n  // ScanTriggers: trigger list attached to security scans.\n  \"ScanTriggers\": [\n    {\n      // ID: unique trigger ID, also used by Type 700 TargetID.\n      \"ID\": \"scan_activated\",\n      // Enabled: controls this single scan trigger.\n      \"Enabled\": true,\n      // Index: scan index resolved by SPO/CTE runtime scan lookup.\n      \"Index\": 0,\n      // TriggerMode: allowed values are OnScanActivated / OnPlayerExitScan / OnAllPlayersEnterScan / OnAllPlayersInsideScan / OnAllPlayersExitScan.\n      \"TriggerMode\": \"OnScanActivated\",\n      // Cooldown: trigger cooldown in seconds; Inside repeat mode should generally be at least 1.\n      \"Cooldown\": 1.0,\n      // UsePlayerCountEvents: true enables PlayerCountEvents by player count inside the scan.\n      \"UsePlayerCountEvents\": false,\n      // PlayerCountEvents: event groups selected by player count 1/2/3/4 inside the scan.\n      \"PlayerCountEvents\": {\n        // 1: events for one player inside the scan.\n        \"1\": [],\n        // 2: events for two players inside the scan.\n        \"2\": [],\n        // 3: events for three players inside the scan.\n        \"3\": [],\n        // 4: events for four players inside the scan.\n        \"4\": []\n      },\n      // UseTriggerCycleEvents: true fires TriggerCycleEvents after repeated successful triggers.\n      \"UseTriggerCycleEvents\": false,\n      // TriggerCycleCount: number of successful triggers required before TriggerCycleEvents fires.\n      \"TriggerCycleCount\": 3,\n      // TriggerCycleEvents: extra events fired when TriggerCycleCount is reached.\n      \"TriggerCycleEvents\": [],\n      // Events: main event group fired when TriggerMode conditions are met.\n      \"Events\": [\n        // Type: event type; 700 is CTE's custom trigger enable/disable event.\n        { \"Type\": 700, \"TargetID\": \"position_radius_any_player_enter\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateChineseBigPickupTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - 大物品触发器默认模板。\n  // Enabled:控制整个文件是否生效;false 表示本文件只是示例模板。\n  \"Enabled\": false,\n  // MainLevelLayoutIDs:指定生效的关卡 LayoutPersistentID/名称;0 是占位值。\n  \"MainLevelLayoutIDs\": 0,\n  // InteractTriggers:交互类触发器列表;此文件用于 BigPickup。\n  \"InteractTriggers\": [\n    {\n      // ID:触发器唯一 ID,可被 Type 700 的 TargetID 用来开启/关闭。\n      \"ID\": \"bigpickup_pickup\",\n      // Enabled:控制此单个大物品触发器是否启用。\n      \"Enabled\": true,\n      // TargetType:目标类型;大物品触发器固定使用 BigPickup。\n      \"TargetType\": \"BigPickup\",\n      // TriggerMode:可填 OnBigPickupPickup / OnBigPickupHeld / OnBigPickupDrop / OnBigPickupPlaced。\n      \"TriggerMode\": \"OnBigPickupPickup\",\n      // Index:大物品索引;-1 表示不按索引筛选。\n      \"Index\": 0,\n      // Cooldown:触发冷却时间,单位秒;Held/Placed 持续类模式建议不低于 1。\n      \"Cooldown\": 1.0,\n      // UseTriggerArea:false 表示全局响应;true 表示 Events 和 PlayerTriggerEvents 只在 TriggerArea 内触发。\n      \"UseTriggerArea\": true,\n      // TriggerArea:限制大物品触发事件生效的区域。\n      \"TriggerArea\": {\n        // DimensionIndex:维度索引,通常主维度为 0。\n        \"DimensionIndex\": 0,\n        // Layer:层级索引,通常 MainLayer 为 0。\n        \"Layer\": 0,\n        // LocalIndex:大区 LocalIndex。\n        \"LocalIndex\": 0,\n        // Count:小区索引;0=Area_a,1=Area_b,-1 表示该大区内所有小区。\n        \"Count\": -1\n      },\n      // PlayerTriggerEvents:按拾取/放下玩家槽位 1/2/3/4 分流执行的事件组。\n      \"PlayerTriggerEvents\": {\n        // 1:1 号槽位玩家触发时执行的事件组。\n        \"1\": [],\n        // 2:2 号槽位玩家触发时执行的事件组。\n        \"2\": [],\n        // 3:3 号槽位玩家触发时执行的事件组。\n        \"3\": [],\n        // 4:4 号槽位玩家触发时执行的事件组。\n        \"4\": []\n      },\n      // UsePickupDropCycleEvents:true 时按“拾取->放下”完整循环累计触发 PickupDropCycleEvents。\n      \"UsePickupDropCycleEvents\": false,\n      // PickupDropCycleCount:累计多少个拾取/放下循环后额外执行 PickupDropCycleEvents。\n      \"PickupDropCycleCount\": 1,\n      // PickupDropCycleEvents:达到 PickupDropCycleCount 后额外执行的事件组。\n      \"PickupDropCycleEvents\": [],\n      // Events:满足 TriggerMode 条件后执行的通用事件组。\n      \"Events\": [\n        // Type:事件类型;700 是 CTE 自定义的触发器开关事件。\n        { \"Type\": 700, \"TargetID\": \"bigpickup_held_repeat\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateEnglishBigPickupTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - BigPickup default template.\n  // Enabled: controls whether this whole file is loaded; false makes this file a disabled example.\n  \"Enabled\": false,\n  // MainLevelLayoutIDs: target LayoutPersistentID/name; 0 is a placeholder.\n  \"MainLevelLayoutIDs\": 0,\n  // InteractTriggers: interaction trigger list; this file is for BigPickup.\n  \"InteractTriggers\": [\n    {\n      // ID: unique trigger ID, also used by Type 700 TargetID.\n      \"ID\": \"bigpickup_pickup\",\n      // Enabled: controls this single big-pickup trigger.\n      \"Enabled\": true,\n      // TargetType: target type; BigPickup triggers use BigPickup.\n      \"TargetType\": \"BigPickup\",\n      // TriggerMode: allowed values are OnBigPickupPickup / OnBigPickupHeld / OnBigPickupDrop / OnBigPickupPlaced.\n      \"TriggerMode\": \"OnBigPickupPickup\",\n      // Index: big pickup index; -1 disables index filtering.\n      \"Index\": 0,\n      // Cooldown: trigger cooldown in seconds; Held/Placed repeat modes should generally be at least 1.\n      \"Cooldown\": 1.0,\n      // UseTriggerArea: false means global response; true means Events and PlayerTriggerEvents only fire inside TriggerArea.\n      \"UseTriggerArea\": true,\n      // TriggerArea: area restriction for big-pickup events.\n      \"TriggerArea\": {\n        // DimensionIndex: dimension index, usually 0 for the main dimension.\n        \"DimensionIndex\": 0,\n        // Layer: layer index, usually 0 for MainLayer.\n        \"Layer\": 0,\n        // LocalIndex: zone LocalIndex.\n        \"LocalIndex\": 0,\n        // Count: area index; 0=Area_a, 1=Area_b, -1 means every area in this zone.\n        \"Count\": -1\n      },\n      // PlayerTriggerEvents: event groups selected by triggering player slot 1/2/3/4.\n      \"PlayerTriggerEvents\": {\n        // 1: events for player slot 1.\n        \"1\": [],\n        // 2: events for player slot 2.\n        \"2\": [],\n        // 3: events for player slot 3.\n        \"3\": [],\n        // 4: events for player slot 4.\n        \"4\": []\n      },\n      // UsePickupDropCycleEvents: true fires PickupDropCycleEvents after full pickup->drop cycles.\n      \"UsePickupDropCycleEvents\": false,\n      // PickupDropCycleCount: number of pickup/drop cycles required before PickupDropCycleEvents fires.\n      \"PickupDropCycleCount\": 1,\n      // PickupDropCycleEvents: extra events fired when PickupDropCycleCount is reached.\n      \"PickupDropCycleEvents\": [],\n      // Events: shared event group fired when TriggerMode conditions are met.\n      \"Events\": [\n        // Type: event type; 700 is CTE's custom trigger enable/disable event.\n        { \"Type\": 700, \"TargetID\": \"bigpickup_held_repeat\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateChineseHudInteractTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - HUD 交互触发器默认模板。\n  // Enabled:控制整个文件是否生效;false 表示本文件只是示例模板。\n  \"Enabled\": false,\n  // MainLevelLayoutIDs:指定生效的关卡 LayoutPersistentID/名称;0 是占位值。\n  \"MainLevelLayoutIDs\": 0,\n  // HudInteractTriggers:地图坐标 HUD 交互触发点列表。\n  \"HudInteractTriggers\": [\n    {\n      // ID:触发器唯一 ID,可被 Type 700 的 TargetID 用来开启/关闭。\n      \"ID\": \"hud_interact_01\",\n      // Enabled:控制此单个 HUD 交互触发点是否启用。\n      \"Enabled\": true,\n      // Position:HUD 交互触发点的世界坐标。\n      \"Position\": { \"x\": 0, \"y\": 0, \"z\": 0 },\n      // Radius:原生交互点可选中的半径,影响玩家靠近后是否能看到/选中 HUD。\n      \"Radius\": 0.5,\n      // HudText:屏幕下方交互 HUD 文本;支持 Unity/TMP 富文本颜色标签,例如 <color=#00BFFF>Text</color>。\n      \"HudText\": \"<color=#00BFFF>Read data</color>\",\n      // HoldTime:按住交互键的读条总时长,单位秒。\n      \"HoldTime\": 3.0,\n      // Cooldown:完成读条后再次允许触发 Events 的冷却时间,单位秒。\n      \"Cooldown\": 1.0,\n      // DebugColor:调试可视化标记颜色,支持 #RRGGBB / #RRGGBBAA。\n      \"DebugColor\": \"#00BFFF\",\n      // DebugAlpha:调试可视化标记透明度,0=完全透明,1=不透明。\n      \"DebugAlpha\": 0.35,\n      // DebugLabel:true 时在调试标记上显示 ID 标签。\n      \"DebugLabel\": true,\n      // ProgressEvents:SPO 风格进度事件;Progress 可填 0-1 或 0-100,-1 表示禁用此占位示例。\n      \"ProgressEvents\": {\n        // Progress:触发进度点;0.5/50 表示 50%,1/100 表示完成。\n        \"Progress\": -1,\n        // Events:达到 Progress 后执行的事件组。\n        \"Events\": []\n      },\n      // CancelEvents:玩家已开始按住交互键但中途放弃时执行;正常完成读条不会触发。\n      \"CancelEvents\": [],\n      // Events:读条完成后执行的事件组。\n      \"Events\": [\n        // Type:事件类型;700 是 CTE 自定义的触发器开关事件。\n        { \"Type\": 700, \"Category\": \"HudInteract\", \"TargetID\": \"hud_interact_01\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}

		private static string CreateEnglishHudInteractTemplateJson()
		{
			return "{\n  // CoordinateTriggerEvents 1.6.8 - HudInteractTriggers default template.\n  // Enabled: controls whether this whole file is loaded; false makes this file a disabled example.\n  \"Enabled\": false,\n  // MainLevelLayoutIDs: target LayoutPersistentID/name; 0 is a placeholder.\n  \"MainLevelLayoutIDs\": 0,\n  // HudInteractTriggers: list of coordinate-based HUD interaction points.\n  \"HudInteractTriggers\": [\n    {\n      // ID: unique trigger ID, also used by Type 700 TargetID.\n      \"ID\": \"hud_interact_01\",\n      // Enabled: controls this single HUD interaction point.\n      \"Enabled\": true,\n      // Position: world-space position of this HUD interaction point.\n      \"Position\": { \"x\": 0, \"y\": 0, \"z\": 0 },\n      // Radius: selectable radius of the native interaction point, affecting whether nearby players can see/select the HUD.\n      \"Radius\": 0.5,\n      // HudText: bottom interaction HUD text; Unity/TMP rich text color tags are supported, for example <color=#00BFFF>Text</color>.\n      \"HudText\": \"<color=#00BFFF>Read data</color>\",\n      // HoldTime: total hold-interact progress duration, in seconds.\n      \"HoldTime\": 3.0,\n      // Cooldown: seconds before completed Events may fire again.\n      \"Cooldown\": 1.0,\n      // DebugColor: debug marker color, supports #RRGGBB / #RRGGBBAA.\n      \"DebugColor\": \"#00BFFF\",\n      // DebugAlpha: debug marker opacity, 0=transparent and 1=opaque.\n      \"DebugAlpha\": 0.35,\n      // DebugLabel: true shows the trigger ID label on the debug marker.\n      \"DebugLabel\": true,\n      // ProgressEvents: SPO-style progress event; Progress accepts 0-1 or 0-100, and -1 disables this placeholder example.\n      \"ProgressEvents\": {\n        // Progress: progress point; 0.5/50 means 50%, 1/100 means complete.\n        \"Progress\": -1,\n        // Events: event group fired when Progress is reached.\n        \"Events\": []\n      },\n      // CancelEvents: fires when the player starts holding interact and then abandons it; normal completion does not fire this list.\n      \"CancelEvents\": [],\n      // Events: event group fired after the hold completes.\n      \"Events\": [\n        // Type: event type; 700 is CTE's custom trigger enable/disable event.\n        { \"Type\": 700, \"Category\": \"HudInteract\", \"TargetID\": \"hud_interact_01\", \"Enabled\": false }\n      ]\n    }\n  ]\n}";
		}
	}
	internal static class PluginInfo
	{
		public const string GUID = "com.gtfo.coordinatetriggerevents";

		public const string NAME = "CoordinateTriggerEvents";

		public const string VERSION = "1.6.8";
	}
	[BepInPlugin("com.gtfo.coordinatetriggerevents", "CoordinateTriggerEvents", "1.6.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BasePlugin
	{
		private Harmony? _harmony;

		public override void Load()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			Runtime.Log = ((BasePlugin)this).Log;
			ConfigManager.LoadOrCreate(((BasePlugin)this).Log, force: true);
			Runtime.SetupHudInteractTriggerSync();
			Runtime.SetupTriggerControlWardenEvent();
			EventAPI.OnExpeditionStarted += Runtime.OnExpeditionStarted;
			_harmony = new Harmony("com.gtfo.coordinatetriggerevents");
			SafePatchAll(_harmony, ((BasePlugin)this).Log);
			Runtime.LogVerbose($"{"CoordinateTriggerEvents"} {"1.6.8"} loaded. Config roots={ConfigManager.ConfigPathSummary}");
		}

		private static void SafePatchAll(Harmony harmony, ManualLogSource log)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			int num = 0;
			int num2 = 0;
			bool flag = default(bool);
			try
			{
				Type[] types = Assembly.GetExecutingAssembly().GetTypes();
				foreach (Type type in types)
				{
					if (!type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Any())
					{
						continue;
					}
					try
					{
						harmony.CreateClassProcessor(type).Patch();
						num++;
					}
					catch (Exception ex)
					{
						num2++;
						BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(36, 3, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Optional Harmony patch skipped: ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(type.FullName);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
						}
						log.LogWarning(val);
					}
				}
			}
			catch (Exception ex2)
			{
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(34, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("SafePatchAll failed unexpectedly: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex2);
				}
				log.LogError(val2);
			}
			Runtime.LogVerbose($"Harmony patches applied. PatchedClasses={num}, SkippedClasses={num2}");
		}
	}
	internal static class Runtime
	{
		private sealed class CachedWardenEventBuild
		{
			public bool Success;

			public WardenObjectiveEventData? EventData;

			public string Failure = string.Empty;

			public bool FailureLogged;
		}

		private sealed class ZoneLookupCacheEntry
		{
			public readonly List<LG_Zone> Zones = new List<LG_Zone>();

			public readonly List<(LG_Zone Zone, object Area, int Index)> Areas = new List<(LG_Zone, object, int)>();

			public bool Resolved;

			public float LastAttemptTime = -999999f;

			public string LastFailure = string.Empty;
		}

		private sealed class PendingConfiguredEvent
		{
			public List<JsonElement> EventGroup = new List<JsonElement>();

			public string OwnerLabel = string.Empty;

			public float DueTime;

			public bool HasSourcePosition;

			public Vector3 SourcePosition;
		}

		private readonly struct PositionTriggerPlayerState
		{
			public readonly int InsidePlayerCount;

			public readonly int EligibleInsidePlayerCount;

			public readonly int EligiblePlayerCount;

			public PositionTriggerPlayerState(int insidePlayerCount, int eligibleInsidePlayerCount, int eligiblePlayerCount)
			{
				InsidePlayerCount = insidePlayerCount;
				EligibleInsidePlayerCount = eligibleInsidePlayerCount;
				EligiblePlayerCount = eligiblePlayerCount;
			}
		}

		private sealed class CourseNodeDimensionMap
		{
			public List<AIG_CourseNode>?[,]? BuildNodeMap;

			public AIG_CourseNode[]?[,]? NodeMap;

			public int MinX = int.MaxValue;

			public int MinZ = int.MaxValue;

			public int MaxX = int.MinValue;

			public int MaxZ = int.MinValue;

			public int SizeX;

			public int SizeZ;

			public void UpdateBounds(Vector3 position)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				int val = Mathf.FloorToInt(position.x / 4f);
				int val2 = Mathf.FloorToInt(position.z / 4f);
				MinX = Math.Min(MinX, val);
				MinZ = Math.Min(MinZ, val2);
				MaxX = Math.Max(MaxX, val);
				MaxZ = Math.Max(MaxZ, val2);
			}

			public bool CreateNodeMap()
			{
				if (MinX == int.MaxValue || MinZ == int.MaxValue || MaxX == int.MinValue || MaxZ == int.MinValue)
				{
					return false;
				}
				SizeX = Math.Max(1, MaxX - MinX + 1);
				SizeZ = Math.Max(1, MaxZ - MinZ + 1);
				BuildNodeMap = new List<AIG_CourseNode>[SizeX, SizeZ];
				NodeMap = new AIG_CourseNode[SizeX, SizeZ][];
				return true;
			}

			public bool AddNode(Vector3 position, AIG_CourseNode node)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				if (BuildNodeMap == null)
				{
					return false;
				}
				(int x, int z) mapPos = GetMapPos(position);
				int item = mapPos.x;
				int item2 = mapPos.z;
				List<AIG_CourseNode> list = BuildNodeMap[item, item2];
				if (list == null)
				{
					list = new List<AIG_CourseNode>();
					BuildNodeMap[item, item2] = list;
				}
				if (!list.Any((AIG_CourseNode existing) => existing != null && existing.NodeID == node.NodeID))
				{
					list.Add(node);
				}
				return true;
			}

			public void FinishBuild()
			{
				if (BuildNodeMap == null || NodeMap == null)
				{
					return;
				}
				for (int i = 0; i < SizeX; i++)
				{
					for (int j = 0; j < SizeZ; j++)
					{
						NodeMap[i, j] = BuildNodeMap[i, j]?.ToArray();
					}
				}
				BuildNodeMap = null;
			}

			public AIG_CourseNode[]? GetNodes(Vector3 position)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				if (NodeMap == null)
				{
					return null;
				}
				var (num, num2) = GetMapPos(position);
				if (NodeMap[num, num2] != null)
				{
					return NodeMap[num, num2];
				}
				int num3 = Math.Max(SizeX, SizeZ);
				for (int i = 1; i < num3; i++)
				{
					int num4 = Math.Max(num - i, 0);
					int num5 = Math.Min(num + i, SizeX - 1);
					int num6 = Math.Max(num2 - i, 0);
					int num7 = Math.Min(num2 + i, SizeZ - 1);
					for (int j = num4; j <= num5; j++)
					{
						if (NodeMap[j, num6] != null)
						{
							return NodeMap[j, num6];
						}
						if (NodeMap[j, num7] != null)
						{
							return NodeMap[j, num7];
						}
					}
					for (int k = num6 + 1; k <= num7 - 1; k++)
					{
						if (NodeMap[num4, k] != null)
						{
							return NodeMap[num4, k];
						}
						if (NodeMap[num5, k] != null)
						{
							return NodeMap[num5, k];
						}
					}
				}
				return null;
			}

			private (int x, int z) GetMapPos(Vector3 position)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				int num = Mathf.FloorToInt(position.x / 4f) - MinX;
				return new ValueTuple<int, int>(item2: Mathf.Clamp(Mathf.FloorToInt(position.z / 4f) - MinZ, 0, SizeZ - 1), item1: Mathf.Clamp(num, 0, SizeX - 1));
			}
		}

		internal static ManualLogSource? Log;

		internal static readonly bool VerboseConsoleLogging = false;

		private static readonly Dictionary<string, TriggerState> States = new Dictionary<string, TriggerState>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<LocalizedText> LocalizedTextRoots = new List<LocalizedText>();

		private static readonly Dictionary<string, CachedWardenEventBuild> WardenEventBuildCache = new Dictionary<string, CachedWardenEventBuild>(StringComparer.Ordinal);

		private const int WardenEventBuildCacheLimit = 4096;

		private static float _lastTickTime;

		private static float _lastLogTime;

		private static readonly Dictionary<string, float> LastLogTimesByMessage = new Dictionary<string, float>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> PositionModeNormalizationCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> ScanModeNormalizationCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> InteractModeNormalizationCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> TargetTypeNormalizationCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private const int NormalizationCacheLimit = 512;

		private static readonly Dictionary<string, uint> PartialDataPersistentIdCache = new Dictionary<string, uint>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, uint> LevelLayoutStringIdCache = new Dictionary<string, uint>(StringComparer.OrdinalIgnoreCase);

		private static Dictionary<string, uint>? PersistentIdDumpCache;

		private static readonly Dictionary<string, ZoneLookupCacheEntry> ZoneLookupCache = new Dictionary<string, ZoneLookupCacheEntry>(StringComparer.OrdinalIgnoreCase);

		private static readonly object ActiveTriggerCacheLock = new object();

		private static readonly List<(ConfigDocument Config, PositionTriggerRule Trigger)> ActivePositionTriggerCache = new List<(ConfigDocument, PositionTriggerRule)>();

		private static readonly List<(ConfigDocument Config, ScanTriggerRule Trigger)> ActiveScanTriggerCache = new List<(ConfigDocument, ScanTriggerRule)>();

		private static readonly List<(ConfigDocument Config, InteractTriggerRule Trigger)> ActiveInteractTriggerCache = new List<(ConfigDocument, InteractTriggerRule)>();

		private static readonly List<(ConfigDocument Config, HudInteractTriggerRule Trigger)> ActiveHudInteractTriggerCache = new List<(ConfigDocument, HudInteractTriggerRule)>();

		private static bool ActiveTriggerCacheDirty = true;

		private static uint ActiveTriggerCacheLayoutId = uint.MaxValue;

		private static string ActiveTriggerCacheLayoutName = string.Empty;

		public const int MaxHudInteractTriggerIdBytes = 96;

		private const string HudInteractRequestEventName = "com.gtfo.coordinatetriggerevents.hud_interact_request.v1";

		private const string HudInteractConfirmEventName = "com.gtfo.coordinatetriggerevents.hud_interact_confirm.v1";

		private static bool HudInteractSyncRegistered;

		private static readonly Queue<PendingConfiguredEvent> PendingConfiguredEvents = new Queue<PendingConfiguredEvent>();

		private const int MaxQueuedConfiguredEventsPerTick = 3;

		private static readonly HashSet<string> KnownLegacyOrEosEventNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"CloseSecurityDoor_Custom", "SetTimerTitle", "AlertEnemiesInZone", "AlertEnemiesInArea", "Terminal_ShowTerminalInfoInZone", "KillEnemiesInArea", "KillEnemiesInZone", "KillEnemiesInDimension", "SpawnHibernate", "PlayGCEndSequence",
			"FF_ToggleFFCheck", "FF_AddPlayersInRangeToCheck", "FF_AddPlayersOutOfRangeToCheck", "FF_ToggleCheckOnGroup", "FF_Reset", "FF_ResetGroup", "FF_SetExpeditionFailedText", "FF_ResetExpeditionFailedText", "SetNavMarker", "ToggleDummyVisual",
			"ToggleLSFBState", "SaveCheckpoint", "SetSuccessPageCustomization", "ToggleCamaraShake", "Info_ZoneHibernate", "Info_LevelHibernate", "Output_LevelHibernateSpawnEvent", "PlayMusic", "StopMusic", "ToggleEventScanState",
			"ToggleSeamlessReload", "ResourceStation_SetEnabled", "ToggleSensorGroupState"
		};

		private const float CourseNodeMapIndexSize = 4f;

		private static readonly Dictionary<eDimensionIndex, CourseNodeDimensionMap> CourseNodePositionMaps = new Dictionary<eDimensionIndex, CourseNodeDimensionMap>();

		private static int CourseNodePositionMapClusterCount = -1;

		private const int TriggerControlEventType = 700;

		private const string TriggerControlEventName = "CTETriggerControl";

		private static bool TriggerControlWardenEventRegistered;

		internal static void LogVerbose(string message)
		{
			if (VerboseConsoleLogging)
			{
				ManualLogSource? log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		internal static void MarkActiveTriggerCacheDirty()
		{
			lock (ActiveTriggerCacheLock)
			{
				ActiveTriggerCacheDirty = true;
			}
		}

		internal static void ClearConfigurationResolutionCaches()
		{
			PartialDataPersistentIdCache.Clear();
			LevelLayoutStringIdCache.Clear();
			PersistentIdDumpCache = null;
			WardenEventBuildCache.Clear();
		}

		internal static void SetupHudInteractTriggerSync()
		{
			if (HudInteractSyncRegistered)
			{
				return;
			}
			try
			{
				NetworkAPI.RegisterEvent<HudInteractTriggerRequestPacket>("com.gtfo.coordinatetriggerevents.hud_interact_request.v1", (Action<ulong, HudInteractTriggerRequestPacket>)HudInteractTriggerManager.OnReceiveRequest);
				NetworkAPI.RegisterEvent<HudInteractTriggerConfirmPacket>("com.gtfo.coordinatetriggerevents.hud_interact_confirm.v1", (Action<ulong, HudInteractTriggerConfirmPacket>)HudInteractTriggerManager.OnReceiveConfirm);
				HudInteractSyncRegistered = true;
				LogVerbose("Registered CTE HUD interact sync events 'com.gtfo.coordinatetriggerevents.hud_interact_request.v1' and 'com.gtfo.coordinatetriggerevents.hud_interact_confirm.v1'.");
			}
			catch (Exception ex)
			{
				LogThrottled("Failed to register CTE HUD interact sync events: " + DescribeException(ex));
			}
		}

		internal static void BroadcastHudInteractRequest(HudInteractTriggerRequestPacket packet)
		{
			if (!HudInteractSyncRegistered)
			{
				return;
			}
			try
			{
				NetworkAPI.InvokeEvent<HudInteractTriggerRequestPacket>("com.gtfo.coordinatetriggerevents.hud_interact_request.v1", packet, (SNet_ChannelType)2);
			}
			catch (Exception ex)
			{
				LogThrottled("Failed to broadcast CTE HUD interact request: " + DescribeException(ex));
			}
		}

		internal static void BroadcastHudInteractConfirm(HudInteractTriggerConfirmPacket packet)
		{
			if (!HudInteractSyncRegistered)
			{
				return;
			}
			try
			{
				NetworkAPI.InvokeEvent<HudInteractTriggerConfirmPacket>("com.gtfo.coordinatetriggerevents.hud_interact_confirm.v1", packet, (SNet_ChannelType)2);
			}
			catch (Exception ex)
			{
				LogThrottled("Failed to broadcast CTE HUD interact confirm: " + DescribeException(ex));
			}
		}

		private static string GetCachedNormalization(Dictionary<string, string> cache, string? text, Func<string, string> normalize)
		{
			string text2 = text ?? string.Empty;
			if (cache.TryGetValue(text2, out string value) && value != null)
			{
				return value;
			}
			string text3 = normalize(text2);
			if (cache.Count < 512)
			{
				cache[text2] = text3;
			}
			return text3;
		}

		private static void ClearHotPathRuntimeCaches()
		{
			if (PositionModeNormalizationCache.Count > 512)
			{
				PositionModeNormalizationCache.Clear();
			}
			if (ScanModeNormalizationCache.Count > 512)
			{
				ScanModeNormalizationCache.Clear();
			}
			if (InteractModeNormalizationCache.Count > 512)
			{
				InteractModeNormalizationCache.Clear();
			}
			if (TargetTypeNormalizationCache.Count > 512)
			{
				TargetTypeNormalizationCache.Clear();
			}
		}

		private static void EnsureActiveTriggerCache()
		{
			uint currentLevelLayoutId = GetCurrentLevelLayoutId();
			string text = TryGetLevelLayoutName(currentLevelLayoutId);
			lock (ActiveTriggerCacheLock)
			{
				if (!ActiveTriggerCacheDirty && ActiveTriggerCacheLayoutId == currentLevelLayoutId && string.Equals(ActiveTriggerCacheLayoutName, text, StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
				ActivePositionTriggerCache.Clear();
				ActiveScanTriggerCache.Clear();
				ActiveInteractTriggerCache.Clear();
				ActiveHudInteractTriggerCache.Clear();
				foreach (ConfigDocument config in ConfigManager.Configs)
				{
					if (!config.Enabled || !MatchesCurrentLevel(config, currentLevelLayoutId, text, out string _))
					{
						continue;
					}
					foreach (PositionTriggerRule positionTrigger in config.PositionTriggers)
					{
						if (positionTrigger.Enabled)
						{
							ActivePositionTriggerCache.Add((config, positionTrigger));
						}
					}
					foreach (ScanTriggerRule scanTrigger in config.ScanTriggers)
					{
						if (scanTrigger.Enabled)
						{
							ActiveScanTriggerCache.Add((config, scanTrigger));
						}
					}
					foreach (InteractTriggerRule interactTrigger in config.InteractTriggers)
					{
						if (interactTrigger.Enabled)
						{
							ActiveInteractTriggerCache.Add((config, interactTrigger));
						}
					}
					foreach (HudInteractTriggerRule hudInteractTrigger in config.HudInteractTriggers)
					{
						if (hudInteractTrigger.Enabled)
						{
							ActiveHudInteractTriggerCache.Add((config, hudInteractTrigger));
						}
					}
				}
				ActiveTriggerCacheLayoutId = currentLevelLayoutId;
				ActiveTriggerCacheLayoutName = text;
				ActiveTriggerCacheDirty = false;
				PrimeWardenEventCacheForActiveTriggers();
			}
		}

		private static void PrimeWardenEventCacheForActiveTriggers()
		{
			foreach (var item5 in ActivePositionTriggerCache)
			{
				PositionTriggerRule item = item5.Trigger;
				PrimeEventList(item.Events);
				PrimeEventList(item.WardenEvents);
				PrimeEventList(item.OnePlayerEvents);
				PrimeEventList(item.TwoPlayerEvents);
				PrimeEventList(item.ThreePlayerEvents);
				PrimeEventList(item.FourPlayerEvents);
				PrimeEventList(item.TriggerCycleEvents);
			}
			foreach (var item6 in ActiveScanTriggerCache)
			{
				ScanTriggerRule item2 = item6.Trigger;
				PrimeEventList(item2.Events);
				PrimeEventList(item2.WardenEvents);
				PrimeEventList(item2.OnePlayerEvents);
				PrimeEventList(item2.TwoPlayerEvents);
				PrimeEventList(item2.ThreePlayerEvents);
				PrimeEventList(item2.FourPlayerEvents);
				PrimeEventList(item2.TriggerCycleEvents);
			}
			foreach (var item7 in ActiveInteractTriggerCache)
			{
				InteractTriggerRule item3 = item7.Trigger;
				PrimeEventList(item3.Events);
				PrimeEventList(item3.WardenEvents);
				PrimeEventList(item3.PickupDropCycleEvents);
			}
			foreach (var item8 in ActiveHudInteractTriggerCache)
			{
				HudInteractTriggerRule item4 = item8.Trigger;
				PrimeEventList(item4.Events);
				PrimeEventList(item4.WardenEvents);
				PrimeEventLi