using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; 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 UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("CoordinateTriggerEvents")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CoordinateTriggerEvents")] [assembly: AssemblyTitle("CoordinateTriggerEvents")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace CoordinateTriggerEvents { internal static class PluginInfo { public const string GUID = "com.gtfo.coordinatetriggerevents"; public const string NAME = "CoordinateTriggerEvents"; public const string VERSION = "1.1.0"; } [BepInPlugin("com.gtfo.coordinatetriggerevents", "CoordinateTriggerEvents", "1.1.0")] [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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Runtime.Log = ((BasePlugin)this).Log; ConfigManager.LoadOrCreate(((BasePlugin)this).Log, force: true); EventAPI.OnExpeditionStarted += Runtime.OnExpeditionStarted; _harmony = new Harmony("com.gtfo.coordinatetriggerevents"); SafePatchAll(_harmony, ((BasePlugin)this).Log); Runtime.LogVerbose($"{"CoordinateTriggerEvents"} {"1.1.0"} 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(type.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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(ex2); } log.LogError(val2); } Runtime.LogVerbose($"Harmony patches applied. PatchedClasses={num}, SkippedClasses={num2}"); } } internal sealed class ConfigDocument { public string FilePath = string.Empty; public bool Enabled = true; public List MainLevelLayoutIDs = new List(); public DebugOptions Debug = new DebugOptions(); public List PositionTriggers = new List(); public List ScanTriggers = new List(); public List InteractTriggers = new List(); } internal sealed class ScanTriggerRule { public string ID { get; set; } = string.Empty; public bool Enabled { get; set; } = true; public int PuzzleOverrideIndex { get; set; } = -1; public string TriggerMode { get; set; } = "OnScanActivated"; public bool UsePlayerCountEvents { get; set; } public List OnePlayerEvents { get; set; } = new List(); public List TwoPlayerEvents { get; set; } = new List(); public List ThreePlayerEvents { get; set; } = new List(); public List FourPlayerEvents { get; set; } = new List(); public float Cooldown { get; set; } public bool RequireInExpedition { get; set; } = true; public bool RequireAlivePlayers { get; set; } = true; public bool UseTriggerCycleEvents { get; set; } public int TriggerCycleCount { get; set; } = 3; public List TriggerCycleEvents { get; set; } = new List(); public List Events { get; set; } = new List(); public List WardenEvents { get; set; } = new List(); } internal sealed class ScanRuntimeState { public int LastPlayerCount; public bool HasObserved; public bool Fired; public float LastFireTime = -999999f; public bool IsActive; public bool ActivatedThisCycle; public bool ActivationPlayerCountEventsFired; public bool HadPlayersInside; public bool ExitTriggeredThisCycle; public bool AllPlayersInsideNow; public bool AllPlayersEnteredThisCycle; public bool AllPlayersExitedRepeatActive; public int ActivationEdgeSequence; public int ExitEdgeSequence; public int AllPlayersEnterEdgeSequence; public int AllPlayersExitEdgeSequence; } internal sealed class InteractTriggerRule { public string ID { get; set; } = string.Empty; public bool Enabled { get; set; } = true; public string TargetType { get; set; } = "Any"; public string TriggerMode { get; set; } = string.Empty; public float Cooldown { get; set; } public bool RequireInExpedition { get; set; } = true; public int Index { get; set; } = -1; public int InstanceID { get; set; } = -1; public int SyncID { get; set; } = -1; public int SerialNumber { get; set; } = -1; public string ItemKey { get; set; } = string.Empty; public string PublicName { get; set; } = string.Empty; public string TerminalSerial { get; set; } = string.Empty; public string WorldEventObjectFilter { get; set; } = string.Empty; public uint DataBlockID { get; set; } public uint ItemID { get; set; } public string InternalName { get; set; } = string.Empty; public PositionData? Position { get; set; } public float Radius { get; set; } = 2f; public bool UsePickupDropCycleEvents { get; set; } public int PickupDropCycleCount { get; set; } = 3; public List PickupDropCycleEvents { get; set; } = new List(); public List Events { get; set; } = new List(); public List WardenEvents { get; set; } = new List(); } internal sealed class DebugOptions { public bool Enabled; public bool ShowScanMarkers = true; public bool ShowNames = true; public string MarkerColor = "#00BFFF"; public string LabelColor = "#FFFFFF"; public float MarkerAlpha = 0.35f; public float HeightOffset = 0.05f; public float MarkerHeight = 0.025f; public float LabelHeightOffset = 1f; public float RadiusScale = 1f; public float MinimumRadius = 0.5f; public float RefreshInterval = 1f; public bool DumpRuntimeIndexes; } internal sealed class PositionTriggerRule { public string ID { get; set; } = string.Empty; public bool Enabled { get; set; } = true; public PositionData? Position { get; set; } public string TriggerAreaMode { get; set; } = "Radius"; public float Radius { get; set; } = 3f; public int LocalIndex { get; set; } = -1; public int Count { get; set; } = -1; public string Layer { get; set; } = string.Empty; public int DimensionIndex { get; set; } = -1; public string TriggerMode { get; set; } = "AnyPlayerEnter"; public bool UsePlayerCountEvents { get; set; } public bool UseTriggerCycleEvents { get; set; } public int TriggerCycleCount { get; set; } = 3; public List TriggerCycleEvents { get; set; } = new List(); public List OnePlayerEvents { get; set; } = new List(); public List TwoPlayerEvents { get; set; } = new List(); public List ThreePlayerEvents { get; set; } = new List(); public List FourPlayerEvents { get; set; } = new List(); public float Cooldown { get; set; } public bool RequireInExpedition { get; set; } = true; public bool RequireAlivePlayers { get; set; } = true; public bool IncludeBots { get; set; } = true; public bool DebugVisible { get; set; } = true; public string DebugColor { get; set; } = string.Empty; public List Events { get; set; } = new List(); public List WardenEvents { get; set; } = new List(); } internal sealed class PositionData { public float x { get; set; } public float y { get; set; } public float z { get; set; } public Vector3 ToVector3() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(x, y, z); } } internal sealed class TriggerState { public bool WasInside; public bool Fired; public float LastFireTime = -999999f; public int LastInsidePlayerCount; public readonly HashSet FiredPlayerCounts = new HashSet(); public int CompletedCycles; } 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 Configs = new List(); private static readonly object LockObject = new object(); private static string _pluginDir = string.Empty; private static readonly Dictionary LastWriteTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ConfigWatchers = new Dictionary(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) ? "" : 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 list = GetConfigSearchRoots(log).Where(Directory.Exists).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); EnsureConfigWatchers(list, log); List list2 = (from p in list.SelectMany((string root) => Directory.GetFiles(root, "*.json", SearchOption.AllDirectories)) where IsCoordinateTriggerConfigPath(p) select p).Distinct(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(item); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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 files) { bool result = files.Count != LastWriteTimes.Count; HashSet hashSet = new HashSet(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 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 hashSet = new HashSet(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(item2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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."); } } else { string text = Path.Combine(customPath, "CoordinateTriggerEvents"); Directory.CreateDirectory(text); WriteTemplateIfMissing(Path.Combine(text, "Template_CN.json"), CreateChineseTemplateJson(), log); WriteTemplateIfMissing(Path.Combine(text, "Template_EN.json"), CreateEnglishTemplateJson(), 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(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); } } } private static IEnumerable 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(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); } } } private static bool TryGetMtfoCustomPath(out string customPath) { customPath = string.Empty; try { if (!((BaseChainloader)(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) { if (!File.Exists(path)) { Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllText(path, content); Runtime.LogVerbose("Created template config: " + path); } } 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(); 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); } ValidateUniqueTriggerIDs(configDocument); return configDocument; } private static void ValidateUniqueTriggerIDs(ConfigDocument doc) { ConfigDocument doc2 = doc; HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (PositionTriggerRule positionTrigger in doc2.PositionTriggers) { Check("Position", positionTrigger.ID); } foreach (ScanTriggerRule scanTrigger in doc2.ScanTriggers) { Check("Scan", scanTrigger.ID); } foreach (InteractTriggerRule interactTrigger in doc2.InteractTriggers) { Check("Interact", interactTrigger.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(id); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'. Trigger IDs must be globally unique within loaded configs. Category="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(category); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", File="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(doc2.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(category); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" trigger '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(triggerId); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' uses TriggerMode='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(triggerMode); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' but is missing required Cooldown. Continuous triggers require Cooldown >= "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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(category); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" trigger '"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(triggerId); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' uses TriggerMode='"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(triggerMode); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' with Cooldown="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(cooldown, "0.###"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(". Clamped to "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(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); if (!(text == "anyplayerinside")) { return text == "allplayersinside"; } return true; } private static bool IsContinuousScanTriggerMode(string triggerMode) { string text = Runtime.NormalizeTriggerMode(triggerMode); if (!(text == "onallplayersinsidescan")) { return text == "onallplayersexitedscan"; } return true; } private static bool IsContinuousInteractTriggerMode(string targetType, string triggerMode) { string text = Runtime.NormalizeTargetType(targetType); string text2 = Runtime.NormalizeInteractionTriggerMode(triggerMode); if (!(text == "bigpickup") || (!(text2 == "onbigpickupheld") && !(text2 == "onbigpickupplaced"))) { if (text == "terminal") { if (!(text2 == "onterminalusing")) { return text2 == "onterminalexited"; } return true; } return false; } return true; } private static List ReadInteractTriggerArray(JsonElement array) { //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Expected O, but got Unknown List list = new List(); 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)), TerminalSerial = GetString(item, "TSL", GetString(item, "TerminalTSL", GetString(item, "TerminalTsl", GetString(item, "TerminalSelector", GetString(item, "TerminalSerial", GetString(item, "TerminalSerialNumber", GetString(item, "TerminalSerialText", GetString(item, "SerialText", GetString(item, "SerialLookup", GetString(item, "TerminalSerialLookup", 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), 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)) }; } 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"); 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(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 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 list = new List(); 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(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 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 list = new List(); 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(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 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(); } 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 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 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 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 { value.Clone() }; } return new List(); } 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 CreateChineseTemplateJson() { return "// CoordinateTriggerEvents 1.1.0 中文配置模板\n// 生成位置:<自定义Rundown数据块文件夹>/Custom/CoordinateTriggerEvents/Template_CN.json\n// 模板默认 Enabled=false,不会影响关卡。复制本文件并改名为关卡配置后,把 Enabled 改为 true。\n// 插件允许 // 注释和尾随逗号。支持多个 JSON 文件;只会启用 MainLevelLayoutIDs 匹配当前关卡的配置。\n{\n \"Enabled\": false,\n \"MainLevelLayoutIDs\":0,\n \"Debug\": {//调试模式,供模组开发者查看触发器的实际位置\n \"Enabled\": false,\n \"ShowScanMarkers\": true,\n \"ShowNames\": true,\n \"MarkerColor\": \"#00BFFF\",\n \"LabelColor\": \"#FFFFFF\",\n \"MarkerAlpha\": 0.35,\n \"HeightOffset\": 0.05,\n \"LabelHeightOffset\": 1.0\n },\n \"PositionTriggers\": [\n {\n \"ID\": \"radius_player_count_example\", //当前触发器的唯一id\n \"TriggerAreaMode\": \"Radius\",\n //Radius:按一个世界坐标点和半径生成触发范围。\n\n \"TriggerMode\": \"AnyPlayerEnter\",\n //AnyPlayerEnter 任意玩家进入触发范围触发事件\n\n //AnyPlayerInside 触发范围内有任意玩家根据\"Cooldown\": 1.0,重复触发\n\n //AllPlayersEnter 所有符合条件的玩家进入范围后触发\n\n //AllPlayersInside 所有符合条件的玩家根据\"Cooldown\": 1.0,重复触发\n\n //AnyPlayerExit 任意玩家从范围内离开范围时触发\n\n //AllPlayersExit 所有玩家从范围内离开时触发\n\n \"Enabled\": true,\n \"Position\": {\n \"x\": 0,\n \"y\": 0,\n \"z\": 0\n },\n \"Radius\": 5.0, //触发器大小\n \"Cooldown\": 1.0, //内置冷却cd\n \"RequireAlivePlayers\": true, //玩家倒地不计算为人数\n \"DebugVisible\": true, //开启调试模式\n \"Events\": [], //事件列表\n \"UsePlayerCountEvents\": false,\n // true 时只执行 PlayerCountEvents 中对应人数的事件组;false 时执行 Events内的事件组\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n // 每成功触发一次+1Count;累计 TriggerCycleCount 组后额外触发事件\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"whole_zone_localindex_0\",\n \"TriggerAreaMode\": \"OverrideBigZone\",\n //OverrideBigZone 生成一个覆盖指定ZONE的触发器\n \"TriggerMode\": \"AnyPlayerEnter\",\n \"Enabled\": true,\n \"DimensionIndex\": 0,\n \"Layer\": 0,\n \"LocalIndex\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"DebugVisible\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"whole_area_count_0\",\n \"TriggerAreaMode\": \"OverrideArea\",\n //OverrideArea 生成一个覆盖指定Zone Area区域的触发器\n \"TriggerMode\": \"AnyPlayerEnter\",\n \"Enabled\": true,\n \"DimensionIndex\": 0,\n \"Layer\":0,\n \"LocalIndex\": 0,\n \"Count\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"DebugVisible\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n }\n ],\n \"ScanTriggers\": [\n {\n \"ID\": \"scan_1_activated_player_count\",\n \"TriggerMode\": \"OnScanActivated\",\n //OnScanActivated 玩家激活扫描点时触发事件\n //OnPlayerExitScan 玩家退出扫描点时触发事件\n \"Enabled\": true,\n // Index 使用 ScanPositionOverride / ECC 在 BepInEx 日志中输出的 PuzzleOverrideIndex,索引从 1 开始。\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_player_exit_event\",\n \"TriggerMode\": \"OnPlayerExitScan\",\n \"Enabled\": true,\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_enter_once\",\n \"TriggerMode\": \"OnAllPlayersEnterScan\",\n \"Enabled\": true,\n // 全员进入扫描点时触发一次。\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_inside_repeat\",\n \"TriggerMode\": \"OnAllPlayersInsideScan\",\n \"Enabled\": true,\n // 全员持续在扫描点内时按 Cooldown 重复触发。\n \"Index\": 0,\n \"Cooldown\": 5.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_exit_once\",\n \"TriggerMode\": \"OnAllPlayersExitScan\",\n \"Enabled\": true,\n // 全员曾进入扫描点后全部退出时触发一次。\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_exited_repeat\",\n \"TriggerMode\": \"OnAllPlayersExitedScan\",\n \"Enabled\": true,\n // 全员曾进入扫描点并全部退出后按 Cooldown 重复触发;初始无人不会触发。\n \"Index\": 0,\n \"Cooldown\": 5.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n }\n ],\n \"InteractTriggers\": [\n {\n \"ID\": \"bigpickup_1_pickup_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupPickup\",\n //OnBigPickupPickup 拾取大物品触发事件\n //OnBigPickupDrop 放下大物品触发事件\n \"Enabled\": true,\n // Index 使用 ScanPositionOverride / ECC 在 BepInEx 日志中输出的 BigPickup Item Index,索引从 1 开始。\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"Events\": [],\n \"UsePickupDropCycleEvents\": false,\n \"PickupDropCycleCount\": 0,\n \"PickupDropCycleEvents\": []\n },\n {\n \"ID\": \"bigpickup_1_drop_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupDrop\",\n \"Enabled\": true,\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"bigpickup_1_held_repeat_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupHeld\",\n \"Enabled\": true,\n \"Index\": 0,\n // 玩家拿着该大物品期间按 Cooldown 重复触发,类似 AnyPlayerInside。\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"bigpickup_1_placed_repeat_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupPlaced\",\n \"Enabled\": true,\n \"Index\": 0,\n // 玩家放下该大物品后,只要大物品保持放置状态,就按 Cooldown 重复触发。\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_use_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUse\",\n //OnTerminalUse 使用终端触发事件\n //OnTerminalUnused 退出终端触发事件\n \"Enabled\": true,\n // TerminalSelector 与 AWO TSL 一致:[TERMINAL_DimensionIndex_LayerIndex_ZoneLocalIndex_TerminalIndexInZone]\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_unused_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUnused\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_using_repeat_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUsing\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n // 玩家正在使用该终端期间按 Cooldown 重复触发,类似 AnyPlayerInside。\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_exited_repeat_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalExited\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n // 必须玩家使用过一次该终端并退出后,才会按 Cooldown 重复触发;初始未使用状态不会触发。\n \"Cooldown\": 5.0,\n \"Events\": []\n }\n ]\n}"; } private static string CreateEnglishTemplateJson() { return "// CoordinateTriggerEvents 1.1.0 English configuration template\n// Generated path: /Custom/CoordinateTriggerEvents/Template_EN.json\n// The template defaults to Enabled=false and will not affect levels. Copy this file, rename it as a level config, then set Enabled to true.\n// The plugin allows // comments and trailing commas. Multiple JSON files are supported; only configs whose MainLevelLayoutIDs match the current level are enabled.\n{\n \"Enabled\": false,\n \"MainLevelLayoutIDs\":0,\n \"Debug\": {//Debug mode, for mod developers to view the actual positions of triggers\n \"Enabled\": false,\n \"ShowScanMarkers\": true,\n \"ShowNames\": true,\n \"MarkerColor\": \"#00BFFF\",\n \"LabelColor\": \"#FFFFFF\",\n \"MarkerAlpha\": 0.35,\n \"HeightOffset\": 0.05,\n \"LabelHeightOffset\": 1.0\n },\n \"PositionTriggers\": [\n {\n \"ID\": \"radius_player_count_example\", //Unique ID of the current trigger\n \"TriggerAreaMode\": \"Radius\",\n //Radius: generates a trigger area from a world position and radius.\n\n \"TriggerMode\": \"AnyPlayerEnter\",\n //AnyPlayerEnter Triggers the event when any player enters the trigger area\n\n //AnyPlayerInside Repeatedly triggers while any player is inside the trigger area according to \"Cooldown\": 1.0,\n\n //AllPlayersEnter Triggers after all eligible players enter the area\n\n //AllPlayersInside Repeatedly triggers while all eligible players are inside according to \"Cooldown\": 1.0,\n\n //AnyPlayerExit Triggers when any player leaves the area from inside\n\n //AllPlayersExit Triggers when all players leave the area from inside\n\n \"Enabled\": true,\n \"Position\": {\n \"x\": 0,\n \"y\": 0,\n \"z\": 0\n },\n \"Radius\": 5.0, //Trigger size\n \"Cooldown\": 1.0, //Built-in cooldown\n \"RequireAlivePlayers\": true, //Downed players are not counted as player count\n \"DebugVisible\": true, //Enable debug mode\n \"Events\": [], //Event list\n \"UsePlayerCountEvents\": false,\n // When true, only executes the event group matching the player count in PlayerCountEvents; when false, executes the event group inside Events\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n // Adds 1 Count for every successful trigger; after accumulating TriggerCycleCount groups, triggers additional events\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"whole_zone_localindex_0\",\n \"TriggerAreaMode\": \"OverrideBigZone\",\n //OverrideBigZone generates a trigger that covers the specified ZONE\n \"TriggerMode\": \"AnyPlayerEnter\",\n \"Enabled\": true,\n \"DimensionIndex\": 0,\n \"Layer\": 0,\n \"LocalIndex\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"DebugVisible\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"whole_area_count_0\",\n \"TriggerAreaMode\": \"OverrideArea\",\n //OverrideArea generates a trigger that covers the specified Zone Area\n \"TriggerMode\": \"AnyPlayerEnter\",\n \"Enabled\": true,\n \"DimensionIndex\": 0,\n \"Layer\":0,\n \"LocalIndex\": 0,\n \"Count\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"DebugVisible\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n }\n ],\n \"ScanTriggers\": [\n {\n \"ID\": \"scan_1_activated_player_count\",\n \"TriggerMode\": \"OnScanActivated\",\n //OnScanActivated Triggers the event when players activate the scan\n //OnPlayerExitScan Triggers the event when a player exits the scan\n \"Enabled\": true,\n // Index uses the PuzzleOverrideIndex printed by ScanPositionOverride / ECC in the BepInEx log. The index starts from 1.\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_player_exit_event\",\n \"TriggerMode\": \"OnPlayerExitScan\",\n \"Enabled\": true,\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_enter_once\",\n \"TriggerMode\": \"OnAllPlayersEnterScan\",\n \"Enabled\": true,\n // Triggers once when all players enter the scan.\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_inside_repeat\",\n \"TriggerMode\": \"OnAllPlayersInsideScan\",\n \"Enabled\": true,\n // Repeatedly triggers by Cooldown while all players continuously stay inside the scan.\n \"Index\": 0,\n \"Cooldown\": 5.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_exit_once\",\n \"TriggerMode\": \"OnAllPlayersExitScan\",\n \"Enabled\": true,\n // Triggers once after all players have entered the scan and then all leave it.\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n },\n {\n \"ID\": \"scan_1_all_players_exited_repeat\",\n \"TriggerMode\": \"OnAllPlayersExitedScan\",\n \"Enabled\": true,\n // Repeatedly triggers by Cooldown after all players have entered the scan and then all leave it; it does not trigger when the scan starts empty.\n \"Index\": 0,\n \"Cooldown\": 5.0,\n \"RequireAlivePlayers\": true,\n \"Events\": [],\n \"UsePlayerCountEvents\": false,\n \"PlayerCountEvents\": {\n \"1\": [],\n \"2\": [],\n \"3\": [],\n \"4\": []\n },\n \"UseTriggerCycleEvents\": false,\n \"TriggerCycleCount\": 0,\n \"TriggerCycleEvents\": []\n }\n ],\n \"InteractTriggers\": [\n {\n \"ID\": \"bigpickup_1_pickup_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupPickup\",\n //OnBigPickupPickup Triggers the event when a big pickup is picked up\n //OnBigPickupDrop Triggers the event when a big pickup is dropped\n \"Enabled\": true,\n // Index uses the BigPickup Item Index printed by ScanPositionOverride / ECC in the BepInEx log. The index starts from 1.\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"Events\": [],\n \"UsePickupDropCycleEvents\": false,\n \"PickupDropCycleCount\": 0,\n \"PickupDropCycleEvents\": []\n },\n {\n \"ID\": \"bigpickup_1_drop_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupDrop\",\n \"Enabled\": true,\n \"Index\": 0,\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"bigpickup_1_held_repeat_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupHeld\",\n \"Enabled\": true,\n \"Index\": 0,\n // Repeatedly triggers by Cooldown while the player is carrying this big pickup, similar to AnyPlayerInside.\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"bigpickup_1_placed_repeat_event\",\n \"TargetType\": \"BigPickup\",\n \"TriggerMode\": \"OnBigPickupPlaced\",\n \"Enabled\": true,\n \"Index\": 0,\n // After the player drops this big pickup, repeatedly triggers by Cooldown while the big pickup remains placed.\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_use_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUse\",\n //OnTerminalUse Triggers the event when using a terminal\n //OnTerminalUnused Triggers the event when leaving a terminal\n \"Enabled\": true,\n // TerminalSelector is the same as AWO TSL: [TERMINAL_DimensionIndex_LayerIndex_ZoneLocalIndex_TerminalIndexInZone]\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_unused_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUnused\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n \"Cooldown\": 1.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_using_repeat_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalUsing\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n // Repeatedly triggers by Cooldown while the player is using this terminal, similar to AnyPlayerInside.\n \"Cooldown\": 5.0,\n \"Events\": []\n },\n {\n \"ID\": \"terminal_tsl_exited_repeat_event\",\n \"TargetType\": \"Terminal\",\n \"TriggerMode\": \"OnTerminalExited\",\n \"Enabled\": true,\n \"TerminalSelector\": \"[TERMINAL_0_0_0_0]\",\n // The player must have used and then exited this terminal once before this repeatedly triggers by Cooldown; it will not trigger in the initial unused state.\n \"Cooldown\": 5.0,\n \"Events\": []\n }\n ]\n}"; } } internal static class Runtime { private sealed class ZoneLookupCacheEntry { public readonly List Zones = new List(); 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 JsonElement EventElement; public string OwnerLabel = string.Empty; public float DueTime; } private readonly struct TerminalTslAddress { public readonly int DimensionIndex; public readonly int LayerIndex; public readonly int ZoneLocalIndex; public readonly int TerminalIndexInZone; public TerminalTslAddress(int dimensionIndex, int layerIndex, int zoneLocalIndex, int terminalIndexInZone) { DimensionIndex = dimensionIndex; LayerIndex = layerIndex; ZoneLocalIndex = zoneLocalIndex; TerminalIndexInZone = terminalIndexInZone; } public string ToToken() { return $"TERMINAL_{DimensionIndex}_{LayerIndex}_{ZoneLocalIndex}_{TerminalIndexInZone}"; } public string ToBracketedToken() { return "[" + ToToken() + "]"; } public bool Matches(TerminalTslAddress other) { if (DimensionIndex == other.DimensionIndex && LayerIndex == other.LayerIndex && ZoneLocalIndex == other.ZoneLocalIndex) { return TerminalIndexInZone == other.TerminalIndexInZone; } return false; } } internal static ManualLogSource? Log; internal static readonly bool VerboseConsoleLogging = false; private static readonly Dictionary States = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly List LocalizedTextRoots = new List(); private static float _lastTickTime; private static float _lastLogTime; private static readonly Dictionary LastLogTimesByMessage = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary PositionModeNormalizationCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary ScanModeNormalizationCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary InteractModeNormalizationCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary TargetTypeNormalizationCache = new Dictionary(StringComparer.Ordinal); private const int NormalizationCacheLimit = 512; private static readonly Dictionary PartialDataPersistentIdCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LevelLayoutStringIdCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary? PersistentIdDumpCache; private static readonly Dictionary> TerminalSelectorCache = new Dictionary>(); private static readonly Dictionary TerminalTslAddressCache = new Dictionary(); private static readonly Dictionary TerminalSelectorMatchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet TerminalSelectorCacheMisses = new HashSet(); private static bool TerminalSelectorCacheWarmupComplete; private static float LastTerminalSelectorCacheWarmupAttempt = -999999f; private static readonly Dictionary ZoneLookupCache = new Dictionary(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 bool ActiveTriggerCacheDirty = true; private static uint ActiveTriggerCacheLayoutId = uint.MaxValue; private static string ActiveTriggerCacheLayoutName = string.Empty; private static readonly Queue PendingConfiguredEvents = new Queue(); private const int MaxQueuedConfiguredEventsPerTick = 3; 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; } private static string GetCachedNormalization(Dictionary cache, string? text, Func 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(); 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)); } } } ActiveTriggerCacheLayoutId = currentLevelLayoutId; ActiveTriggerCacheLayoutName = text; ActiveTriggerCacheDirty = false; } } internal static void OnExpeditionStarted() { States.Clear(); LocalizedTextRoots.Clear(); ZoneLookupCache.Clear(); LastLogTimesByMessage.Clear(); PendingConfiguredEvents.Clear(); ClearHotPathRuntimeCaches(); ClearTerminalSelectorCache(); ConfigManager.LoadOrCreate(Log, force: true); MarkActiveTriggerCacheDirty(); EnsureActiveTriggerCache(); uint currentLevelLayoutId = GetCurrentLevelLayoutId(); string value = TryGetLevelLayoutName(currentLevelLayoutId); int count = GetActiveTriggers().Count; int count2 = GetActiveScanTriggers().Count; int count3 = GetActiveInteractTriggers().Count; ScanTriggerManager.Reset(); InteractTriggerManager.Reset(); LogVerbose($"Expedition started. LevelLayoutID={currentLevelLayoutId}, LevelLayoutName='{value}', active coordinate triggers={count}, active scan triggers={count2}, active interact triggers={count3}, configs={ConfigManager.ConfigPathSummary}"); } internal static void Tick() { try { if (Time.realtimeSinceStartup - _lastTickTime < 0.2f) { return; } _lastTickTime = Time.realtimeSinceStartup; ConfigManager.ProcessQueuedReload(Log); if (!GameStateManager.IsInExpedition) { DebugMarkerManager.Clear(); return; } if (ShouldDumpRuntimeBindings()) { ScanTriggerManager.DumpScanIndexesIfNeeded(); InteractTriggerManager.DumpTargetIndexesIfNeeded(); } WarmTerminalSelectorCacheIfNeeded(); ProcessQueuedConfiguredEvents(); InteractTriggerManager.ProcessPendingTerminalEvents(); InteractTriggerManager.ProcessTerminalRepeatEvents(); InteractTriggerManager.ProcessBigPickupRepeatEvents(); ScanTriggerManager.ProcessScanRepeatEvents(); List<(ConfigDocument, PositionTriggerRule)> activeTriggers = GetActiveTriggers(); DebugMarkerManager.UpdateMarkers(activeTriggers); if (activeTriggers.Count == 0) { return; } foreach (var (config, trigger) in activeTriggers) { EvaluateTrigger(config, trigger); } } catch (Exception ex) { LogThrottled("Tick failed: " + ex.GetType().Name + ": " + ex.Message); } } private static List<(ConfigDocument Config, PositionTriggerRule Trigger)> GetActiveTriggers() { EnsureActiveTriggerCache(); return ActivePositionTriggerCache; } internal static List<(ConfigDocument Config, ScanTriggerRule Trigger)> GetActiveScanTriggers() { EnsureActiveTriggerCache(); return ActiveScanTriggerCache; } internal static List<(ConfigDocument Config, InteractTriggerRule Trigger)> GetActiveInteractTriggers() { EnsureActiveTriggerCache(); return ActiveInteractTriggerCache; } private static bool ShouldDumpRuntimeBindings() { return ConfigManager.ShouldDumpRuntimeIndexes(); } private static int QueueConfiguredEventList(IEnumerable events, string ownerLabel, float delaySeconds = 0.05f) { int num = 0; float dueTime = Time.realtimeSinceStartup + Math.Max(0f, delaySeconds); foreach (JsonElement @event in events) { PendingConfiguredEvents.Enqueue(new PendingConfiguredEvent { EventElement = @event.Clone(), OwnerLabel = ownerLabel, DueTime = dueTime }); num++; } return num; } private static void ProcessQueuedConfiguredEvents() { if (PendingConfiguredEvents.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; int num = 0; while (PendingConfiguredEvents.Count > 0 && num < 3) { PendingConfiguredEvent pendingConfiguredEvent = PendingConfiguredEvents.Peek(); if (!(pendingConfiguredEvent.DueTime > realtimeSinceStartup)) { PendingConfiguredEvents.Dequeue(); TryExecuteConfiguredEvent(pendingConfiguredEvent.EventElement, pendingConfiguredEvent.OwnerLabel); num++; continue; } break; } } internal static void FireInteractTrigger(string sourceKind, string eventName, Component? source, PlayerAgent? player, string sourceName, string stateKeySuffix) { int firedDispatchCount = InteractTriggerManager.FiredDispatchCount; foreach (var (config, trigger) in GetActiveInteractTriggers()) { FireInteractTrigger(config, trigger, sourceKind, eventName, source, player, sourceName, stateKeySuffix); } if (InteractTriggerManager.FiredDispatchCount == firedDispatchCount && (NormalizeTargetType(sourceKind) == "bigpickup" || NormalizeTargetType(sourceKind) == "terminal")) { LogVerbose($"No interact trigger matched. Event={eventName}, TargetType={sourceKind}, Source={sourceName}. Check Index/SerialNumber/ItemKey/TerminalSelector in config."); } } internal static void FireBigPickupCycleEvents(Component source, PlayerAgent? player, string sourceName, int completedCycles) { foreach (var (configDocument, interactTriggerRule) in GetActiveInteractTriggers()) { if (!interactTriggerRule.UsePickupDropCycleEvents || interactTriggerRule.PickupDropCycleEvents.Count == 0 || (interactTriggerRule.RequireInExpedition && !GameStateManager.IsInExpedition) || !InteractTriggerMatches(interactTriggerRule, "BigPickup", "OnBigPickupDrop", source, ignoreTriggerMode: true)) { continue; } int num = Math.Max(1, interactTriggerRule.PickupDropCycleCount); if (completedCycles % num == 0) { string key = configDocument.FilePath + "::interact-cycle::" + interactTriggerRule.ID + "::" + InteractTriggerManager.GetRuntimeIndex("BigPickup", source) + "::" + completedCycles; if (!InteractTriggerManager.RuleStates.TryGetValue(key, out TriggerState value)) { value = new TriggerState(); InteractTriggerManager.RuleStates[key] = value; } if (!(interactTriggerRule.Cooldown > 0f) || !(Time.realtimeSinceStartup - value.LastFireTime < interactTriggerRule.Cooldown)) { value.LastFireTime = Time.realtimeSinceStartup; value.Fired = true; int value2 = ExecuteEventList(interactTriggerRule.PickupDropCycleEvents, $"BigPickup pickup/drop cycle trigger '{interactTriggerRule.ID}' cycles={completedCycles}"); LogVerbose($"BigPickup pickup/drop cycle trigger '{interactTriggerRule.ID}' fired. Cycles={completedCycles}, Required={num}, Source={sourceName}, ExecutedEvents={value2}"); } } } } private static void FireInteractTrigger(ConfigDocument config, InteractTriggerRule trigger, string sourceKind, string eventName, Component? source, PlayerAgent? player, string sourceName, string stateKeySuffix) { if ((trigger.RequireInExpedition && !GameStateManager.IsInExpedition) || !InteractTriggerMatches(trigger, sourceKind, eventName, source)) { return; } string key = config.FilePath + "::interact::" + trigger.ID + "::" + stateKeySuffix; if (!InteractTriggerManager.RuleStates.TryGetValue(key, out TriggerState value)) { value = new TriggerState(); InteractTriggerManager.RuleStates[key] = value; } if (trigger.Cooldown > 0f && Time.realtimeSinceStartup - value.LastFireTime < trigger.Cooldown) { return; } value.Fired = true; value.LastFireTime = Time.realtimeSinceStartup; int num = 0; IEnumerable enumerable = trigger.Events.Concat(trigger.WardenEvents); bool flag = NormalizeTargetType(sourceKind) == "terminal" && (NormalizeInteractionTriggerMode(eventName) == "onterminaluse" || NormalizeInteractionTriggerMode(eventName) == "onterminalunused"); if (flag) { num = QueueConfiguredEventList(enumerable, "Interact trigger '" + trigger.ID + "'"); } else { foreach (JsonElement item in enumerable) { if (TryExecuteConfiguredEvent(item, "Interact trigger '" + trigger.ID + "'")) { num++; } } } InteractTriggerManager.FiredDispatchCount++; LogVerbose($"Interact trigger '{trigger.ID}' fired. Event={eventName}, TargetType={sourceKind}, Source={sourceName}, {(flag ? "QueuedEvents" : "ExecutedEvents")}={num}"); } private static bool InteractTriggerMatches(InteractTriggerRule trigger, string sourceKind, string eventName, Component? source, bool ignoreTriggerMode = false) { //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) string text = NormalizeTargetType(trigger.TargetType); string text2 = NormalizeTargetType(sourceKind); if (!string.IsNullOrWhiteSpace(text) && text != "any" && text != text2) { return false; } if (!ignoreTriggerMode) { string text3 = NormalizeInteractionTriggerMode(trigger.TriggerMode); string text4 = NormalizeInteractionTriggerMode(eventName); if (!string.IsNullOrWhiteSpace(text3) && text3 != text4) { return false; } } if ((Object)(object)source == (Object)null) { return true; } if (trigger.Index < 0 && trigger.InstanceID < 0 && trigger.SyncID < 0 && trigger.SerialNumber < 0 && trigger.DataBlockID == 0 && trigger.ItemID == 0 && string.IsNullOrWhiteSpace(trigger.ItemKey) && string.IsNullOrWhiteSpace(trigger.PublicName) && string.IsNullOrWhiteSpace(trigger.TerminalSerial) && string.IsNullOrWhiteSpace(trigger.WorldEventObjectFilter) && string.IsNullOrWhiteSpace(trigger.InternalName) && trigger.Position == null) { return true; } if (trigger.Index >= 0) { if (text2 == "bigpickup") { if (trigger.Index <= 0) { LogThrottled($"BigPickup trigger '{trigger.ID}' uses invalid Index={trigger.Index}. ScanPosOverride/ECC BigPickup item indices start at 1."); return false; } if (!SpoIndexResolver.TryGetBigPickupSpoIndex(source, out int index, out string _)) { LogThrottled($"BigPickup trigger '{trigger.ID}' cannot match: SPO/ECC BigPickup Item Index is unavailable. ConfigIndex={trigger.Index}, Source={((source != null) ? ((Object)source).name : null) ?? ""}"); return false; } if (index != trigger.Index) { return false; } } else { int index = InteractTriggerManager.GetRuntimeIndex(text2, source); if (index < 0) { index = TryGetIntMember(source, "Index", "m_index", "m_terminalIndex", "m_terminalSerialIndex", "PuzzleOverrideIndex"); } if (index != trigger.Index) { return false; } } } if (!string.IsNullOrWhiteSpace(trigger.WorldEventObjectFilter) && !ObjectMatchesFilter(source, trigger.WorldEventObjectFilter)) { return false; } if (!string.IsNullOrWhiteSpace(trigger.InternalName) && TryGetPublicObjectName(source).IndexOf(trigger.InternalName, StringComparison.OrdinalIgnoreCase) < 0) { return false; } if (trigger.DataBlockID != 0 && TryGetUIntMember(source, "DataBlockID", "ItemDataBlockID", "m_dataBlockID", "m_itemDataBlockID", "ItemID", "m_itemID") != trigger.DataBlockID) { return false; } if (trigger.InstanceID >= 0) { int num = -1; try { num = ((Object)source).GetInstanceID(); } catch { } if (num != trigger.InstanceID) { return false; } } if (trigger.SyncID >= 0 && TryGetIntMember(source, "SyncID", "m_syncID") != trigger.SyncID) { return false; } if (trigger.SerialNumber >= 0 && TryGetIntMember(source, "m_serialNumber", "SerialNumber", "m_serial") != trigger.SerialNumber) { return false; } if (!string.IsNullOrWhiteSpace(trigger.ItemKey) && !string.Equals(TryGetStringMember(source, "m_itemKey", "ItemKey", "Key"), trigger.ItemKey, StringComparison.OrdinalIgnoreCase)) { return false; } if (!string.IsNullOrWhiteSpace(trigger.TerminalSerial)) { if (text2 != "terminal") { return false; } if (!TerminalMatchesSelector(source, trigger.TerminalSerial)) { return false; } } if (!string.IsNullOrWhiteSpace(trigger.PublicName) && TryGetPublicObjectName(source).IndexOf(trigger.PublicName, StringComparison.OrdinalIgnoreCase) < 0) { return false; } if (trigger.Position != null) { Vector3 position; try { position = source.transform.position; } catch { return false; } Vector3 val = trigger.Position.ToVector3(); float num2 = Math.Max(0.01f, trigger.Radius); Vector3 val2 = position - val; if (((Vector3)(ref val2)).sqrMagnitude > num2 * num2) { return false; } } return true; } internal static string NormalizeTargetType(string text) { return GetCachedNormalization(TargetTypeNormalizationCache, text, (string value) => string.IsNullOrWhiteSpace(value) ? "any" : (value.Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant() switch { "terminal" => "terminal", "computerterminal" => "terminal", "interact" => "interact", "interaction" => "interact", "position" => "position", "coordinate" => "coordinate", "scan" => "scan", "bioscan" => "bioscan", "bigpickup" => "bigpickup", "largepickup" => "bigpickup", "carryitem" => "bigpickup", "carryitempickup" => "bigpickup", "any" => "any", "all" => "any", _ => value.Trim().ToLowerInvariant(), })); } internal static string NormalizeInteractionTriggerMode(string text) { return GetCachedNormalization(InteractModeNormalizationCache, text, (string value) => string.IsNullOrWhiteSpace(value) ? string.Empty : (value.Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant() switch { "pickup" => "onbigpickuppickup", "pickedup" => "onbigpickuppickup", "onpickup" => "onbigpickuppickup", "onpickedup" => "onbigpickuppickup", "bigpickuppickup" => "onbigpickuppickup", "onbigpickuppickup" => "onbigpickuppickup", "bigpickuppickedup" => "onbigpickuppickup", "onbigpickup" => "onbigpickuppickup", "held" => "onbigpickupheld", "holding" => "onbigpickupheld", "bigpickupheld" => "onbigpickupheld", "bigpickupholding" => "onbigpickupheld", "onbigpickupheld" => "onbigpickupheld", "onbigpickupholding" => "onbigpickupheld", "onbigpickuppickuprepeat" => "onbigpickupheld", "onbigpickuppickupinside" => "onbigpickupheld", "drop" => "onbigpickupdrop", "dropped" => "onbigpickupdrop", "ondrop" => "onbigpickupdrop", "onbigpickupdrop" => "onbigpickupdrop", "onbigpickupdropped" => "onbigpickupdrop", "placed" => "onbigpickupplaced", "inlevel" => "onbigpickupplaced", "bigpickupplaced" => "onbigpickupplaced", "bigpickupinlevel" => "onbigpickupplaced", "onbigpickupplaced" => "onbigpickupplaced", "onbigpickupinlevel" => "onbigpickupplaced", "onbigpickupdroprepeat" => "onbigpickupplaced", "onbigpickupdropinside" => "onbigpickupplaced", "terminaluse" => "onterminaluse", "useterminal" => "onterminaluse", "use" => "onterminaluse", "onuse" => "onterminaluse", "onterminaluse" => "onterminaluse", "terminalusing" => "onterminalusing", "usingterminal" => "onterminalusing", "using" => "onterminalusing", "onterminalusing" => "onterminalusing", "onterminaluserepeat" => "onterminalusing", "onterminaluseinside" => "onterminalusing", "terminalheld" => "onterminalusing", "terminalunused" => "onterminalunused", "terminalunuse" => "onterminalunused", "terminalexit" => "onterminalunused", "unuse" => "onterminalunused", "exit" => "onterminalunused", "onterminalunused" => "onterminalunused", "onterminalunuse" => "onterminalunused", "onterminalexit" => "onterminalunused", "terminalexited" => "onterminalexited", "afterterminalexit" => "onterminalexited", "afterterminalunused" => "onterminalexited", "onterminalexited" => "onterminalexited", "onterminalunusedrepeat" => "onterminalexited", "onterminalexitrepeat" => "onterminalexited", "onterminalexitinside" => "onterminalexited", _ => value.Trim().ToLowerInvariant(), })); } private static int TryGetIntMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && int.TryParse(obj2.ToString(), out var result)) { return result; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && int.TryParse(obj2.ToString(), out result)) { return result; } } catch { } } return -1; } private static uint TryGetUIntMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && uint.TryParse(obj2.ToString(), out var result)) { return result; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && uint.TryParse(obj2.ToString(), out result)) { return result; } } catch { } } return 0u; } private static bool ObjectMatchesFilter(Component source, string filter) { if (string.IsNullOrWhiteSpace(filter)) { return true; } string text = filter.Trim(); foreach (string item in EnumerateObjectSelectorStrings(source)) { if (!string.IsNullOrWhiteSpace(item)) { if (string.Equals(item, text, StringComparison.OrdinalIgnoreCase)) { return true; } if (item.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } return false; } private static IEnumerable EnumerateObjectSelectorStrings(Component source) { yield return TryGetPublicObjectName(source); yield return TryGetStringMember(source, "WorldEventObjectFilter", "m_worldEventObjectFilter", "Filter", "m_filter", "m_publicName", "PublicName", "ItemKey", "m_itemKey", "Key", "m_serial", "m_serialNumber", "SerialNumber", "TerminalSerial", "m_terminalSerial"); string text = string.Empty; try { if ((Object)(object)source.gameObject != (Object)null) { text = ((Object)source.gameObject).name; } } catch { text = string.Empty; } if (!string.IsNullOrWhiteSpace(text)) { yield return text; } text = string.Empty; try { if ((Object)(object)source.transform != (Object)null && (Object)(object)source.transform.parent != (Object)null) { text = ((Object)source.transform.parent).name; } } catch { text = string.Empty; } if (!string.IsNullOrWhiteSpace(text)) { yield return text; } _ = string.Empty; try { text = ((Object)source).ToString() ?? string.Empty; } catch { text = string.Empty; } if (!string.IsNullOrWhiteSpace(text)) { yield return text; } } private static string TryGetStringMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } } catch { } } return string.Empty; } private static string TryGetPublicObjectName(Component source) { try { object obj = ((object)source).GetType().GetProperty("PublicName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(source); if (obj != null) { return obj.ToString() ?? string.Empty; } } catch { } try { if ((Object)(object)source.gameObject != (Object)null) { return ((Object)source.gameObject).name ?? string.Empty; } } catch { } return ((Object)source).ToString() ?? string.Empty; } internal static void ClearTerminalSelectorCache() { TerminalSelectorCache.Clear(); TerminalTslAddressCache.Clear(); TerminalSelectorMatchCache.Clear(); TerminalSelectorCacheMisses.Clear(); TerminalSelectorCacheWarmupComplete = false; LastTerminalSelectorCacheWarmupAttempt = -999999f; } internal static void CacheTerminalSelectorsForTerminal(LG_ComputerTerminal terminal, int runtimeIndex) { CacheTerminalSelectorsForTerminalCore(terminal, runtimeIndex, hasKnownAddress: false, default(TerminalTslAddress)); } private static void CacheTerminalSelectorsForTerminalCore(LG_ComputerTerminal terminal, int runtimeIndex, bool hasKnownAddress, TerminalTslAddress knownAddress) { if ((Object)(object)terminal == (Object)null) { return; } try { int instanceID = ((Object)terminal).GetInstanceID(); HashSet selectors = new HashSet(StringComparer.OrdinalIgnoreCase); string text = TryGetPublicObjectName((Component)(object)terminal); string value2 = TryGetStringMember(terminal, "ItemKey", "_ItemKey_k__BackingField", "m_itemKey", "Key"); int num = TryGetIntMember(terminal, "m_serialNumber", "SerialNumber", "Serial", "TerminalSerialNumber"); string value3 = TryGetNestedStringMember(terminal, "m_serial", "Serial", "m_serialText", "m_terminalSerial", "TerminalSerial", "TerminalSerialText", "SerialText", "SerialLookup", "TerminalSerialLookup"); Add(text); Add(value2); Add(value3); Add(TryGetStringMember(terminal, "TerminalSerial", "TerminalSerialText", "TerminalSelector", "TerminalTSL", "TSL", "SerialText", "SerialLookup", "TerminalSerialLookup")); if (num >= 0) { Add(num.ToString()); Add($"TERMINAL_{num}"); Add($"[TERMINAL_{num}]"); } if (runtimeIndex >= 0) { Add(runtimeIndex.ToString()); Add($"TERMINAL_{runtimeIndex}"); Add($"[TERMINAL_{runtimeIndex}]"); } TerminalTslAddress value4; if (hasKnownAddress) { value4 = knownAddress; TerminalTslAddressCache[instanceID] = value4; Add(value4.ToToken()); Add(value4.ToBracketedToken()); } else if (TryGetTerminalTslAddress(terminal, out value4)) { TerminalTslAddressCache[instanceID] = value4; Add(value4.ToToken()); Add(value4.ToBracketedToken()); } TerminalSelectorCache[instanceID] = selectors; TerminalSelectorCacheMisses.Remove(instanceID); LogVerbose("Cached terminal selectors for " + text + ": " + string.Join(", ", selectors.Take(12))); void Add(string value) { if (!string.IsNullOrWhiteSpace(value)) { selectors.Add(NormalizeSelector(value)); } } } catch (Exception ex) { LogThrottled("Terminal selector cache build failed: " + ex.GetType().Name + ": " + ex.Message); } } internal static void WarmTerminalSelectorCacheIfNeeded(bool force = false) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected I4, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected I4, but got Unknown if (!GameStateManager.IsInExpedition || (!force && TerminalSelectorCacheWarmupComplete) || (!force && !HasActiveTerminalSelectorTriggers())) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!force && realtimeSinceStartup - LastTerminalSelectorCacheWarmupAttempt < 1f) { return; } LastTerminalSelectorCacheWarmupAttempt = realtimeSinceStartup; try { int num = 0; HashSet hashSet = new HashSet(); foreach (LG_Zone item in Object.FindObjectsOfType()) { if ((Object)(object)item == (Object)null || item.TerminalsSpawnedInZone == null) { continue; } int dimensionIndex = (int)item.DimensionIndex; int layerIndex = TryGetLayerIndex(item); int zoneLocalIndex = (int)item.LocalIndex; int count = item.TerminalsSpawnedInZone.Count; for (int i = 0; i < count; i++) { LG_ComputerTerminal val = item.TerminalsSpawnedInZone[i]; if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); if (hashSet.Add(instanceID)) { int runtimeIndex = InteractTriggerManager.GetRuntimeIndex("Terminal", (Component?)(object)val); TerminalTslAddress knownAddress = new TerminalTslAddress(dimensionIndex, layerIndex, zoneLocalIndex, i); CacheTerminalSelectorsForTerminalCore(val, runtimeIndex, hasKnownAddress: true, knownAddress); num++; } } } } foreach (LG_ComputerTerminal item2 in Object.FindObjectsOfType()) { if (!((Object)(object)item2 == (Object)null)) { int instanceID2 = ((Object)item2).GetInstanceID(); if (!hashSet.Contains(instanceID2)) { int runtimeIndex2 = InteractTriggerManager.GetRuntimeIndex("Terminal", (Component?)(object)item2); CacheTerminalSelectorsForTerminalCore(item2, runtimeIndex2, hasKnownAddress: false, default(TerminalTslAddress)); num++; } } } if (num > 0) { TerminalSelectorCacheWarmupComplete = true; LogVerbose($"Terminal selector cache warmup complete. CachedTerminals={num}"); } } catch (Exception ex) { LogThrottled("Terminal selector cache warmup failed: " + ex.GetType().Name + ": " + ex.Message); } } private static bool HasActiveTerminalSelectorTriggers() { try { foreach (var activeInteractTrigger in GetActiveInteractTriggers()) { InteractTriggerRule item = activeInteractTrigger.Trigger; if (!(NormalizeTargetType(item.TargetType) != "terminal") && !string.IsNullOrWhiteSpace(item.TerminalSerial)) { return true; } } } catch { } return false; } private static bool TerminalMatchesSelector(Component source, string selector) { if (string.IsNullOrWhiteSpace(selector)) { return true; } string text = NormalizeSelector(selector); string item = text.Trim('[', ']'); LG_ComputerTerminal val = TryGetLGTerminal(source); if ((Object)(object)val != (Object)null) { try { int instanceID = ((Object)val).GetInstanceID(); string key = instanceID + "::" + text; if (TerminalSelectorMatchCache.TryGetValue(key, out var value)) { return value; } if (!TerminalSelectorCache.ContainsKey(instanceID) && !TerminalSelectorCacheMisses.Contains(instanceID)) { WarmTerminalSelectorCacheIfNeeded(force: true); if (!TerminalSelectorCache.ContainsKey(instanceID)) { int runtimeIndex = InteractTriggerManager.GetRuntimeIndex("Terminal", (Component?)(object)val); CacheTerminalSelectorsForTerminal(val, runtimeIndex); if (!TerminalSelectorCache.ContainsKey(instanceID)) { TerminalSelectorCacheMisses.Add(instanceID); } } } if (TerminalSelectorCache.TryGetValue(instanceID, out HashSet value2)) { bool flag = value2.Contains(text) || value2.Contains(item); if (!flag && TryParseAwoTerminalSelector(selector, out var address) && TryGetTerminalTslAddress(val, out var address2)) { flag = address2.Matches(address); } if (!flag && TryParseSingleTerminalIndex(selector, out var index)) { flag = InteractTriggerManager.GetRuntimeIndex("terminal", (Component?)(object)val) == index; } TerminalSelectorMatchCache[key] = flag; return flag; } } catch { } } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string item2 in EnumerateTerminalSelectorStrings(source)) { if (!string.IsNullOrWhiteSpace(item2)) { hashSet.Add(NormalizeSelector(item2)); } } bool flag2 = hashSet.Contains(text) || hashSet.Contains(item); if (!flag2 && TryParseAwoTerminalSelector(selector, out var address3)) { LG_ComputerTerminal val2 = TryGetLGTerminal(source); if ((Object)(object)val2 != (Object)null && TryGetTerminalTslAddress(val2, out var address4)) { hashSet.Add(NormalizeSelector(address4.ToToken())); hashSet.Add(NormalizeSelector(address4.ToBracketedToken())); flag2 = address4.Matches(address3); } } if (!flag2 && TryParseSingleTerminalIndex(selector, out var index2)) { flag2 = InteractTriggerManager.GetRuntimeIndex("terminal", source) == index2; } if (!flag2) { LogThrottled($"Terminal selector '{selector}' did not match source '{TryGetPublicObjectName(source)}'. Candidates={string.Join(", ", hashSet.Take(18))}"); } return flag2; } private static IEnumerable EnumerateTerminalSelectorStrings(Component source) { yield return TryGetPublicObjectName(source); yield return TryGetStringMember(source, "m_serialNumber", "SerialNumber", "TerminalSerial", "m_terminalSerial", "ItemKey"); yield return TryGetNestedStringMember(source, "m_serial", "Serial", "m_serialText"); LG_ComputerTerminal terminal = TryGetLGTerminal(source); if ((Object)(object)terminal != (Object)null) { int runtimeIndex = InteractTriggerManager.GetRuntimeIndex("terminal", (Component?)(object)terminal); int serialNumber = TryGetIntMember(terminal, "m_serialNumber", "SerialNumber"); string serialText = TryGetNestedStringMember(terminal, "m_serial", "Serial", "m_serialText"); string itemKey = TryGetStringMember(terminal, "ItemKey", "_ItemKey_k__BackingField"); yield return TryGetPublicObjectName((Component)(object)terminal); yield return itemKey; yield return (serialNumber >= 0) ? serialNumber.ToString() : string.Empty; yield return serialText; yield return (runtimeIndex >= 0) ? $"TERMINAL_{runtimeIndex}" : string.Empty; yield return (runtimeIndex >= 0) ? $"[TERMINAL_{runtimeIndex}]" : string.Empty; yield return (serialNumber >= 0) ? $"TERMINAL_{serialNumber}" : string.Empty; yield return (serialNumber >= 0) ? $"[TERMINAL_{serialNumber}]" : string.Empty; if (TryGetTerminalTslAddress(terminal, out var address)) { yield return address.ToToken(); yield return address.ToBracketedToken(); } } } private static LG_ComputerTerminal? TryGetLGTerminal(Component source) { try { LG_ComputerTerminal val = (LG_ComputerTerminal)(object)((source is LG_ComputerTerminal) ? source : null); if (val != null) { return val; } } catch { } try { Interact_ComputerTerminal val2 = (Interact_ComputerTerminal)(object)((source is Interact_ComputerTerminal) ? source : null); if (val2 != null && (Object)(object)val2.m_terminal != (Object)null) { return val2.m_terminal; } } catch { } try { object? obj3 = TryGetObjectMember(source, "m_terminal", "Terminal", "terminal"); LG_ComputerTerminal val3 = (LG_ComputerTerminal)((obj3 is LG_ComputerTerminal) ? obj3 : null); if (val3 != null) { return val3; } } catch { } try { LG_ComputerTerminal componentInParent = source.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } } catch { } try { LG_ComputerTerminal componentInChildren = source.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } } catch { } return null; } private static object? TryGetObjectMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2; } } catch { } } return null; } private static int TryGetCollectionCount(object? collection) { if (collection == null) { return 0; } try { if (collection is ICollection collection2) { return collection2.Count; } } catch { } Type type = collection.GetType(); string[] array = new string[6] { "Count", "Length", "count", "length", "m_count", "_size" }; foreach (string name in array) { try { if (TryConvertToInt(type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(collection), out var result)) { return result; } } catch { } try { if (TryConvertToInt(type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(collection), out var result2)) { return result2; } } catch { } } try { if (TryConvertToInt(type.GetMethod("get_Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(collection, Array.Empty()), out var result3)) { return result3; } } catch { } return 0; } private static bool TryConvertToInt(object? value, out int result) { result = 0; if (value == null) { return false; } try { if (!(value is int num)) { if (!(value is uint num2)) { if (!(value is long num3)) { if (value is short num4) { result = num4; return true; } if (value is byte b) { result = b; return true; } } else if (num3 >= int.MinValue && num3 <= int.MaxValue) { result = (int)num3; return true; } } else if (num2 <= int.MaxValue) { result = (int)num2; return true; } return int.TryParse(value.ToString(), out result); } result = num; return true; } catch { result = 0; return false; } } private static List CollectionToObjectList(object? collection, int maxCount = 512) { List list = new List(); if (collection == null) { return list; } try { if (collection is IEnumerable enumerable) { foreach (object item2 in enumerable) { list.Add(item2); if (list.Count >= maxCount) { return list; } } return list; } } catch { } int num = Math.Min(TryGetCollectionCount(collection), maxCount); if (num <= 0) { return list; } Type type = collection.GetType(); MethodInfo methodInfo = null; try { methodInfo = type.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null); } catch { } PropertyInfo propertyInfo = null; try { propertyInfo = type.GetProperty("Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, new Type[1] { typeof(int) }, null); } catch { } for (int i = 0; i < num; i++) { try { object item = ((methodInfo != null) ? methodInfo.Invoke(collection, new object[1] { i }) : propertyInfo?.GetValue(collection, new object[1] { i })); list.Add(item); } catch { } } return list; } private static string TryGetNestedStringMember(object obj, params string[] names) { object obj2 = TryGetObjectMember(obj, names); if (obj2 == null) { return string.Empty; } try { object obj3 = (obj2.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? obj2.GetType().GetProperty("Text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj2); if (obj3 != null) { return obj3.ToString() ?? string.Empty; } } catch { } return obj2.ToString() ?? string.Empty; } private static string NormalizeSelector(string text) { return (text ?? string.Empty).Trim().Trim('"').Trim() .ToUpperInvariant(); } private static bool TryParseAwoTerminalSelector(string selector, out TerminalTslAddress address) { address = default(TerminalTslAddress); string text = selector.Trim().Trim('[', ']').ToUpperInvariant(); if (!text.StartsWith("TERMINAL_", StringComparison.OrdinalIgnoreCase)) { return false; } string[] array = text.Split('_', StringSplitOptions.RemoveEmptyEntries); if (array.Length != 5) { return false; } if (!int.TryParse(array[1], out var result)) { return false; } if (!int.TryParse(array[2], out var result2)) { return false; } if (!int.TryParse(array[3], out var result3)) { return false; } if (!int.TryParse(array[4], out var result4)) { return false; } address = new TerminalTslAddress(result, result2, result3, result4); return true; } private static bool TryParseSingleTerminalIndex(string selector, out int index) { index = -1; string text = selector.Trim().Trim('[', ']').ToUpperInvariant(); if (!text.StartsWith("TERMINAL_", StringComparison.OrdinalIgnoreCase)) { return false; } string[] array = text.Split('_', StringSplitOptions.RemoveEmptyEntries); if (array.Length != 2) { return false; } return int.TryParse(array[1], out index); } private static bool TryGetTerminalTslAddress(LG_ComputerTerminal terminal, out TerminalTslAddress address) { address = default(TerminalTslAddress); if ((Object)(object)terminal == (Object)null) { return false; } try { int instanceID = ((Object)terminal).GetInstanceID(); if (TerminalTslAddressCache.TryGetValue(instanceID, out address)) { return true; } if (!TerminalSelectorCacheWarmupComplete) { WarmTerminalSelectorCacheIfNeeded(force: true); if (TerminalTslAddressCache.TryGetValue(instanceID, out address)) { return true; } } } catch (Exception ex) { LogThrottled("Could not resolve terminal AWO TSL address from cache: " + ex.GetType().Name + ": " + ex.Message); } return false; } internal static string GetTerminalTslSelectorText(LG_ComputerTerminal terminal) { if ((Object)(object)terminal != (Object)null && TryGetTerminalTslAddress(terminal, out var address)) { return address.ToBracketedToken(); } return ""; } private static int TryGetLayerIndex(LG_Zone zone) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected I4, but got Unknown try { if (zone.Layer != null) { return (int)zone.Layer.m_type; } } catch { } try { object? obj2 = TryGetObjectMember(zone, "m_layer", "Layer"); LG_Layer val = (LG_Layer)((obj2 is LG_Layer) ? obj2 : null); if (val != null) { return (int)val.m_type; } } catch { } return 0; } private static bool IsSameUnityObject(Object? a, Object? b) { if (a == (Object)null || b == (Object)null) { return false; } try { return a.GetInstanceID() == b.GetInstanceID(); } catch { return false; } } private static bool TryResolveTerminalSelectorViaAwo(string selector, out string resolved) { resolved = string.Empty; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] types; try { types = assembly.GetTypes(); } catch { continue; } Type[] array = types; foreach (Type type in array) { string text = type.FullName ?? type.Name; if (text.IndexOf("TerminalSerialLookup", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("TerminalSerial", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("TSL", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.ReturnType != typeof(string)) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && !(parameters[0].ParameterType != typeof(string))) { string name = methodInfo.Name; if ((name.IndexOf("Resolve", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Lookup", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Format", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Parse", StringComparison.OrdinalIgnoreCase) >= 0) && methodInfo.Invoke(null, new object[1] { selector }) is string text2 && !string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, selector, StringComparison.OrdinalIgnoreCase)) { resolved = text2; return true; } } } } } } catch (Exception ex) { LogThrottled($"AWO/TSL reflection lookup failed for '{selector}': {ex.GetType().Name}: {ex.Message}"); } return false; } internal static void FireScanTrigger(ConfigDocument config, ScanTriggerRule trigger, string eventName, string sourceName, int puzzleIndex, int playersInScan, string stateKeySuffix) { if (trigger.RequireInExpedition && !GameStateManager.IsInExpedition) { return; } if (trigger.RequireAlivePlayers && !HasAnyAlivePlayer()) { LogThrottled("Scan trigger '" + trigger.ID + "' skipped because RequireAlivePlayers=true and no alive players are currently available."); } else { if (trigger.PuzzleOverrideIndex >= 0 && trigger.PuzzleOverrideIndex != puzzleIndex) { return; } string a = NormalizeTriggerMode(trigger.TriggerMode); string b = NormalizeTriggerMode(eventName); if (!string.Equals(a, b, StringComparison.OrdinalIgnoreCase)) { return; } if (trigger.UsePlayerCountEvents && (playersInScan < 1 || playersInScan > 4)) { LogVerbose($"Scan trigger '{trigger.ID}' matched Event={eventName}, Index={puzzleIndex}, but UsePlayerCountEvents=true and PlayersInScan={playersInScan}; waiting for a 1-4 player count event group and not executing Events/WardenEvents."); return; } string key = config.FilePath + "::scan::" + trigger.ID + "::" + stateKeySuffix; if (!ScanTriggerManager.RuleStates.TryGetValue(key, out TriggerState value)) { value = new TriggerState(); ScanTriggerManager.RuleStates[key] = value; } if (trigger.Cooldown > 0f && Time.realtimeSinceStartup - value.LastFireTime < trigger.Cooldown) { return; } value.Fired = true; value.LastFireTime = Time.realtimeSinceStartup; int num = 0; IEnumerable enumerable = ((!trigger.UsePlayerCountEvents) ? trigger.Events.Concat(trigger.WardenEvents) : ((playersInScan >= 1 && playersInScan <= 4) ? GetEventsForPlayerCount(trigger, playersInScan) : Enumerable.Empty())); foreach (JsonElement item in enumerable) { if (TryExecuteConfiguredEvent(item, "Scan trigger '" + trigger.ID + "'")) { num++; } } HandleScanTriggerCycle(config, trigger, eventName, puzzleIndex); LogVerbose($"Scan trigger '{trigger.ID}' fired. Event={eventName}, Index={puzzleIndex}, PlayersInScan={playersInScan}, UsePlayerCountEvents={trigger.UsePlayerCountEvents}, Source={sourceName}, ExecutedEvents={num}"); } } private static void HandleScanTriggerCycle(ConfigDocument config, ScanTriggerRule trigger, string eventName, int puzzleIndex) { if (trigger.UseTriggerCycleEvents && trigger.TriggerCycleEvents.Count != 0) { string key = config.FilePath + "::scan-cycle::" + trigger.ID + "::" + NormalizeTriggerMode(eventName) + "::" + puzzleIndex; if (!ScanTriggerManager.RuleStates.TryGetValue(key, out TriggerState value)) { value = new TriggerState(); ScanTriggerManager.RuleStates[key] = value; } value.CompletedCycles++; int num = Math.Max(1, trigger.TriggerCycleCount); LogVerbose($"Scan trigger cycle progress. ID={trigger.ID}, Event={eventName}, Index={puzzleIndex}, Cycles={value.CompletedCycles}/{num}"); if (value.CompletedCycles >= num) { value.CompletedCycles = 0; int value2 = ExecuteEventList(trigger.TriggerCycleEvents, $"Scan trigger '{trigger.ID}' cycle Event={eventName} Index={puzzleIndex}"); LogVerbose($"Scan trigger cycle events fired. ID={trigger.ID}, Event={eventName}, Index={puzzleIndex}, ExecutedEvents={value2}"); } } } internal static string NormalizeTriggerMode(string text) { return GetCachedNormalization(ScanModeNormalizationCache, text, (string value) => string.IsNullOrWhiteSpace(value) ? string.Empty : (value.Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant() switch { "activate" => "onscanactivated", "activated" => "onscanactivated", "scanactivated" => "onscanactivated", "onscanactivate" => "onscanactivated", "onscanactivated" => "onscanactivated", "allenter" => "onallplayersenterscan", "allplayersenter" => "onallplayersenterscan", "allplayersentered" => "onallplayersenterscan", "allplayersenterscan" => "onallplayersenterscan", "allplayersenteredscan" => "onallplayersenterscan", "onallplayersenter" => "onallplayersenterscan", "onallplayersentered" => "onallplayersenterscan", "onallplayersenterscan" => "onallplayersenterscan", "onallplayersenteredscan" => "onallplayersenterscan", "allinside" => "onallplayersinsidescan", "allplayersinside" => "onallplayersinsidescan", "allplayersinscan" => "onallplayersinsidescan", "allplayersinsidescan" => "onallplayersinsidescan", "onallplayersinside" => "onallplayersinsidescan", "onallplayersinscan" => "onallplayersinsidescan", "onallplayersinsidescan" => "onallplayersinsidescan", "allexit" => "onallplayersexitscan", "allleave" => "onallplayersexitscan", "allplayersexit" => "onallplayersexitscan", "allplayersleave" => "onallplayersexitscan", "allplayersexitscan" => "onallplayersexitscan", "allplayersleavescan" => "onallplayersexitscan", "onallplayersexit" => "onallplayersexitscan", "onallplayersleave" => "onallplayersexitscan", "onallplayersexitscan" => "onallplayersexitscan", "onallplayersleavescan" => "onallplayersexitscan", "alloutside" => "onallplayersexitedscan", "allexited" => "onallplayersexitedscan", "allplayersoutside" => "onallplayersexitedscan", "allplayersexited" => "onallplayersexitedscan", "allplayersoutscan" => "onallplayersexitedscan", "allplayersoutsidescan" => "onallplayersexitedscan", "allplayersexitedscan" => "onallplayersexitedscan", "onallplayersoutside" => "onallplayersexitedscan", "onallplayersexited" => "onallplayersexitedscan", "onallplayersoutscan" => "onallplayersexitedscan", "onallplayersoutsidescan" => "onallplayersexitedscan", "onallplayersexitedscan" => "onallplayersexitedscan", "exit" => "onplayerexitscan", "leave" => "onplayerexitscan", "playerexit" => "onplayerexitscan", "playerleave" => "onplayerexitscan", "playerleavescan" => "onplayerexitscan", "playerexitscan" => "onplayerexitscan", "onplayerexit" => "onplayerexitscan", "onplayerleave" => "onplayerexitscan", "onplayerleavescan" => "onplayerexitscan", "onplayerexitscan" => "onplayerexitscan", _ => value.Trim().ToLowerInvariant(), })); } private static void EvaluateTrigger(ConfigDocument config, PositionTriggerRule trigger) { if (trigger.RequireInExpedition && !GameStateManager.IsInExpedition) { return; } string key = config.FilePath + "::" + trigger.ID; if (!States.TryGetValue(key, out TriggerState value)) { value = new TriggerState(); States[key] = value; } if (trigger.Cooldown > 0f && Time.realtimeSinceStartup - value.LastFireTime < trigger.Cooldown) { return; } int num = CountPlayersInsideTriggerArea(trigger); int lastInsidePlayerCount = value.LastInsidePlayerCount; bool flag = num > 0; bool wasInside = value.WasInside; bool usePlayerCountEvents = trigger.UsePlayerCountEvents; string text = NormalizePositionTriggerMode(trigger.TriggerMode); if ((text == "anyplayerinside" || text == "allplayersinside") && trigger.Cooldown < 1f) { trigger.Cooldown = 1f; } if (usePlayerCountEvents) { bool flag2 = false; int num2 = num; int num3 = CountEligiblePlayers(trigger); switch (text) { case "anyplayerinside": flag2 = flag; num2 = num; break; case "allplayersinside": flag2 = num3 > 0 && num >= num3; num2 = num; break; case "allplayersexit": flag2 = wasInside && !flag; num2 = Math.Max(1, lastInsidePlayerCount); break; case "anyplayerexit": flag2 = lastInsidePlayerCount > num; num2 = Math.Max(1, lastInsidePlayerCount); break; case "allplayersenter": flag2 = !wasInside && num3 > 0 && num >= num3; num2 = num; break; default: flag2 = num > lastInsidePlayerCount && num >= 1; num2 = num; break; } value.WasInside = flag; value.LastInsidePlayerCount = num; if (flag2 && num2 >= 1 && num2 <= 4) { FireTriggerForPlayerCount(trigger, value, num2); } } else { int num4 = CountEligiblePlayers(trigger); bool flag3 = text switch { "anyplayerinside" => flag, "allplayersinside" => num4 > 0 && num >= num4, "allplayersenter" => !wasInside && num4 > 0 && num >= num4, "allplayersexit" => wasInside && !flag, "anyplayerexit" => lastInsidePlayerCount > num, _ => !wasInside && flag, }; value.WasInside = flag; value.LastInsidePlayerCount = num; if (flag3) { FireTrigger(trigger, value); } } } internal static string NormalizePositionTriggerMode(string text) { return GetCachedNormalization(PositionModeNormalizationCache, text, (string value) => (value ?? string.Empty).Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant() switch { "inside" => "anyplayerinside", "anyplayerinside" => "anyplayerinside", "playerinside" => "anyplayerinside", "onplayerinside" => "anyplayerinside", "allinside" => "allplayersinside", "allplayerinside" => "allplayersinside", "allplayersinside" => "allplayersinside", "enter" => "anyplayerenter", "onenter" => "anyplayerenter", "playerenter" => "anyplayerenter", "playerentered" => "anyplayerenter", "anyplayerenter" => "anyplayerenter", "onplayerenter" => "anyplayerenter", "anyplayerentered" => "anyplayerenter", "allenter" => "allplayersenter", "allplayerenter" => "allplayersenter", "allplayersenter" => "allplayersenter", "allplayersentered" => "allplayersenter", "exit" => "anyplayerexit", "leave" => "anyplayerexit", "left" => "anyplayerexit", "onexit" => "anyplayerexit", "onleave" => "anyplayerexit", "playerexit" => "anyplayerexit", "playerleave" => "anyplayerexit", "playerleft" => "anyplayerexit", "playerexited" => "anyplayerexit", "anyplayerexit" => "anyplayerexit", "anyplayerleave" => "anyplayerexit", "anyplayerleft" => "anyplayerexit", "anyplayerexited" => "anyplayerexit", "onplayerexit" => "anyplayerexit", "onplayerleave" => "anyplayerexit", "onplayerleft" => "anyplayerexit", "onplayerexited" => "anyplayerexit", "onplayerexitarea" => "anyplayerexit", "onplayerleavearea" => "anyplayerexit", "allexit" => "allplayersexit", "allleave" => "allplayersexit", "allleft" => "allplayersexit", "allplayerexit" => "allplayersexit", "allplayerleave" => "allplayersexit", "allplayersleft" => "allplayersexit", "allplayersexit" => "allplayersexit", "allplayersleave" => "allplayersexit", "onallplayersexit" => "allplayersexit", "onallplayersleave" => "allplayersexit", _ => "anyplayerenter", }); } private static bool IsTriggerInside(PositionTriggerRule trigger) { return CountPlayersInsideTriggerArea(trigger) > 0; } private static bool AreAllPlayersInside(PositionTriggerRule trigger) { int num = CountEligiblePlayers(trigger); if (num > 0) { return CountPlayersInsideTriggerArea(trigger) >= num; } return false; } private static int CountEligiblePlayers(PositionTriggerRule trigger) { int num = 0; foreach (PlayerAgent item in EnumeratePlayers(trigger)) { _ = item; num++; } return num; } private static int CountPlayersInsideTriggerArea(PositionTriggerRule trigger) { int num = 0; foreach (PlayerAgent item in EnumeratePlayers(trigger)) { if (IsPlayerInsideTriggerArea(item, trigger)) { num++; } } return num; } private static bool IsPlayerInsideTriggerArea(PlayerAgent agent, PositionTriggerRule trigger) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) switch ((trigger.TriggerAreaMode ?? "Radius").Trim().ToLowerInvariant()) { case "overridebigzone": case "bigzone": case "zone": return IsPlayerInsideOverrideBigZone(agent, trigger); case "overridearea": case "area": return IsPlayerInsideOverrideArea(agent, trigger); default: { if (trigger.Position == null) { LogThrottled("Coordinate trigger '" + trigger.ID + "' uses Radius mode but Position is missing."); return false; } Vector3 val = trigger.Position.ToVector3(); float num = Math.Max(0.01f, trigger.Radius * trigger.Radius); Vector3 val2 = ((Agent)agent).Position - val; return ((Vector3)(ref val2)).sqrMagnitude <= num; } } } private static bool IsPlayerInsideOverrideBigZone(PlayerAgent agent, PositionTriggerRule trigger) { if (trigger.LocalIndex < 0) { LogThrottled("Coordinate trigger '" + trigger.ID + "' uses TriggerAreaMode=OverrideBigZone but LocalIndex is missing. Radius fallback is not used."); return false; } if (!TryGetCachedZones(trigger, out List zones, out string failure)) { if (!IsTransientLookupFailure(failure)) { LogThrottled("Coordinate trigger '" + trigger.ID + "' OverrideBigZone unresolved: " + failure); } return false; } foreach (LG_Zone item in zones) { if (PlayerMatchesAwoZone(agent, item)) { return true; } } return false; } private static bool IsPlayerInsideOverrideArea(PlayerAgent agent, PositionTriggerRule trigger) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (trigger.Count < 0) { LogThrottled("Coordinate trigger '" + trigger.ID + "' uses TriggerAreaMode=OverrideArea but Count is missing. Count should match the AWO-style area index, for example Count=0."); return false; } if (!TryGetCachedAreas(trigger, out List<(LG_Zone, object, int)> areas, out string failure)) { if (!IsTransientLookupFailure(failure)) { LogThrottled("Coordinate trigger '" + trigger.ID + "' OverrideArea unresolved: " + failure); } return false; } foreach (var item in areas) { if (IsPositionInsideAreaByReflection(item.Item2, ((Agent)agent).Position)) { return true; } } return false; } private static bool ZoneMatchesTrigger(LG_Zone zone, PositionTriggerRule trigger) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 try { if ((int)zone.LocalIndex != trigger.LocalIndex) { return false; } if (trigger.DimensionIndex >= 0 && (int)zone.DimensionIndex != trigger.DimensionIndex) { return false; } if (!string.IsNullOrWhiteSpace(trigger.Layer) && !LayerMatches(TryGetLayerIndex(zone), trigger.Layer)) { return false; } return true; } catch { return false; } } private static bool AreaMatchesTrigger(object area, PositionTriggerRule trigger) { if (trigger.Count < 0) { return false; } try { if (!TryGetCachedAreas(trigger, out List<(LG_Zone, object, int)> areas, out string _)) { return false; } foreach (var item in areas) { if (item.Item2 == area || object.Equals(item.Item2, area)) { return true; } } } catch { } return false; } private static bool AreaMatchesCachedArea(object area, List<(LG_Zone Zone, object Area, int Index)> areas) { try { foreach (var area2 in areas) { if (area2.Area == area || object.Equals(area2.Area, area)) { return true; } } } catch { } return false; } private static bool PlayerCourseNodeMatchesCachedArea(object node, List<(LG_Zone Zone, object Area, int Index)> areas) { try { object obj = TryGetObjectMember(node, "m_area", "Area", "area", "m_areaData", "LG_Area", "lgArea"); if (obj != null && AreaMatchesCachedArea(obj, areas)) { return true; } foreach (var area in areas) { if (AreaContainsCourseNode(area.Area, node)) { return true; } } } catch { } return false; } private static bool AreaContainsCourseNode(object area, object node) { if (ObjectCollectionContains(TryGetObjectMember(area, "m_courseNodes", "CourseNodes", "m_courseNodeList", "CourseNodeList", "m_nodes", "Nodes", "nodes", "m_navNodes", "NavNodes", "m_nodeList", "NodeList"), node)) { return true; } return false; } private static bool TryGetCachedZones(PositionTriggerRule trigger, out List zones, out string failure) { ZoneLookupCacheEntry orRefreshZoneLookupCache = GetOrRefreshZoneLookupCache(BuildZoneCacheKey("zone", trigger), trigger, includeAreas: false); zones = orRefreshZoneLookupCache.Zones; failure = orRefreshZoneLookupCache.LastFailure; if (orRefreshZoneLookupCache.Resolved) { return zones.Count > 0; } return false; } private static bool TryGetCachedAreas(PositionTriggerRule trigger, out List<(LG_Zone Zone, object Area, int Index)> areas, out string failure) { ZoneLookupCacheEntry orRefreshZoneLookupCache = GetOrRefreshZoneLookupCache(BuildZoneCacheKey("area", trigger), trigger, includeAreas: true); areas = orRefreshZoneLookupCache.Areas; failure = orRefreshZoneLookupCache.LastFailure; if (orRefreshZoneLookupCache.Resolved) { return areas.Count > 0; } return false; } private static ZoneLookupCacheEntry GetOrRefreshZoneLookupCache(string key, PositionTriggerRule trigger, bool includeAreas) { if (!ZoneLookupCache.TryGetValue(key, out ZoneLookupCacheEntry value)) { value = new ZoneLookupCacheEntry(); ZoneLookupCache[key] = value; } if (value.Resolved) { return value; } if (Time.realtimeSinceStartup - value.LastAttemptTime < 1.5f) { return value; } value.LastAttemptTime = Time.realtimeSinceStartup; value.Zones.Clear(); value.Areas.Clear(); value.LastFailure = string.Empty; if (!TryGetAwoZone(trigger, out LG_Zone zone, out string failure)) { value.LastFailure = failure; return value; } if ((Object)(object)zone == (Object)null) { value.LastFailure = "AWO-style zone lookup returned null zone."; return value; } value.Zones.Add(zone); if (includeAreas) { foreach (var item in EnumerateZoneAreas(zone)) { if (item.Index == trigger.Count) { value.Areas.Add((zone, item.Candidate, item.Index)); } } } if (!includeAreas && value.Zones.Count > 0) { value.Resolved = true; value.LastFailure = string.Empty; LogVerbose($"Resolved OverrideBigZone trigger '{trigger.ID}' to {value.Zones.Count} LG_Zone(s): {string.Join(", ", value.Zones.Select(SafeZoneDescriptor).Take(8))}"); return value; } if (includeAreas && value.Areas.Count > 0) { value.Resolved = true; value.LastFailure = string.Empty; LogVerbose($"Resolved OverrideArea trigger '{trigger.ID}' to {value.Areas.Count} area(s): {string.Join(", ", value.Areas.Select<(LG_Zone, object, int), string>(((LG_Zone Zone, object Area, int Index) a) => SafeZoneDescriptor(a.Zone) + $"/Area[{a.Index}]").Take(8))}"); return value; } string value2 = (includeAreas ? $"DimensionIndex={trigger.DimensionIndex}, Layer='{trigger.Layer}', LocalIndex={trigger.LocalIndex}, Count={trigger.Count}" : $"DimensionIndex={trigger.DimensionIndex}, Layer='{trigger.Layer}', LocalIndex={trigger.LocalIndex}"); value.LastFailure = $"no matching {(includeAreas ? "LG_Area" : "LG_Zone")} for {value2}. AvailableZones={DescribeAvailableZonesFromBuilder()}"; return value; } private static string BuildZoneCacheKey(string kind, PositionTriggerRule trigger) { return $"{kind}|D={trigger.DimensionIndex}|L={trigger.Layer.Trim().ToLowerInvariant()}|Z={trigger.LocalIndex}|A={trigger.Count}"; } private static bool TryGetAwoZone(PositionTriggerRule trigger, out LG_Zone? zone, out string failure) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) zone = null; failure = string.Empty; if ((Object)(object)Builder.CurrentFloor == (Object)null) { failure = "Builder.CurrentFloor is not ready; retrying after level geometry is ready."; return false; } if (trigger.LocalIndex < 0) { failure = "LocalIndex is missing. AWO-style zone lookup requires LocalIndex."; return false; } eDimensionIndex val = (eDimensionIndex)((trigger.DimensionIndex >= 0) ? trigger.DimensionIndex : 0); LG_LayerType val2 = ParseLayerTypeOrDefault(trigger.Layer); eLocalZoneIndex val3 = (eLocalZoneIndex)trigger.LocalIndex; if (!IsAwoFloorReadyForLookup(val, out string failure2)) { failure = failure2; return false; } try { if (Builder.CurrentFloor.TryGetZoneByLocalIndex(val, val2, val3, ref zone) && (Object)(object)zone != (Object)null) { return true; } } catch (Exception ex) { failure = $"AWO-style TryGetZoneByLocalIndex({val}, {val2}, {val3}) failed: {ex.GetType().Name}: {ex.Message}"; return false; } failure = $"AWO-style TryGetZoneByLocalIndex({val}, {val2}, {val3}) returned no zone. AvailableZones={DescribeAvailableZonesFromBuilder()}"; return false; } private static bool IsAwoFloorReadyForLookup(eDimensionIndex dimension, out string failure) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected I4, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) failure = string.Empty; object currentFloor = Builder.CurrentFloor; if (currentFloor == null) { failure = "Builder.CurrentFloor is not ready; retrying after level geometry is ready."; return false; } int num = TryGetCollectionCount(TryGetObjectMember(currentFloor, "m_dimensions", "Dimensions", "dimensions")); int num2 = (int)dimension; if (num <= 0) { failure = $"Builder.CurrentFloor dimensions are not ready yet; skip AWO lookup for {dimension} to avoid LG_Floor out-of-bounds spam."; return false; } if (num2 < 0 || num2 >= num) { failure = $"DimensionIndex {dimension} ({num2}) is outside Builder.CurrentFloor.m_dimensions Count={num}."; return false; } if (TryGetCollectionCount(TryGetObjectMember(currentFloor, "allZones", "m_zones", "Zones", "zones")) <= 0) { failure = "Builder.CurrentFloor zones are not ready yet; retrying before AWO zone lookup."; return false; } return true; } private static LG_LayerType ParseLayerTypeOrDefault(string layerText) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(layerText)) { return (LG_LayerType)0; } string text = layerText.Trim(); if (int.TryParse(text, out var result)) { return (LG_LayerType)(byte)result; } if (Enum.TryParse(text, ignoreCase: true, out LG_LayerType result2)) { return result2; } switch (text.Replace("_", string.Empty).Replace("-", string.Empty).Replace(" ", string.Empty) .ToLowerInvariant()) { case "main": case "mainlayer": return (LG_LayerType)0; case "secondary": case "secondarylayer": return (LG_LayerType)1; case "third": case "thirdlayer": return (LG_LayerType)2; default: return (LG_LayerType)0; } } private static bool PlayerMatchesAwoZone(PlayerAgent agent, LG_Zone zone) { try { AIG_CourseNode courseNode = ((Agent)agent).CourseNode; if ((Object)(object)((courseNode != null) ? courseNode.m_zone : null) == (Object)null) { return false; } return ((Agent)agent).CourseNode.m_zone.ID == zone.ID; } catch { return false; } } private static string DescribeAvailableZonesFromBuilder() { try { LG_Floor currentFloor = Builder.CurrentFloor; if (((currentFloor != null) ? currentFloor.allZones : null) != null) { return DescribeAvailableZonesFromCollection(Builder.CurrentFloor.allZones); } } catch { } try { return DescribeAvailableZones((IEnumerable)Object.FindObjectsOfType()); } catch { return ""; } } private static string DescribeAvailableZonesFromCollection(object zones) { try { List list = new List(); foreach (object item in CollectionToObjectList(zones)) { LG_Zone val = (LG_Zone)((item is LG_Zone) ? item : null); if (val != null) { list.Add(val); } } if (list.Count > 0) { return DescribeAvailableZones(list); } } catch { } return ""; } private static string DescribeAvailableZones(IEnumerable zones) { try { string[] array = zones.Where((LG_Zone z) => (Object)(object)z != (Object)null).Select(SafeZoneDescriptor).Distinct(StringComparer.OrdinalIgnoreCase) .Take(16) .ToArray(); return (array.Length == 0) ? "" : string.Join(", ", array); } catch { return ""; } } private static string SafeZoneDescriptor(LG_Zone zone) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected I4, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected I4, but got Unknown try { return $"D{(int)zone.DimensionIndex}/L{TryGetLayerIndex(zone)}/Z{(int)zone.LocalIndex}"; } catch { return ""; } } private static List<(object Candidate, int Index)> EnumerateZoneAreas(LG_Zone zone) { List<(object, int)> list = new List<(object, int)>(); object obj = TryGetObjectMember(zone, "m_areas", "Areas", "m_areaList", "AreaList"); if (obj == null) { return list; } try { int num = 0; foreach (object item in CollectionToObjectList(obj)) { if (item != null) { list.Add((item, num)); } num++; } } catch { } return list; } private static bool ZoneContainsCourseNode(LG_Zone zone, object node) { return ObjectCollectionContains(TryGetObjectMember(zone, "m_courseNodes", "CourseNodes", "m_courseNodeList", "CourseNodeList", "Nodes", "m_nodes"), node); } private static bool ObjectCollectionContains(object? collection, object target) { if (collection == null) { return false; } try { if (collection == target) { return true; } foreach (object item in CollectionToObjectList(collection)) { if (item != null) { if (item == target) { return true; } if (object.Equals(item, target)) { return true; } } } } catch { } return false; } private static bool TryResolveZoneByPosition(Vector3 position, PositionTriggerRule trigger, bool requireArea) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { foreach (LG_Zone item in Object.FindObjectsOfType()) { if (ZoneMatchesTrigger(item, trigger)) { if (!requireArea) { return IsPositionInsideZoneByReflection(item, position); } if (TryAnyAreaInZoneMatchesPosition(item, position, trigger)) { return true; } } } } catch { } return false; } private static bool IsPositionInsideZoneByReflection(LG_Zone zone, Vector3 position) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (TryGetZoneBounds(zone, out var bounds)) { return ((Bounds)(ref bounds)).Contains(position); } return false; } private static bool TryAnyAreaInZoneMatchesPosition(LG_Zone zone, Vector3 position, PositionTriggerRule trigger) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { foreach (var item in EnumerateZoneAreas(zone)) { if (item.Index == trigger.Count && IsPositionInsideAreaByReflection(item.Candidate, position)) { return true; } } } catch { } return false; } private static bool IsPositionInsideAreaByReflection(object area, Vector3 position) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (TryGetAreaBounds(area, out var bounds)) { return BoundsContainsXZ(bounds, position, 0.1f); } return false; } private static bool BoundsContainsXZ(Bounds bounds, Vector3 position, float margin = 0f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!IsUsableBounds(bounds)) { return false; } if (position.x >= ((Bounds)(ref bounds)).min.x - margin && position.x <= ((Bounds)(ref bounds)).max.x + margin && position.z >= ((Bounds)(ref bounds)).min.z - margin) { return position.z <= ((Bounds)(ref bounds)).max.z + margin; } return false; } private static bool IsValidDebugVector(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z)) { return false; } if (Mathf.Abs(v.x) >= 9000f || Mathf.Abs(v.y) >= 9000f || Mathf.Abs(v.z) >= 9000f) { return false; } return true; } private static bool IsUsableBounds(Bounds b) { //IL_0002: 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) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!IsValidDebugVector(((Bounds)(ref b)).center)) { return false; } if (float.IsNaN(((Bounds)(ref b)).size.x) || float.IsNaN(((Bounds)(ref b)).size.y) || float.IsNaN(((Bounds)(ref b)).size.z)) { return false; } Vector3 size = ((Bounds)(ref b)).size; if (((Vector3)(ref size)).sqrMagnitude < 0.01f) { return false; } return true; } private static bool TryGetVector3Member(object obj, out Vector3 value, params string[] names) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) value = default(Vector3); foreach (string name in names) { try { if (obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is Vector3 val && IsValidDebugVector(val)) { value = val; return true; } object obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 is Vector3) { Vector3 val2 = (Vector3)obj2; if (IsValidDebugVector(val2)) { value = val2; return true; } } } catch { } } return false; } private static bool TryGetNodeOrAreaBounds(object obj, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); List list = new List(); try { if (TryGetVector3Member(obj, out var value, "m_position", "Position", "position", "m_pos", "pos")) { list.Add(value); } object? obj2 = TryGetObjectMember(obj, "transform"); Transform val = (Transform)((obj2 is Transform) ? obj2 : null); if ((Object)(object)val != (Object)null && IsValidDebugVector(val.position)) { list.Add(val.position); } foreach (object item in CollectionToObjectList(TryGetObjectMember(obj, "m_courseNodes", "CourseNodes", "m_courseNodeList", "CourseNodeList", "Nodes", "m_nodes", "m_navNodes", "NavNodes"))) { if (item != null) { if (TryGetVector3Member(item, out var value2, "m_position", "Position", "position", "m_pos", "pos")) { list.Add(value2); } object? obj3 = TryGetObjectMember(item, "transform"); Transform val2 = (Transform)((obj3 is Transform) ? obj3 : null); if ((Object)(object)val2 != (Object)null && IsValidDebugVector(val2.position)) { list.Add(val2.position); } } } } catch { } if (list.Count == 0) { return false; } bounds = new Bounds(list[0], Vector3.one * 2f); for (int i = 1; i < list.Count; i++) { ((Bounds)(ref bounds)).Encapsulate(list[i]); } Vector3 size = ((Bounds)(ref bounds)).size; if (((Vector3)(ref size)).sqrMagnitude < 0.01f) { bounds = new Bounds(((Bounds)(ref bounds)).center, Vector3.one * 2f); } return IsUsableBounds(bounds); } private static bool TryGetZoneBounds(LG_Zone zone, out Bounds bounds, bool allowTransformFallback = true) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) bool flag = false; Bounds val = default(Bounds); try { foreach (var item in EnumerateZoneAreas(zone)) { if (TryGetAreaBounds(item.Candidate, out var bounds2, allowTransformFallback)) { if (!flag) { val = bounds2; flag = true; } else { ((Bounds)(ref val)).Encapsulate(bounds2); } } } } catch { } if (flag && IsUsableBounds(val)) { bounds = val; return true; } if (TryGetObjectMember(zone, "m_bounds", "Bounds", "bounds") is Bounds val2 && IsUsableBounds(val2)) { bounds = val2; return true; } object obj2 = TryGetObjectMember(zone, "m_center", "Center", "center"); object obj3 = TryGetObjectMember(zone, "m_size", "Size", "size"); if (obj2 is Vector3 val3 && obj3 is Vector3 val4) { Bounds val5 = default(Bounds); ((Bounds)(ref val5))..ctor(val3, val4); if (IsUsableBounds(val5)) { bounds = val5; return true; } } if (TryGetNodeOrAreaBounds(zone, out bounds)) { return true; } Transform val6 = (Transform)(allowTransformFallback ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val6 != (Object)null && IsValidDebugVector(val6.position)) { Bounds val7 = default(Bounds); ((Bounds)(ref val7))..ctor(val6.position, new Vector3(4f, 4f, 4f)); if (IsUsableBounds(val7)) { bounds = val7; return true; } } bounds = default(Bounds); return false; } private static float BoundsXZArea(Bounds b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!IsUsableBounds(b)) { return -1f; } return Math.Max(0f, ((Bounds)(ref b)).size.x) * Math.Max(0f, ((Bounds)(ref b)).size.z); } private static void AddBoundsCandidate(List candidates, Bounds b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (IsUsableBounds(b)) { candidates.Add(b); } } private static bool TryGetHierarchyBounds(object obj, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); GameObject val = null; try { GameObject val2 = (GameObject)((obj is GameObject) ? obj : null); if (val2 != null) { val = val2; } else { Component val3 = (Component)((obj is Component) ? obj : null); if (val3 != null) { val = val3.gameObject; } else { object? obj2 = TryGetObjectMember(obj, "gameObject", "GameObject", "m_gameObject", "GO"); GameObject val4 = (GameObject)((obj2 is GameObject) ? obj2 : null); if (val4 != null) { val = val4; } else { object? obj3 = TryGetObjectMember(obj, "transform", "Transform", "m_transform"); Transform val5 = (Transform)((obj3 is Transform) ? obj3 : null); if (val5 != null) { val = ((Component)val5).gameObject; } } } } } catch { } if ((Object)(object)val == (Object)null) { return false; } bool flag = false; Bounds val6 = default(Bounds); try { Renderer[] array = Il2CppArrayBase.op_Implicit(val.GetComponentsInChildren(true)); foreach (Renderer val7 in array) { if ((Object)(object)val7 == (Object)null) { continue; } Bounds bounds2 = val7.bounds; if (IsUsableBounds(bounds2)) { if (!flag) { val6 = bounds2; flag = true; } else { ((Bounds)(ref val6)).Encapsulate(bounds2); } } } } catch { } try { Collider[] array2 = Il2CppArrayBase.op_Implicit(val.GetComponentsInChildren(true)); foreach (Collider val8 in array2) { if ((Object)(object)val8 == (Object)null) { continue; } Bounds bounds3 = val8.bounds; if (IsUsableBounds(bounds3)) { if (!flag) { val6 = bounds3; flag = true; } else { ((Bounds)(ref val6)).Encapsulate(bounds3); } } } } catch { } if (flag && IsUsableBounds(val6)) { bounds = val6; return true; } return false; } private static bool TryGetAreaBounds(object area, out Bounds bounds, bool allowTransformFallback = true) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (TryGetHierarchyBounds(area, out var bounds2)) { AddBoundsCandidate(list, bounds2); } if (TryGetObjectMember(area, "m_bounds", "Bounds", "bounds") is Bounds b) { AddBoundsCandidate(list, b); } object obj = TryGetObjectMember(area, "m_center", "Center", "center"); object obj2 = TryGetObjectMember(area, "m_size", "Size", "size"); if (obj is Vector3 val && obj2 is Vector3 val2) { AddBoundsCandidate(list, new Bounds(val, val2)); } if (TryGetNodeOrAreaBounds(area, out var bounds3)) { AddBoundsCandidate(list, bounds3); } Transform val3 = (Transform)(allowTransformFallback ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val3 != (Object)null && IsValidDebugVector(val3.position)) { AddBoundsCandidate(list, new Bounds(val3.position, new Vector3(6f, 4f, 6f))); } if (list.Count > 0) { bounds = list.OrderByDescending(BoundsXZArea).First(); return true; } bounds = default(Bounds); return false; } internal static bool TryGetPositionTriggerDebugBounds(PositionTriggerRule trigger, out Bounds bounds, out string source) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) switch ((trigger.TriggerAreaMode ?? "Radius").Trim().ToLowerInvariant()) { case "overridebigzone": case "bigzone": case "zone": { if (TryGetCachedZones(trigger, out List zones, out string failure2)) { foreach (LG_Zone item in zones) { if (TryGetZoneBounds(item, out bounds, allowTransformFallback: false)) { source = $"OverrideBigZone LocalIndex={trigger.LocalIndex}"; return true; } } } bounds = default(Bounds); source = $"OverrideBigZone LocalIndex={trigger.LocalIndex} unresolved ({failure2})"; return false; } case "overridearea": case "area": { if (TryGetCachedAreas(trigger, out List<(LG_Zone, object, int)> areas, out string failure)) { foreach (var item2 in areas) { if (TryGetAreaBounds(item2.Item2, out bounds, allowTransformFallback: false)) { source = $"OverrideArea LocalIndex={trigger.LocalIndex} Count={trigger.Count}"; return true; } } } bounds = default(Bounds); source = $"OverrideArea LocalIndex={trigger.LocalIndex} Count={trigger.Count} unresolved ({failure})"; return false; } default: { if (trigger.Position == null) { bounds = default(Bounds); source = "Radius Position missing"; return false; } Vector3 val = trigger.Position.ToVector3(); float num = Math.Max(0.5f, trigger.Radius); bounds = new Bounds(val, new Vector3(num * 2f, Math.Max(1f, num), num * 2f)); source = "Radius Position"; return true; } } } private static bool LayerMatches(int zoneLayer, string layerText) { string text = layerText.Trim().ToLowerInvariant(); if (int.TryParse(text, out var result)) { return zoneLayer == result; } switch (text) { case "main": case "mainlayer": return zoneLayer == 0; case "secondary": case "secondarylayer": return zoneLayer == 1; case "third": case "thirdlayer": return zoneLayer == 2; default: return true; } } private static bool HasAnyAlivePlayer() { List playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel; if (playerAgentsInLevel == null) { return false; } for (int i = 0; i < playerAgentsInLevel.Count; i++) { try { PlayerAgent val = playerAgentsInLevel[i]; if ((Object)(object)val != (Object)null && ((Agent)val).Alive) { return true; } } catch { } } return false; } private static IEnumerable EnumeratePlayers(PositionTriggerRule trigger) { List list = PlayerManager.PlayerAgentsInLevel; if (list == null) { yield break; } for (int i = 0; i < list.Count; i++) { PlayerAgent val = null; try { val = list[i]; } catch { } if (!((Object)(object)val == (Object)null) && (!trigger.RequireAlivePlayers || ((Agent)val).Alive)) { yield return val; } } } private static void FireTrigger(PositionTriggerRule trigger, TriggerState state) { state.Fired = true; state.LastFireTime = Time.realtimeSinceStartup; int num = ExecuteEventList(trigger.Events.Concat(trigger.WardenEvents), "Coordinate trigger '" + trigger.ID + "'"); LogVerbose($"Coordinate trigger '{trigger.ID}' fired. Mode={trigger.TriggerAreaMode}, ExecutedEvents={num}"); if (num == 0) { LogVerbose("Coordinate trigger '" + trigger.ID + "' matched its area but executed 0 events. Add Events/WardenEvents, or enable UsePlayerCountEvents with non-empty player-count event arrays."); } HandlePositionTriggerCycle(trigger, state, "Coordinate trigger '" + trigger.ID + "'"); } private static void FireTriggerForPlayerCount(PositionTriggerRule trigger, TriggerState state, int insidePlayerCount) { state.Fired = true; state.FiredPlayerCounts.Add(insidePlayerCount); state.LastFireTime = Time.realtimeSinceStartup; int num = ExecuteEventList(GetEventsForPlayerCount(trigger, insidePlayerCount), $"Coordinate trigger '{trigger.ID}' playerCount={insidePlayerCount}"); LogVerbose($"Coordinate trigger '{trigger.ID}' fired by player count. Mode={trigger.TriggerAreaMode}, PlayersInside={insidePlayerCount}, ExecutedEvents={num}"); if (num == 0) { LogVerbose($"Coordinate trigger '{trigger.ID}' matched with PlayersInside={insidePlayerCount} but that player-count event list is empty."); } HandlePositionTriggerCycle(trigger, state, $"Coordinate trigger '{trigger.ID}' playerCount={insidePlayerCount}"); } private static void HandlePositionTriggerCycle(PositionTriggerRule trigger, TriggerState state, string ownerLabel) { if (trigger.UseTriggerCycleEvents && trigger.TriggerCycleEvents.Count != 0) { int num = Math.Max(1, trigger.TriggerCycleCount); state.CompletedCycles++; if (state.CompletedCycles % num != 0) { LogVerbose($"Position trigger cycle progress. ID={trigger.ID}, Cycles={state.CompletedCycles}/{num}"); } else { int value = ExecuteEventList(trigger.TriggerCycleEvents, $"{ownerLabel} cycle={state.CompletedCycles}"); LogVerbose($"Position trigger cycle events fired. ID={trigger.ID}, Cycles={state.CompletedCycles}, Required={num}, ExecutedEvents={value}"); } } } private static int ExecuteEventList(IEnumerable events, string ownerLabel) { int num = 0; foreach (JsonElement @event in events) { if (TryExecuteConfiguredEvent(@event, ownerLabel)) { num++; } } return num; } private static IEnumerable GetEventsForPlayerCount(PositionTriggerRule trigger, int count) { return count switch { 1 => trigger.OnePlayerEvents, 2 => trigger.TwoPlayerEvents, 3 => trigger.ThreePlayerEvents, 4 => trigger.FourPlayerEvents, _ => Enumerable.Empty(), }; } private static bool HasPlayerCountEvents(PositionTriggerRule trigger) { if (trigger.OnePlayerEvents.Count <= 0 && trigger.TwoPlayerEvents.Count <= 0 && trigger.ThreePlayerEvents.Count <= 0) { return trigger.FourPlayerEvents.Count > 0; } return true; } private static IEnumerable GetEventsForPlayerCount(ScanTriggerRule trigger, int count) { return count switch { 1 => trigger.OnePlayerEvents, 2 => trigger.TwoPlayerEvents, 3 => trigger.ThreePlayerEvents, 4 => trigger.FourPlayerEvents, _ => Enumerable.Empty(), }; } private static bool HasPlayerCountEvents(ScanTriggerRule trigger) { if (trigger.OnePlayerEvents.Count <= 0 && trigger.TwoPlayerEvents.Count <= 0 && trigger.ThreePlayerEvents.Count <= 0) { return trigger.FourPlayerEvents.Count > 0; } return true; } private static bool TryExecuteConfiguredEvent(JsonElement eventElement, string ownerLabel) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) bool flag = default(bool); try { if (TryHandleTriggerControlEvent(eventElement, ownerLabel)) { return true; } if (TryBuildWardenEvent(eventElement, out WardenObjectiveEventData data) && data != null) { try { ExecuteWardenEvent(data); return true; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(31, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ownerLabel); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" failed to execute event '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(data.Type); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogError(val); } } } } catch (Exception ex2) { ManualLogSource log = Log; if (log != null) { BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(39, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ownerLabel); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" failed to execute configured event: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex2.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex2.Message); } log.LogError(val); } } return false; } private static bool TryHandleTriggerControlEvent(JsonElement element, string ownerLabel) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown if (element.ValueKind != JsonValueKind.Object) { return false; } string text = GetString(element, "Type", GetString(element, "EventType", string.Empty)).Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant(); if (text != "settriggerenabled" && text != "seteventtriggerenabled" && text != "settriggerstate" && text != "enabletrigger" && text != "disabletrigger") { return false; } bool flag = text == "enabletrigger" || (!(text == "disabletrigger") && GetBool(element, "Enabled", GetBool(element, "Enable", defaultValue: true))); string @string = GetString(element, "TargetID", GetString(element, "TriggerID", GetString(element, "ID", string.Empty))); string string2 = GetString(element, "Category", GetString(element, "TargetType", GetString(element, "TriggerCategory", "Any"))); if (string.IsNullOrWhiteSpace(@string)) { ManualLogSource log = Log; if (log != null) { bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(49, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ownerLabel); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" trigger-control event has no TargetID/TriggerID."); } log.LogWarning(val); } return true; } int value = SetTriggerEnabled(@string, string2, flag); LogVerbose($"{ownerLabel} trigger-control event set Enabled={flag} for TargetID='{@string}', Category='{string2}', Changed={value}."); return true; } private static int SetTriggerEnabled(string targetID, string category, bool enabled) { string targetID2 = targetID; int num = 0; string text = NormalizeTargetType(category); bool flag = string.IsNullOrWhiteSpace(category) || text == "any" || string.Equals(category, "all", StringComparison.OrdinalIgnoreCase); foreach (ConfigDocument config in ConfigManager.Configs) { if ((flag || text == "position" || text == "coordinate") && config.PositionTriggers != null) { foreach (PositionTriggerRule positionTrigger in config.PositionTriggers) { if (IDMatches(positionTrigger.ID) && positionTrigger.Enabled != enabled) { positionTrigger.Enabled = enabled; num++; } } } if ((flag || text == "scan" || text == "bioscan") && config.ScanTriggers != null) { foreach (ScanTriggerRule scanTrigger in config.ScanTriggers) { if (IDMatches(scanTrigger.ID) && scanTrigger.Enabled != enabled) { scanTrigger.Enabled = enabled; num++; } } } if (!flag) { switch (text) { case "interact": case "interaction": case "terminal": case "bigpickup": break; default: continue; } } if (config.InteractTriggers == null) { continue; } foreach (InteractTriggerRule interactTrigger in config.InteractTriggers) { if (IDMatches(interactTrigger.ID) && interactTrigger.Enabled != enabled) { interactTrigger.Enabled = enabled; num++; } } } if (num > 0) { MarkActiveTriggerCacheDirty(); } return num; bool IDMatches(string id) { if (!(targetID2 == "*")) { return string.Equals(id, targetID2, StringComparison.OrdinalIgnoreCase); } return true; } } private static bool TryHandleWardenIntelDirect(JsonElement element, string ownerLabel) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) try { if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("WardenIntel", out var value)) { return false; } string text = GetString(element, "Type", string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "WardenIntel", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "2", StringComparison.OrdinalIgnoreCase)) { return false; } LG_LayerType val = (LG_LayerType)0; if (TryParseEnum(GetString(element, "Layer", "MainLayer"), out LG_LayerType value2)) { val = value2; } if (TryBuildLocalizedTextIdOnly(value, out LocalizedText text2) && text2 != (LocalizedText)null) { try { WardenObjectiveManager.DisplayWardenIntel(val, text2); LogVerbose($"{ownerLabel} displayed vanilla WardenIntel. Layer={val}, Value={value}"); return true; } catch (Exception ex) { LogThrottled($"Direct DisplayWardenIntel failed for {ownerLabel}: {ex.GetType().Name}: {ex.Message}"); return false; } } if (TryReadLocalizedTextRawString(value, out string text3) && !string.IsNullOrWhiteSpace(text3)) { try { GuiManager.PlayerLayer.ShowWardenIntel(text3, 0f, GetFloat(element, "WardenIntelDuration", GetFloat(element, "IntelDuration", 5f))); LogVerbose(ownerLabel + " used raw-string WardenIntel fallback. For vanilla behavior use a TextDataBlock ID instead. Text='" + text3 + "'"); return true; } catch (Exception ex2) { LogThrottled($"Raw-string WardenIntel fallback failed for {ownerLabel}: {ex2.GetType().Name}: {ex2.Message}"); return false; } } } catch (Exception ex3) { LogThrottled($"TryHandleWardenIntelDirect failed for {ownerLabel}: {ex3.GetType().Name}: {ex3.Message}"); } return false; } private static void ExecuteWardenEvent(WardenObjectiveEventData eventData) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) try { WardenObjectiveManager.CheckAndExecuteEventsOnTrigger(eventData, eventData.Trigger, true, eventData.Delay); } catch (Exception ex) { try { WorldEventManager.ExecuteEvent(eventData, eventData.Delay); LogThrottled($"Fallback WorldEventManager.ExecuteEvent used for '{eventData.Type}' after WardenObjectiveManager failed: {ex.Message}"); } catch (Exception ex2) { LogThrottled($"WardenEvent execution failed for '{eventData.Type}': Manager={ex.GetType().Name}: {ex.Message}; WorldEvent={ex2.GetType().Name}: {ex2.Message}"); } } } private static void TryDisplayWardenIntel(WardenObjectiveEventData eventData, JsonElement rawEvent) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) try { if (!TryResolveWardenIntelString(rawEvent, out string text) || string.IsNullOrWhiteSpace(text)) { return; } bool flag = false; try { GuiManager.PlayerLayer.ShowWardenIntel(text, 0f, GetFloat(rawEvent, "WardenIntelDuration", GetFloat(rawEvent, "IntelDuration", 5f))); flag = true; } catch (Exception ex) { LogThrottled($"PlayerGui ShowWardenIntel normal-text path failed for event '{eventData.Type}': {ex.GetType().Name}: {ex.Message}"); } if (!flag) { try { GuiManager.PlayerLayer.m_gameEventLog.AddLogItem(text, (eGameEventChatLogType)3); flag = true; } catch (Exception ex2) { LogThrottled($"GameEventLog text fallback failed for event '{eventData.Type}': {ex2.GetType().Name}: {ex2.Message}"); } } if (!flag) { LogThrottled($"WardenIntel normal text was present for event '{eventData.Type}' but no display path succeeded. Text='{text}'"); } } catch (Exception ex3) { LogThrottled($"Could not display WardenIntel normal text for event '{eventData.Type}': {ex3.GetType().Name}: {ex3.Message}"); } } private static bool TryResolveWardenIntelString(JsonElement rawEvent, out string text) { text = string.Empty; if (rawEvent.ValueKind != JsonValueKind.Object || !rawEvent.TryGetProperty("WardenIntel", out var value)) { return false; } return TryResolveTextDataString(value, out text); } private static bool TryResolveTextDataString(JsonElement rawValue, out string text) { text = string.Empty; try { if (rawValue.ValueKind == JsonValueKind.Number && rawValue.TryGetUInt32(out var value)) { return TryGetTextDataBlockString(value, out text); } if (rawValue.ValueKind == JsonValueKind.String) { string @string = rawValue.GetString(); if (string.IsNullOrWhiteSpace(@string)) { return false; } if (uint.TryParse(@string.Trim(), out var result)) { return TryGetTextDataBlockString(result, out text); } text = @string; return true; } if (rawValue.ValueKind == JsonValueKind.Object) { string[] array = new string[9] { "TextID", "TextId", "textID", "id", "ID", "persistentID", "PersistentID", "Value", "value" }; foreach (string propertyName in array) { if (rawValue.TryGetProperty(propertyName, out var value2) && TryResolveTextDataString(value2, out text)) { return true; } } array = new string[4] { "Text", "text", "UntranslatedText", "English" }; foreach (string propertyName2 in array) { if (rawValue.TryGetProperty(propertyName2, out var value3) && value3.ValueKind == JsonValueKind.String) { text = value3.GetString() ?? string.Empty; return !string.IsNullOrWhiteSpace(text); } } } } catch (Exception ex) { LogThrottled($"Could not resolve TextDataBlock text from json value '{rawValue}': {ex.GetType().Name}: {ex.Message}"); } return false; } private static bool TryGetTextDataBlockString(uint id, out string text) { text = string.Empty; try { text = Text.Get(id); if (!string.IsNullOrWhiteSpace(text)) { return true; } } catch (Exception ex) { LogThrottled($"Localization.Text.Get({id}) failed: {ex.GetType().Name}: {ex.Message}"); } try { TextDataBlock block = GameDataBlockBase.GetBlock(id); if (block != null) { text = block.GetText((Language)1, false); if (!string.IsNullOrWhiteSpace(text)) { return true; } text = block.English; if (!string.IsNullOrWhiteSpace(text)) { return true; } } } catch (Exception ex2) { LogThrottled($"TextDataBlock.GetBlock({id}) normal text fallback failed: {ex2.GetType().Name}: {ex2.Message}"); } return false; } private static bool TryBuildWardenEvent(JsonElement element, out WardenObjectiveEventData? data) { data = null; if (element.ValueKind != JsonValueKind.Object) { return false; } if ((element.TryGetProperty("WardenIntel", out var _) || IsLikelyAwoEvent(element)) && TryBuildWardenEventViaAwoJson(element, out data)) { return true; } return TryBuildNativeWardenEvent(element, out data); } private static bool TryBuildNativeWardenEvent(JsonElement element, out WardenObjectiveEventData? data) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Expected O, but got Unknown //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Expected O, but got Unknown //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Expected O, but got Unknown //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Expected O, but got Unknown data = null; if (element.ValueKind != JsonValueKind.Object) { return false; } string text = GetString(element, "Type", string.Empty).Trim(); eWardenObjectiveEventType value = (eWardenObjectiveEventType)0; JsonElement value2; bool flag = element.TryGetProperty("WardenIntel", out value2); bool flag2 = string.Equals(text, "WardenIntel", StringComparison.OrdinalIgnoreCase); if (!string.IsNullOrWhiteSpace(text) && !flag2 && !TryParseEnum(text, out value)) { ManualLogSource log = Log; if (log != null) { bool flag3 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Skipping event with invalid Type='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'."); } log.LogWarning(val); } return false; } WardenObjectiveEventData val2 = new WardenObjectiveEventData(); val2.Type = (eWardenObjectiveEventType)((!((string.IsNullOrWhiteSpace(text) || flag2) && flag)) ? ((int)value) : 0); val2.Delay = GetFloat(element, "Delay", 0f); val2.Duration = GetFloat(element, "Duration", 0f); val2.Enabled = GetBool(element, "Enabled", defaultValue: true); val2.Count = GetInt(element, "Count", 0); val2.ChainPuzzle = GetUInt(element, "ChainPuzzle", GetUInt(element, "ChainPuzzleID", 0u)); val2.UseStaticBioscanPoints = GetBool(element, "UseStaticBioscanPoints", defaultValue: false); val2.ClearDimension = GetBool(element, "ClearDimension", defaultValue: false); if (TryGetLocalizedTextIdOnly(element, "WardenIntel", out LocalizedText text2)) { val2.WardenIntel = text2; } if (TryGetLocalizedText(element, "CustomSubObjectiveHeader", out LocalizedText text3)) { val2.CustomSubObjectiveHeader = text3; } if (TryGetLocalizedText(element, "CustomSubObjective", out LocalizedText text4)) { val2.CustomSubObjective = text4; } if (TryGetLocalizedText(element, "SoundSubtitle", out LocalizedText text5)) { val2.SoundSubtitle = text5; } val2.SoundID = GetUInt(element, "SoundID", 0u); val2.DialogueID = GetUInt(element, "DialogueID", 0u); val2.FogSetting = GetUInt(element, "FogSetting", 0u); val2.FogTransitionDuration = GetFloat(element, "FogTransitionDuration", 0f); val2.EnemyID = GetUInt(element, "EnemyID", 0u); val2.WorldEventObjectFilter = GetString(element, "WorldEventObjectFilter", string.Empty); if (TryParseEnum(GetString(element, "Trigger", string.Empty), out eWardenObjectiveEventTrigger value3)) { val2.Trigger = value3; } if (TryParseEnum(GetString(element, "Layer", string.Empty), out LG_LayerType value4)) { val2.Layer = value4; } if (TryParseEnum(GetString(element, "DimensionIndex", string.Empty), out eDimensionIndex value5)) { val2.DimensionIndex = value5; } if (TryParseEnum(GetString(element, "LocalIndex", string.Empty), out eLocalZoneIndex value6)) { val2.LocalIndex = value6; } if (element.TryGetProperty("Position", out var value7) && value7.ValueKind == JsonValueKind.Object) { val2.Position = new Vector3(GetFloat(value7, "x", GetFloat(value7, "X", 0f)), GetFloat(value7, "y", GetFloat(value7, "Y", 0f)), GetFloat(value7, "z", GetFloat(value7, "Z", 0f))); } if (element.TryGetProperty("Condition", out var value8) && value8.ValueKind == JsonValueKind.Object) { WorldEventConditionPair val3 = new WorldEventConditionPair(); val3.ConditionIndex = GetInt(value8, "ConditionIndex", GetInt(value8, "Index", 0)); val3.IsTrue = GetBool(value8, "IsTrue", GetBool(value8, "Value", defaultValue: true)); val2.Condition = val3; } else if (element.TryGetProperty("ConditionIndex", out value2)) { WorldEventConditionPair val4 = new WorldEventConditionPair(); val4.ConditionIndex = GetInt(element, "ConditionIndex", 0); val4.IsTrue = GetBool(element, "ConditionValue", GetBool(element, "Value", defaultValue: true)); val2.Condition = val4; } if (element.TryGetProperty("EnemyWaveData", out var value9) && value9.ValueKind == JsonValueKind.Object) { GenericEnemyWaveData val5 = new GenericEnemyWaveData(); val5.WaveSettings = GetUInt(value9, "WaveSettings", 0u); val5.WavePopulation = GetUInt(value9, "WavePopulation", 0u); val5.AreaDistance = GetInt(value9, "AreaDistance", 0); val5.WorldEventObjectFilterSpawnPoint = GetString(value9, "WorldEventObjectFilterSpawnPoint", string.Empty); val5.SpawnDelay = GetFloat(value9, "SpawnDelay", 0f); val5.TriggerAlarm = GetBool(value9, "TriggerAlarm", defaultValue: false); val2.EnemyWaveData = val5; } else if (element.TryGetProperty("WaveSettings", out value2) || element.TryGetProperty("WavePopulation", out value2)) { GenericEnemyWaveData val6 = new GenericEnemyWaveData(); val6.WaveSettings = GetUInt(element, "WaveSettings", 0u); val6.WavePopulation = GetUInt(element, "WavePopulation", 0u); val6.AreaDistance = GetInt(element, "AreaDistance", 0); val6.WorldEventObjectFilterSpawnPoint = GetString(element, "WorldEventObjectFilterSpawnPoint", string.Empty); val6.SpawnDelay = GetFloat(element, "SpawnDelay", 0f); val6.TriggerAlarm = GetBool(element, "TriggerAlarm", defaultValue: false); val2.EnemyWaveData = val6; } data = val2; return true; } private static bool IsLikelyAwoEvent(JsonElement element) { string @string = GetString(element, "Type", string.Empty); if (string.IsNullOrWhiteSpace(@string)) { return false; } if (int.TryParse(@string, out var result) && result >= 10000) { return true; } if (!TryParseEnum(@string, out eWardenObjectiveEventType _)) { return true; } string[] array = new string[43] { "SpecialBool", "SpecialNumber", "SpecialText", "SubObjective", "Fog", "Reactor", "Countdown", "Countup", "CleanupEnemies", "SpawnHibernates", "SpawnScouts", "AddTerminalCommand", "AddCommand", "HideTerminalCommand", "HideCommand", "UnhideTerminalCommand", "UnhideCommand", "GiveResource", "ActiveEnemyWave", "NestedEvent", "StartEventLoop", "EventLoop", "TeleportPlayer", "InfectPlayer", "DamagePlayer", "RevivePlayer", "AdjustTimer", "NavMarker", "CameraShake", "Portal", "SuccessScreen", "MultiProgression", "WaveRoarSound", "CustomHudText", "CustomHud", "SpecialHudTimer", "SpecialHud", "PlayerDialogue", "SetTerminalLog", "TerminalLog", "ObjectiveItems", "DimensionData", "EnvironmentData" }; foreach (string propertyName in array) { if (element.TryGetProperty(propertyName, out var _)) { return true; } } return false; } private static bool TryBuildWardenEventViaAwoJson(JsonElement element, out WardenObjectiveEventData? data) { data = null; try { Type type = FindLoadedType("Il2CppJsonNet.JsonConvert"); if (type == null) { return false; } MethodInfo methodInfo = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public) where m.Name == "DeserializeObject" && m.IsGenericMethodDefinition select m).FirstOrDefault(delegate(MethodInfo m) { ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(string); }); if (methodInfo == null) { return false; } string text = NormalizeWardenIntelOnlyEventJson(element); object? obj = methodInfo.MakeGenericMethod(typeof(WardenObjectiveEventData)).Invoke(null, new object[1] { text }); WardenObjectiveEventData val = (WardenObjectiveEventData)((obj is WardenObjectiveEventData) ? obj : null); if (val != null) { data = val; return true; } } catch (Exception ex) { LogThrottled($"AWO Json build failed for event '{GetString(element, "Type", string.Empty)}': {ex.GetType().Name}: {ex.Message}"); } return false; } private static string NormalizeWardenIntelOnlyEventJson(JsonElement element) { try { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("Type", out var value) && value.ValueKind == JsonValueKind.String && string.Equals(value.GetString(), "WardenIntel", StringComparison.OrdinalIgnoreCase)) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (JsonProperty item in element.EnumerateObject()) { if (string.Equals(item.Name, "Type", StringComparison.OrdinalIgnoreCase)) { dictionary[item.Name] = "None"; } else { dictionary[item.Name] = JsonElementToPlainObject(item.Value); } } return JsonSerializer.Serialize(dictionary); } } catch (Exception ex) { LogThrottled("NormalizeWardenIntelOnlyEventJson failed: " + ex.GetType().Name + ": " + ex.Message); } return element.GetRawText(); } private static object? JsonElementToPlainObject(JsonElement element) { switch (element.ValueKind) { case JsonValueKind.Object: { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); { foreach (JsonProperty item in element.EnumerateObject()) { dictionary[item.Name] = JsonElementToPlainObject(item.Value); } return dictionary; } } case JsonValueKind.Array: { List list = new List(); { foreach (JsonElement item2 in element.EnumerateArray()) { list.Add(JsonElementToPlainObject(item2)); } return list; } } case JsonValueKind.String: return element.GetString(); case JsonValueKind.Number: { if (element.TryGetInt64(out var value)) { return value; } if (element.TryGetDouble(out var value2)) { return value2; } return element.GetRawText(); } case JsonValueKind.True: return true; case JsonValueKind.False: return false; default: return null; } } private static bool MatchesCurrentLevel(ConfigDocument config, uint currentLayoutId, string currentLayoutName, out string reason) { reason = string.Empty; if (config.MainLevelLayoutIDs == null || config.MainLevelLayoutIDs.Count == 0) { return false; } foreach (JsonElement mainLevelLayoutID in config.MainLevelLayoutIDs) { foreach (string item in ExtractSelectorTokens(mainLevelLayoutID)) { if (MatchesCurrentLevelLayoutString(item, currentLayoutId, currentLayoutName, out reason)) { return true; } } } return false; } private static IEnumerable ExtractSelectorTokens(JsonElement selector) { if (selector.ValueKind == JsonValueKind.Number && selector.TryGetUInt32(out var value)) { yield return value.ToString(); } else if (selector.ValueKind == JsonValueKind.String) { string @string = selector.GetString(); if (!string.IsNullOrWhiteSpace(@string)) { yield return @string; } } else { if (selector.ValueKind != JsonValueKind.Object) { yield break; } string[] array = new string[8] { "persistentID", "PersistentID", "id", "ID", "value", "Value", "LevelLayoutID", "MainLevelLayoutID" }; foreach (string propertyName in array) { if (!selector.TryGetProperty(propertyName, out var value2)) { continue; } foreach (string item in ExtractSelectorTokens(value2)) { yield return item; } } } } private static bool MatchesCurrentLevelLayoutString(string token, uint currentLayoutId, string currentLayoutName, out string reason) { reason = string.Empty; if (string.IsNullOrWhiteSpace(token)) { return false; } string text = token.Trim(); if (currentLayoutId != 0 && string.Equals(text, currentLayoutId.ToString(), StringComparison.OrdinalIgnoreCase)) { reason = "numeric text:" + text; return true; } if (!string.IsNullOrWhiteSpace(currentLayoutName) && string.Equals(text, currentLayoutName, StringComparison.OrdinalIgnoreCase)) { reason = "LevelLayoutDataBlock name:" + text; return true; } if (TryResolvePartialDataPersistentId(text, out var id) && id == currentLayoutId) { reason = $"MTFO PartialData persistentID:{text}->{id}"; return true; } if (TryResolveLevelLayoutStringToId(text, out var id2) && id2 == currentLayoutId) { reason = $"LevelLayoutDataBlock string:{text}->{id2}"; return true; } return false; } private static bool TryResolvePartialDataPersistentId(string persistentId, out uint id) { id = 0u; if (string.IsNullOrWhiteSpace(persistentId)) { return false; } if (PartialDataPersistentIdCache.TryGetValue(persistentId, out var value)) { id = value; return value != 0; } try { MethodInfo methodInfo = FindLoadedType("MTFO.Ext.PartialData.PersistentIDManager")?.GetMethod("TryGetId", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(uint).MakeByRefType() }, null); if (methodInfo != null) { object[] array = new object[2] { persistentId, 0u }; object obj = methodInfo.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { object obj2 = array[1]; if (obj2 is uint) { uint num2 = (uint)obj2; if (num2 != 0) { id = num2; PartialDataPersistentIdCache[persistentId] = id; return true; } } } } } catch (Exception ex) { LogThrottled("Could not resolve PartialData persistentID '" + persistentId + "' from runtime manager: " + ex.Message); } bool flag2 = TryResolvePersistentIdFromDumpFiles(persistentId, out id); PartialDataPersistentIdCache[persistentId] = (flag2 ? id : 0u); return flag2; } private static bool TryResolvePersistentIdFromDumpFiles(string persistentId, out uint id) { id = 0u; try { return GetOrBuildPersistentIdDumpCache().TryGetValue(persistentId, out id) && id != 0; } catch (Exception ex) { LogThrottled("Could not scan PartialData persistentID dump files: " + ex.Message); return false; } } private static Dictionary GetOrBuildPersistentIdDumpCache() { if (PersistentIdDumpCache != null) { return PersistentIdDumpCache; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { string[] files = Directory.GetFiles(Paths.PluginPath, "_persistentID.json", SearchOption.AllDirectories); foreach (string text in files) { try { using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(text), new JsonDocumentOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array) { continue; } foreach (JsonElement item in jsonDocument.RootElement.EnumerateArray()) { if (item.ValueKind == JsonValueKind.Object) { string @string = GetString(item, "GUID", GetString(item, "persistentID", GetString(item, "PersistentID", string.Empty))); uint uInt = GetUInt(item, "ID", GetUInt(item, "id", 0u)); if (uInt != 0 && !string.IsNullOrWhiteSpace(@string)) { dictionary[@string] = uInt; } } } } catch (Exception ex) { LogThrottled("Could not read PartialData dump '" + text + "': " + ex.Message); } } } catch (Exception ex2) { LogThrottled("Could not scan PartialData persistentID dump files: " + ex2.Message); } PersistentIdDumpCache = dictionary; return dictionary; } private static bool TryResolveLevelLayoutStringToId(string levelLayoutString, out uint id) { id = 0u; if (string.IsNullOrWhiteSpace(levelLayoutString)) { return false; } if (LevelLayoutStringIdCache.TryGetValue(levelLayoutString, out var value)) { id = value; return value != 0; } try { if (uint.TryParse(levelLayoutString, out var result)) { id = result; LevelLayoutStringIdCache[levelLayoutString] = id; return result != 0; } if (GameDataBlockBase.HasBlock(levelLayoutString)) { id = GameDataBlockBase.GetBlockID(levelLayoutString); LevelLayoutStringIdCache[levelLayoutString] = id; return id != 0; } } catch (Exception ex) { LogThrottled("Could not resolve LevelLayout string '" + levelLayoutString + "': " + ex.Message); } LevelLayoutStringIdCache[levelLayoutString] = 0u; return false; } private static uint GetCurrentLevelLayoutId() { try { return RundownManager.ActiveExpedition.LevelLayoutData; } catch { return 0u; } } private static string TryGetLevelLayoutName(uint id) { if (id == 0) { return string.Empty; } try { string blockName = GameDataBlockBase.GetBlockName(id); if (!string.IsNullOrWhiteSpace(blockName)) { return blockName; } } catch { } try { LevelLayoutDataBlock block = GameDataBlockBase.GetBlock(id); if (block != null && !string.IsNullOrWhiteSpace(((GameDataBlockBase)(object)block).name)) { return ((GameDataBlockBase)(object)block).name; } } catch { } return string.Empty; } private static Type? FindLoadedType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(fullName, throwOnError: false, ignoreCase: false); if (type != null) { return type; } } return null; } private static bool TryParseEnum(string text, out T value) where T : struct, Enum { value = default(T); if (string.IsNullOrWhiteSpace(text)) { return false; } if (Enum.TryParse(text, ignoreCase: true, out value)) { return true; } if (int.TryParse(text, out var result)) { value = (T)Enum.ToObject(typeof(T), result); return true; } return false; } 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 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; } if (value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var value2)) { return value2 != 0; } } 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 bool TryGetLocalizedText(JsonElement obj, string name, out LocalizedText? text) { text = null; if (obj.ValueKind != JsonValueKind.Object || !obj.TryGetProperty(name, out var value)) { return false; } return TryBuildLocalizedText(value, out text); } private static bool TryGetLocalizedTextIdOnly(JsonElement obj, string name, out LocalizedText? text) { text = null; if (obj.ValueKind != JsonValueKind.Object || !obj.TryGetProperty(name, out var value)) { return false; } return TryBuildLocalizedTextIdOnly(value, out text); } private static bool TryBuildLocalizedTextIdOnly(JsonElement value, out LocalizedText? text) { text = null; try { if (TryReadLocalizedTextId(value, out var id)) { text = CreateRootedLocalizedText(id); return text != (LocalizedText)null; } } catch (Exception ex) { LogThrottled($"Could not build ID-only LocalizedText from json value '{value}': {ex.GetType().Name}: {ex.Message}"); } return false; } private static bool TryBuildLocalizedText(JsonElement value, out LocalizedText? text) { text = null; try { if (TryReadLocalizedTextId(value, out var id)) { text = CreateRootedLocalizedText(id); return text != (LocalizedText)null; } if (TryReadLocalizedTextRawString(value, out string text2)) { text = CreateRootedLocalizedText(text2); return text != (LocalizedText)null; } } catch (Exception ex) { LogThrottled($"Could not build LocalizedText from json value '{value}': {ex.GetType().Name}: {ex.Message}"); } return false; } private static bool TryReadLocalizedTextId(JsonElement value, out uint id) { id = 0u; if (value.ValueKind == JsonValueKind.Number && value.TryGetUInt32(out id)) { return true; } if (value.ValueKind == JsonValueKind.String) { return uint.TryParse((value.GetString() ?? string.Empty).Trim(), out id); } if (value.ValueKind == JsonValueKind.Object) { string[] array = new string[9] { "Id", "ID", "id", "PersistentId", "PersistentID", "persistentID", "TextID", "TextId", "textId" }; foreach (string propertyName in array) { if (value.TryGetProperty(propertyName, out var value2) && TryReadLocalizedTextId(value2, out id)) { return true; } } } return false; } private static bool TryReadLocalizedTextRawString(JsonElement value, out string text) { text = string.Empty; if (value.ValueKind == JsonValueKind.String) { string text2 = value.GetString() ?? string.Empty; if (!string.IsNullOrWhiteSpace(text2) && !uint.TryParse(text2.Trim(), out var _)) { text = text2; return true; } } if (value.ValueKind == JsonValueKind.Object) { string[] array = new string[5] { "UntranslatedText", "Text", "text", "Value", "value" }; foreach (string propertyName in array) { if (value.TryGetProperty(propertyName, out var value2) && TryReadLocalizedTextRawString(value2, out text)) { return true; } } } return false; } private static LocalizedText? CreateRootedLocalizedText(uint id) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown try { LocalizedText val = LocalizedText.op_Implicit(id); LocalizedTextRoots.Add(val); return val; } catch (Exception ex) { try { LocalizedText val2 = new LocalizedText(); val2.Id = id; LocalizedTextRoots.Add(val2); LogThrottled($"LocalizedText implicit conversion failed for id={id}; used default constructor + Id assignment. Original={ex.GetType().Name}: {ex.Message}"); return val2; } catch (Exception ex2) { LogThrottled($"Could not create LocalizedText id={id}: {ex2.GetType().Name}: {ex2.Message}"); return null; } } } private static LocalizedText? CreateRootedLocalizedText(string untranslatedText) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown try { LocalizedText val = new LocalizedText(untranslatedText); LocalizedTextRoots.Add(val); return val; } catch (Exception ex) { LogThrottled($"Could not create untranslated LocalizedText '{untranslatedText}': {ex.GetType().Name}: {ex.Message}"); return null; } } internal static void LogThrottled(string message) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(Time.realtimeSinceStartup - _lastLogTime < 0.25f) && (!LastLogTimesByMessage.TryGetValue(message, out var value) || !(realtimeSinceStartup - value < 30f))) { _lastLogTime = realtimeSinceStartup; LastLogTimesByMessage[message] = realtimeSinceStartup; ManualLogSource? log = Log; if (log != null) { log.LogWarning((object)message); } } } internal static bool IsTransientLookupFailure(string failure) { if (string.IsNullOrWhiteSpace(failure)) { return false; } string text = failure.ToLowerInvariant(); if (!text.Contains("not ready") && !text.Contains("retrying") && !text.Contains("out-of-bounds spam") && !text.Contains("dimensions are not ready") && !text.Contains("zones are not ready")) { return text.Contains("currentfloor is not ready"); } return true; } } internal static class SpoIndexResolver { private static Type? FindType(params string[] fullNames) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { foreach (string name in fullNames) { try { Type type = assembly.GetType(name, throwOnError: false, ignoreCase: true); if (type != null) { return type; } } catch { } } } return null; } private static object? GetCurrent(string typeName) { Type type = FindType(typeName, "ScanPosOverride." + typeName.Split('.').Last()); if (type == null) { return null; } try { object obj = type.GetField("Current", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj != null) { return obj; } } catch { } try { object obj3 = type.GetProperty("Current", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj3 != null) { return obj3; } } catch { } return null; } public static bool TryGetScanSpoIndex(CP_Bioscan_Core scan, out int index, out string source) { index = -1; source = "Missing:ScanPosOverride.PuzzleOverrideIndex"; if ((Object)(object)scan == (Object)null) { return false; } object current = GetCurrent("ScanPosOverride.Managers.PuzzleOverrideManager"); if (current == null) { return false; } try { object obj = current.GetType().GetMethod("GetBioscanCoreOverrideIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(CP_Bioscan_Core) }, null)?.Invoke(current, new object[1] { scan }); if (obj != null && uint.TryParse(obj.ToString(), out var result) && result != 0) { index = checked((int)result); source = "ScanPosOverride.PuzzleOverrideManager.GetBioscanCoreOverrideIndex"; return true; } } catch (Exception ex) { Runtime.LogThrottled("SPO scan index reflection failed: " + ex.GetType().Name + ": " + ex.Message); } return false; } public static bool TryGetBigPickupSpoIndex(Component? component, out int index, out string source) { index = -1; source = "Missing:ScanPosOverride.BigPickupItemIndex"; if ((Object)(object)component == (Object)null) { return false; } CarryItemPickup_Core val = ((Il2CppObjectBase)component).TryCast(); if ((Object)(object)val == (Object)null) { try { val = component.GetComponent(); } catch { } } if ((Object)(object)val == (Object)null) { return false; } object current = GetCurrent("ScanPosOverride.Managers.PuzzleReqItemManager"); if (current == null) { return false; } try { if (current.GetType().GetField("BigPickupItemsInLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(current) is IEnumerable enumerable) { int num = SafeInstanceId((Component)(object)val); foreach (object item in enumerable) { if (item == null) { continue; } object obj2 = item.GetType().GetProperty("Key")?.GetValue(item); object obj3 = item.GetType().GetProperty("Value")?.GetValue(item); if (obj2 == null || obj3 == null || !int.TryParse(obj2.ToString(), out var result) || result <= 0) { continue; } Component val2 = (Component)((obj3 is Component) ? obj3 : null); if ((Object)(object)val2 == (Object)null) { CarryItemPickup_Core val3 = (CarryItemPickup_Core)((obj3 is CarryItemPickup_Core) ? obj3 : null); if (val3 != null) { val2 = (Component)(object)val3; } } if (!((Object)(object)val2 == (Object)null) && (val2 == val || SafeInstanceId(val2) == num)) { index = result; source = "ScanPosOverride.PuzzleReqItemManager.BigPickupItemsInLevel"; return true; } } } } catch (Exception ex) { Runtime.LogThrottled("SPO BigPickup dictionary reflection failed: " + ex.GetType().Name + ": " + ex.Message); } try { MethodInfo method = current.GetType().GetMethod("GetBigPickupItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null); if (method != null) { int num2 = SafeInstanceId((Component)(object)val); for (int i = 1; i <= 512; i++) { object obj4 = method.Invoke(current, new object[1] { i }); Component val4 = (Component)((obj4 is Component) ? obj4 : null); if ((Object)(object)val4 == (Object)null) { CarryItemPickup_Core val5 = (CarryItemPickup_Core)((obj4 is CarryItemPickup_Core) ? obj4 : null); if (val5 != null) { val4 = (Component)(object)val5; } } if ((Object)(object)val4 != (Object)null && (val4 == val || SafeInstanceId(val4) == num2)) { index = i; source = "ScanPosOverride.PuzzleReqItemManager.GetBigPickupItem"; return true; } } } } catch (Exception ex2) { Runtime.LogThrottled("SPO BigPickup GetBigPickupItem reflection failed: " + ex2.GetType().Name + ": " + ex2.Message); } return false; } private static int SafeInstanceId(Component c) { try { return ((Object)c).GetInstanceID(); } catch { return ((Il2CppObjectBase)c).Pointer.GetHashCode(); } } } internal static class ScanTriggerManager { private readonly struct ScanBindingInfo { public readonly int Index; public readonly string Source; public ScanBindingInfo(int index, string source) { Index = index; Source = source; } } internal static readonly Dictionary RuleStates = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ScanStates = new Dictionary(); private static readonly Dictionary ActiveScanRefs = new Dictionary(); private static readonly Dictionary ScanRepeatNextDueTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LastActivationTimes = new Dictionary(); private static readonly HashSet ActivationFiredThisCycle = new HashSet(); private static bool _dumpedScanIndexes; private static float _lastScanDumpTryTime; internal static void Reset() { RuleStates.Clear(); ScanStates.Clear(); ActiveScanRefs.Clear(); ScanRepeatNextDueTimes.Clear(); LastActivationTimes.Clear(); ActivationFiredThisCycle.Clear(); _dumpedScanIndexes = false; _lastScanDumpTryTime = 0f; } private static bool IsScanRepeatDue(string dueKey, float cooldown) { if (cooldown < 1f) { cooldown = 1f; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (ScanRepeatNextDueTimes.TryGetValue(dueKey, out var value) && realtimeSinceStartup < value) { return false; } ScanRepeatNextDueTimes[dueKey] = realtimeSinceStartup + cooldown; return true; } internal static void ProcessScanRepeatEvents() { try { List<(ConfigDocument, ScanTriggerRule)> activeScanTriggers = Runtime.GetActiveScanTriggers(); if (activeScanTriggers.Count == 0) { return; } bool flag = false; bool flag2 = false; foreach (var item in activeScanTriggers) { string text = Runtime.NormalizeTriggerMode(item.Item2.TriggerMode); if (text == "onallplayersinsidescan") { flag = true; } if (text == "onallplayersexitedscan") { flag2 = true; } } if (!flag && !flag2) { return; } int num = CountEligibleScanPlayers(); if (num <= 0) { return; } KeyValuePair[] array = ActiveScanRefs.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; int key = keyValuePair.Key; CP_Bioscan_Core value = keyValuePair.Value; if ((Object)(object)value == (Object)null) { ActiveScanRefs.Remove(key); ScanStates.Remove(key); } else { if (!ScanStates.TryGetValue(key, out ScanRuntimeState value2) || (!value2.IsActive && !value2.AllPlayersExitedRepeatActive)) { continue; } int index = GetScanBindingInfo(value).Index; int num2 = GetPlayersInScan(value); if (num2 < 0) { num2 = 0; } bool flag3 = value2.IsActive && num2 >= num; bool flag4 = value2.IsActive && num2 == 0 && value2.AllPlayersExitedRepeatActive; if (flag && flag3) { foreach (var (configDocument, scanTriggerRule) in activeScanTriggers) { if (!(Runtime.NormalizeTriggerMode(scanTriggerRule.TriggerMode) != "onallplayersinsidescan") && (scanTriggerRule.PuzzleOverrideIndex < 0 || scanTriggerRule.PuzzleOverrideIndex == index)) { string text2 = $"{key}:allplayersinside-repeat"; if (IsScanRepeatDue(configDocument.FilePath + "::scan-repeat::" + scanTriggerRule.ID + "::" + text2, scanTriggerRule.Cooldown)) { Runtime.FireScanTrigger(configDocument, scanTriggerRule, "OnAllPlayersInsideScan", GetScanSourceName(value), index, Math.Min(Math.Max(num2, 1), 4), text2); } } } } if (!(flag2 && flag4)) { continue; } int playersInScan = Math.Min(Math.Max(num, 1), 4); foreach (var (configDocument2, scanTriggerRule2) in activeScanTriggers) { if (!(Runtime.NormalizeTriggerMode(scanTriggerRule2.TriggerMode) != "onallplayersexitedscan") && (scanTriggerRule2.PuzzleOverrideIndex < 0 || scanTriggerRule2.PuzzleOverrideIndex == index)) { string text3 = $"{key}:allplayersexited-repeat"; if (IsScanRepeatDue(configDocument2.FilePath + "::scan-repeat::" + scanTriggerRule2.ID + "::" + text3, scanTriggerRule2.Cooldown)) { Runtime.FireScanTrigger(configDocument2, scanTriggerRule2, "OnAllPlayersExitedScan", GetScanSourceName(value), index, playersInScan, text3); } } } } } } catch (Exception ex) { Runtime.LogThrottled("ProcessScanRepeatEvents failed: " + ex.GetType().Name + ": " + ex.Message); } } internal static void DumpScanIndexesIfNeeded() { if (_dumpedScanIndexes || !GameStateManager.IsInExpedition || Time.realtimeSinceStartup - _lastScanDumpTryTime < 3f) { return; } _lastScanDumpTryTime = Time.realtimeSinceStartup; int num = 0; try { foreach (CP_Bioscan_Core item in Object.FindObjectsOfType()) { if (!((Object)(object)item == (Object)null)) { num++; ScanBindingInfo scanBindingInfo = GetScanBindingInfo(item); Runtime.LogVerbose($"CTE Scan Index={scanBindingInfo.Index} Core={GetInstanceId(item)} PuzzleIndex= PuzzleOverrideIndex={scanBindingInfo.Index} Source={scanBindingInfo.Source} Name='{SafeScanObjectName((Component)(object)item)}' ScanSource='{GetScanSourceName(item)}' PlayersInScan={GetPlayersInScan(item)} Status='{SafeScanStringMember(item, "m_state", "State", "Status", "m_status")}' Position={SafeScanPosition((Component)(object)item)}"); } } } catch (Exception ex) { Runtime.LogThrottled("Scan index dump failed: " + ex.GetType().Name + ": " + ex.Message); } if (num > 0) { _dumpedScanIndexes = true; Runtime.LogVerbose($"CTE scan binding dump complete. Scans={num}. Use BepInEx ScanPosOverride PuzzleOverrideIndex for ScanTriggers[Index]."); } } internal static void OnScanActivated(CP_Bioscan_Core? scan) { if ((Object)(object)scan == (Object)null) { return; } try { int instanceId = GetInstanceId(scan); ActiveScanRefs[instanceId] = scan; ScanRuntimeState orCreateScanState = GetOrCreateScanState(instanceId); if (orCreateScanState.ActivatedThisCycle || ActivationFiredThisCycle.Contains(instanceId) || (LastActivationTimes.TryGetValue(instanceId, out var value) && Time.realtimeSinceStartup - value < 0.25f)) { return; } LastActivationTimes[instanceId] = Time.realtimeSinceStartup; ActivationFiredThisCycle.Add(instanceId); ScanBindingInfo scanBindingInfo = GetScanBindingInfo(scan); int index = scanBindingInfo.Index; int playersInScan = GetPlayersInScan(scan); orCreateScanState.IsActive = true; orCreateScanState.ActivatedThisCycle = true; orCreateScanState.ExitTriggeredThisCycle = false; orCreateScanState.HadPlayersInside = playersInScan > 0; orCreateScanState.HasObserved = true; orCreateScanState.LastPlayerCount = playersInScan; orCreateScanState.ActivationPlayerCountEventsFired = playersInScan > 0; string stateKeySuffix = $"{instanceId}:activated"; Runtime.LogVerbose($"Detected scan activation. Index={index}, BindingSource={scanBindingInfo.Source}, PlayersInScan={playersInScan}, Source={GetScanSourceName(scan)}"); if (playersInScan <= 0) { Runtime.LogVerbose($"Detected scan activation with no players. Waiting for first player-count edge before firing OnScanActivated. Index={index}, BindingSource={scanBindingInfo.Source}"); return; } foreach (var (config, trigger) in Runtime.GetActiveScanTriggers()) { Runtime.FireScanTrigger(config, trigger, "OnScanActivated", GetScanSourceName(scan), index, playersInScan, stateKeySuffix); } HandleAllPlayersScanState(scan, orCreateScanState, 0, playersInScan, "Activate"); } catch (Exception ex) { Runtime.LogThrottled("OnScanActivated failed: " + ex.GetType().Name + ": " + ex.Message); } } internal static void OnScanPlayersChanged(CP_Bioscan_Core? scan) { if (!((Object)(object)scan == (Object)null)) { OnScanPlayersChanged(scan, GetPlayersInScan(scan), "property"); } } internal static void OnScanPlayersChanged(CP_Bioscan_Core? scan, int currentPlayers, string source) { if ((Object)(object)scan == (Object)null) { return; } try { int instanceId = GetInstanceId(scan); ActiveScanRefs[instanceId] = scan; ScanBindingInfo scanBindingInfo = GetScanBindingInfo(scan); int index = scanBindingInfo.Index; if (currentPlayers < 0) { currentPlayers = GetPlayersInScan(scan); } ScanRuntimeState orCreateScanState = GetOrCreateScanState(instanceId); if (IsScanClosedOrFinishedSource(source)) { if (orCreateScanState.IsActive || orCreateScanState.HasObserved || orCreateScanState.LastPlayerCount > 0) { Runtime.LogVerbose($"Detected scan end state. Closing current scan trigger cycle without firing events. Index={index}, BindingSource={scanBindingInfo.Source}, PlayersInScan={currentPlayers}, Source={source}"); } CloseScanCycle(instanceId, orCreateScanState); return; } if (!orCreateScanState.IsActive && !orCreateScanState.HasObserved) { orCreateScanState.HasObserved = true; orCreateScanState.LastPlayerCount = currentPlayers; if (currentPlayers > 0) { orCreateScanState.HadPlayersInside = true; } Runtime.LogVerbose($"Ignored scan player count change before activation. Index={index}, BindingSource={scanBindingInfo.Source}, Current={currentPlayers}, Source={source}"); return; } int num = (orCreateScanState.HasObserved ? orCreateScanState.LastPlayerCount : 0); orCreateScanState.HasObserved = true; orCreateScanState.IsActive = true; orCreateScanState.LastPlayerCount = currentPlayers; if (currentPlayers > 0) { orCreateScanState.HadPlayersInside = true; } HandleAllPlayersScanState(scan, orCreateScanState, num, currentPlayers, source); if (currentPlayers == num) { return; } if (currentPlayers > num) { orCreateScanState.ExitTriggeredThisCycle = false; Runtime.LogVerbose($"Detected scan player count increase. Event=OnScanActivated, Index={index}, BindingSource={scanBindingInfo.Source}, Previous={num}, Current={currentPlayers}, Source={source}"); if (currentPlayers < 1 || currentPlayers > 4) { return; } orCreateScanState.ActivationPlayerCountEventsFired = true; string stateKeySuffix = $"{instanceId}:activated-playercount:{++orCreateScanState.ActivationEdgeSequence}:{num}->{currentPlayers}"; Runtime.LogVerbose($"Detected scan activation player count edge. Event=OnScanActivated, Index={index}, BindingSource={scanBindingInfo.Source}, PlayersInScan={currentPlayers}, Previous={num}, Source={source}"); { foreach (var (config, trigger) in Runtime.GetActiveScanTriggers()) { Runtime.FireScanTrigger(config, trigger, "OnScanActivated", GetScanSourceName(scan), index, currentPlayers, stateKeySuffix); } return; } } if (num <= 0 || currentPlayers != 0 || !orCreateScanState.HadPlayersInside) { return; } orCreateScanState.ExitTriggeredThisCycle = true; string text = "OnPlayerExitScan"; string stateKeySuffix2 = $"{instanceId}:{text}:{++orCreateScanState.ExitEdgeSequence}:{num}->{currentPlayers}"; Runtime.LogVerbose($"Detected scan player count decrease. Event={text}, Index={index}, BindingSource={scanBindingInfo.Source}, Previous={num}, Current={currentPlayers}, Source={source}"); foreach (var (config2, trigger2) in Runtime.GetActiveScanTriggers()) { Runtime.FireScanTrigger(config2, trigger2, text, GetScanSourceName(scan), index, num, stateKeySuffix2); } } catch (Exception ex) { Runtime.LogThrottled("OnScanPlayersChanged failed: " + ex.GetType().Name + ": " + ex.Message); } } private static void HandleAllPlayersScanState(CP_Bioscan_Core scan, ScanRuntimeState state, int previousPlayers, int currentPlayers, string source) { try { int num = CountEligibleScanPlayers(); if (num <= 0) { return; } int instanceId = GetInstanceId(scan); ActiveScanRefs[instanceId] = scan; int index = GetScanBindingInfo(scan).Index; bool flag = state.AllPlayersInsideNow || previousPlayers >= num; if (currentPlayers >= num) { state.AllPlayersEnteredThisCycle = true; state.AllPlayersExitedRepeatActive = false; if (!flag) { state.AllPlayersInsideNow = true; string stateKeySuffix = $"{instanceId}:allplayersenter:{++state.AllPlayersEnterEdgeSequence}"; { foreach (var (config, trigger) in Runtime.GetActiveScanTriggers()) { Runtime.FireScanTrigger(config, trigger, "OnAllPlayersEnterScan", GetScanSourceName(scan), index, Math.Min(Math.Max(currentPlayers, 1), 4), stateKeySuffix); } return; } } state.AllPlayersInsideNow = true; return; } if (state.AllPlayersInsideNow && currentPlayers < num) { state.AllPlayersInsideNow = false; } if (!state.AllPlayersEnteredThisCycle || previousPlayers <= 0 || currentPlayers != 0) { return; } state.AllPlayersEnteredThisCycle = false; state.AllPlayersExitedRepeatActive = true; string stateKeySuffix2 = $"{instanceId}:allplayersexit:{++state.AllPlayersExitEdgeSequence}"; int playersInScan = Math.Min(Math.Max(previousPlayers, num), 4); foreach (var (config2, trigger2) in Runtime.GetActiveScanTriggers()) { Runtime.FireScanTrigger(config2, trigger2, "OnAllPlayersExitScan", GetScanSourceName(scan), index, playersInScan, stateKeySuffix2); } Runtime.LogVerbose($"Detected all players exit scan. Index={index}, EligiblePlayers={num}, Previous={previousPlayers}, Current={currentPlayers}, Source={source}"); } catch (Exception ex) { Runtime.LogThrottled("HandleAllPlayersScanState failed: " + ex.GetType().Name + ": " + ex.Message); } } private static int CountEligibleScanPlayers() { int num = 0; List playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel; if (playerAgentsInLevel == null) { return 0; } for (int i = 0; i < playerAgentsInLevel.Count; i++) { try { PlayerAgent val = playerAgentsInLevel[i]; if ((Object)(object)val != (Object)null && ((Agent)val).Alive) { num++; } } catch { } } return num; } internal static void OnScanStoppedOrTimedOut(CP_Bioscan_Core? scan, string source) { if ((Object)(object)scan == (Object)null) { return; } try { int instanceId = GetInstanceId(scan); ActivationFiredThisCycle.Remove(instanceId); LastActivationTimes.Remove(instanceId); ScanRuntimeState orCreateScanState = GetOrCreateScanState(instanceId); if (!orCreateScanState.HasObserved) { ResetScanCycle(orCreateScanState); return; } if (orCreateScanState.LastPlayerCount > 0) { Runtime.LogVerbose($"Scan stopped or completed while players were inside. Closing scan cycle without firing OnPlayerExitScan. Source={source}, PreviousPlayers={orCreateScanState.LastPlayerCount}"); } CloseScanCycle(instanceId, orCreateScanState); } catch (Exception ex) { Runtime.LogThrottled("OnScanStoppedOrTimedOut failed: " + ex.GetType().Name + ": " + ex.Message); } } private static bool IsScanClosedOrFinishedSource(string source) { if (string.IsNullOrWhiteSpace(source)) { return false; } string text = source.Trim().ToLowerInvariant(); if (!text.Contains("finished") && !text.Contains("finish") && !text.Contains("complete") && !text.Contains("completed") && !text.Contains("solved") && !text.Contains("success") && !text.Contains("done") && !text.Contains("inactive") && !text.Contains("timeout") && !text.Contains("timedout") && !text.Contains("deactivate")) { return text.Contains("disabled"); } return true; } private static void CloseScanCycle(int instanceId, ScanRuntimeState state) { ActivationFiredThisCycle.Remove(instanceId); LastActivationTimes.Remove(instanceId); ResetScanCycle(state); } private static ScanRuntimeState GetOrCreateScanState(int instanceId) { if (!ScanStates.TryGetValue(instanceId, out ScanRuntimeState value)) { value = new ScanRuntimeState(); ScanStates[instanceId] = value; } return value; } private static void ResetScanCycle(ScanRuntimeState state) { state.IsActive = false; state.ActivatedThisCycle = false; state.ActivationPlayerCountEventsFired = false; state.HadPlayersInside = false; state.ExitTriggeredThisCycle = false; state.ActivationEdgeSequence = 0; state.ExitEdgeSequence = 0; state.AllPlayersInsideNow = false; state.AllPlayersEnteredThisCycle = false; state.AllPlayersExitedRepeatActive = false; state.AllPlayersEnterEdgeSequence = 0; state.AllPlayersExitEdgeSequence = 0; state.HasObserved = false; state.LastPlayerCount = 0; state.Fired = false; state.LastFireTime = -999999f; } internal static CP_Bioscan_Core? TryFindCoreFromScanner(CP_PlayerScanner? scanner) { if ((Object)(object)scanner == (Object)null) { return null; } try { CP_Bioscan_Core component = ((Component)scanner).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } } catch { } try { CP_Bioscan_Core componentInParent = ((Component)scanner).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } } catch { } try { CP_Bioscan_Core componentInChildren = ((Component)scanner).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } } catch { } return null; } private static int GetInstanceId(CP_Bioscan_Core scan) { try { return ((Object)scan).GetInstanceID(); } catch { return ((Il2CppObjectBase)scan).Pointer.GetHashCode(); } } private static ScanBindingInfo GetScanBindingInfo(CP_Bioscan_Core scan) { if (SpoIndexResolver.TryGetScanSpoIndex(scan, out int index, out string source)) { return new ScanBindingInfo(index, source); } if (TryGetPuzzleOverrideIndexFromScanPosOverride(scan, out int index2, out string source2)) { return new ScanBindingInfo(index2, source2); } return new ScanBindingInfo(-1, "Missing:ScanPosOverride.PuzzleOverrideIndex"); } private static bool TryGetPuzzleOverrideIndexFromScanPosOverride(CP_Bioscan_Core scan, out int index, out string source) { index = -1; source = string.Empty; foreach (Component item in EnumerateScanRelatedComponents(scan)) { if (!((Object)(object)item == (Object)null)) { string text = SafeTypeName(item); if ((text.IndexOf("ScanPosOverride", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("ScanPositionOverride", StringComparison.OrdinalIgnoreCase) >= 0) && TryReadIntMember(item, out index, "PuzzleOverrideIndex", "puzzleOverrideIndex", "m_puzzleOverrideIndex")) { source = text + ".PuzzleOverrideIndex"; return true; } } } foreach (Component item2 in EnumerateScanRelatedComponents(scan)) { if (!((Object)(object)item2 == (Object)null) && TryReadIntMember(item2, out index, "PuzzleOverrideIndex", "puzzleOverrideIndex", "m_puzzleOverrideIndex")) { source = SafeTypeName(item2) + ".PuzzleOverrideIndex"; return true; } } return false; } private static IEnumerable EnumerateScanRelatedComponents(CP_Bioscan_Core scan) { yield return (Component)(object)scan; Il2CppArrayBase val = null; try { val = ((Component)scan).GetComponents(); } catch { } if (val != null) { foreach (Component item in val) { if ((Object)(object)item != (Object)null) { yield return item; } } } Il2CppArrayBase val2 = null; try { val2 = ((Component)scan).GetComponentsInParent(true); } catch { } if (val2 != null) { foreach (Component item2 in val2) { if ((Object)(object)item2 != (Object)null) { yield return item2; } } } Il2CppArrayBase val3 = null; try { val3 = ((Component)scan).GetComponentsInChildren(true); } catch { } if (val3 == null) { yield break; } foreach (Component item3 in val3) { if ((Object)(object)item3 != (Object)null) { yield return item3; } } } private static bool TryReadIntMember(object obj, out int value, params string[] names) { value = -1; Type type = obj.GetType(); foreach (string name in names) { try { object obj2 = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && int.TryParse(obj2.ToString(), out value)) { return true; } } catch { } try { object obj4 = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj4 != null && int.TryParse(obj4.ToString(), out value)) { return true; } } catch { } } return false; } private static string SafeTypeName(object obj) { try { return obj.GetType().FullName ?? obj.GetType().Name; } catch { return ""; } } private static int GetInternalPuzzleIndex(CP_Bioscan_Core scan) { try { return scan.m_puzzleIndex; } catch { } try { object obj2 = ((object)scan).GetType().GetProperty("m_puzzleIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(scan); if (obj2 is int result) { return result; } if (obj2 != null && int.TryParse(obj2.ToString(), out var result2)) { return result2; } } catch { } return -1; } private static int GetPlayersInScan(CP_Bioscan_Core scan) { try { List playersOnScan = scan.PlayersOnScan; if (playersOnScan != null) { return playersOnScan.Count; } } catch { } try { return scan.m_currentPlayerCount; } catch { } return 0; } private static string SafeScanObjectName(Component source) { try { return ((Object)(object)source.gameObject != (Object)null) ? ((Object)source.gameObject).name : ((Object)source).ToString(); } catch { return ((Object)source).ToString(); } } private static string SafeScanPosition(Component source) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { Vector3 position = source.transform.position; return $"({position.x:F2},{position.y:F2},{position.z:F2})"; } catch { return ""; } } private static string SafeScanStringMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } } catch { } } return string.Empty; } private static string GetScanSourceName(CP_Bioscan_Core scan) { try { iChainedPuzzleOwner owner = scan.Owner; if (owner != null) { return ((object)owner).ToString() ?? string.Empty; } } catch { } return $"CP_Bioscan_Core#{GetInstanceId(scan)}"; } } [HarmonyPatch(typeof(CP_Bioscan_Core), "Activate")] internal static class CP_Bioscan_Core_Activate_Patch { private static void Postfix(CP_Bioscan_Core __instance) { ScanTriggerManager.OnScanActivated(__instance); } } [HarmonyPatch(typeof(CP_PlayerScanner), "StartScan")] internal static class CP_PlayerScanner_StartScan_Patch { private static void Postfix(CP_PlayerScanner __instance) { ScanTriggerManager.OnScanActivated(ScanTriggerManager.TryFindCoreFromScanner(__instance)); } } [HarmonyPatch(typeof(CP_PlayerScanner), "StopScan")] internal static class CP_PlayerScanner_StopScan_Patch { private static void Postfix(CP_PlayerScanner __instance) { ScanTriggerManager.OnScanStoppedOrTimedOut(ScanTriggerManager.TryFindCoreFromScanner(__instance), "CP_PlayerScanner.StopScan"); } } [HarmonyPatch(typeof(CP_Bioscan_Core), "Deactivate")] internal static class CP_Bioscan_Core_Deactivate_Patch { private static void Postfix(CP_Bioscan_Core __instance) { ScanTriggerManager.OnScanStoppedOrTimedOut(__instance, "CP_Bioscan_Core.Deactivate"); } } [HarmonyPatch(typeof(CP_Bioscan_Core), "SetTimeOut")] internal static class CP_Bioscan_Core_SetTimeOut_Patch { private static void Postfix(CP_Bioscan_Core __instance) { ScanTriggerManager.OnScanStoppedOrTimedOut(__instance, "CP_Bioscan_Core.SetTimeOut"); } } [HarmonyPatch(typeof(CP_Bioscan_Core), "Master_OnPlayerScanChangedCheckProgress")] internal static class CP_Bioscan_Core_Master_OnPlayerScanChangedCheckProgress_Patch { private static void Postfix(CP_Bioscan_Core __instance, float scanProgress, List playersInScan, int inScanMax, Il2CppStructArray reqObjsInScan) { int currentPlayers = -1; try { if (playersInScan != null) { currentPlayers = playersInScan.Count; } } catch { } ScanTriggerManager.OnScanPlayersChanged(__instance, currentPlayers, "Master_OnPlayerScanChangedCheckProgress"); } } [HarmonyPatch(typeof(CP_Bioscan_Core), "OnSyncStateChange")] internal static class CP_Bioscan_Core_OnSyncStateChange_Patch { private static void Postfix(CP_Bioscan_Core __instance, eBioscanStatus status, float progress, List playersInScan, int playersMax, Il2CppStructArray reqItemStatus, bool isDropinState) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) int currentPlayers = -1; try { if (playersInScan != null) { currentPlayers = playersInScan.Count; } } catch { } ScanTriggerManager.OnScanPlayersChanged(__instance, currentPlayers, $"OnSyncStateChange:{status}"); } } internal static class InteractTriggerManager { private sealed class BigPickupCycleState { public bool WaitingForDrop; public int CompletedCycles; } private sealed class PendingTerminalEvent { public Component Terminal; public PlayerAgent? Player; public string EventName = string.Empty; public string Source = string.Empty; public float DueTime; public string Key = string.Empty; } internal static readonly Dictionary RuleStates = new Dictionary(StringComparer.OrdinalIgnoreCase); internal static int FiredDispatchCount; private static readonly Dictionary LastRawEventTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary TerminalUseStates = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LastTerminalTransitionTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Queue PendingTerminalEvents = new Queue(); private static readonly HashSet PendingTerminalEventKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet TerminalUsingRepeatEligibleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet TerminalExitedRepeatEligibleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet TerminalEverUsedKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary TerminalRepeatObjects = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary TerminalRepeatNextDueTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BigPickupHeldStates = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LastBigPickupTransitionTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BigPickupCycleStates = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet BigPickupRepeatEligibleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BigPickupRepeatObjects = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BigPickupRepeatNextDueTimes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RuntimeIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary NextIndexByType = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _dumpedTargetIndexes; private static float _lastDumpTryTime; internal static void Reset() { RuleStates.Clear(); FiredDispatchCount = 0; LastRawEventTimes.Clear(); TerminalUseStates.Clear(); LastTerminalTransitionTimes.Clear(); PendingTerminalEvents.Clear(); PendingTerminalEventKeys.Clear(); TerminalUsingRepeatEligibleKeys.Clear(); TerminalExitedRepeatEligibleKeys.Clear(); TerminalEverUsedKeys.Clear(); TerminalRepeatObjects.Clear(); TerminalRepeatNextDueTimes.Clear(); Runtime.ClearTerminalSelectorCache(); BigPickupHeldStates.Clear(); LastBigPickupTransitionTimes.Clear(); BigPickupCycleStates.Clear(); BigPickupRepeatEligibleKeys.Clear(); BigPickupRepeatObjects.Clear(); BigPickupRepeatNextDueTimes.Clear(); RuntimeIndexes.Clear(); NextIndexByType.Clear(); _dumpedTargetIndexes = false; _lastDumpTryTime = 0f; } internal static int GetRuntimeIndex(string sourceKind, Component? source) { if ((Object)(object)source == (Object)null) { return -1; } try { string text = (string.IsNullOrWhiteSpace(sourceKind) ? "Any" : sourceKind.Trim().ToLowerInvariant()); string key = text + ":" + ((Object)source).GetInstanceID(); if (RuntimeIndexes.TryGetValue(key, out var value)) { return value; } if (!NextIndexByType.TryGetValue(text, out var value2)) { value2 = 0; } RuntimeIndexes[key] = value2; NextIndexByType[text] = value2 + 1; return value2; } catch { return -1; } } internal static void DumpTargetIndexesIfNeeded() { if (_dumpedTargetIndexes || !GameStateManager.IsInExpedition || Time.realtimeSinceStartup - _lastDumpTryTime < 3f) { return; } _lastDumpTryTime = Time.realtimeSinceStartup; int num = 0; int num2 = 0; try { foreach (CarryItemPickup_Core item in Object.FindObjectsOfType()) { if (!((Object)(object)item == (Object)null)) { num++; int runtimeIndex = GetRuntimeIndex("bigpickup", (Component?)(object)item); int index = -1; string source = "Missing:ScanPosOverride.BigPickupItemIndex"; SpoIndexResolver.TryGetBigPickupSpoIndex((Component?)(object)item, out index, out source); BigPickupHeldStates[BuildStableComponentKey("bigpickup", (Component)(object)item)] = false; Runtime.LogVerbose($"CTE BigPickup SPOIndex={index} CTERuntimeIndex={runtimeIndex} Source='{source}' Name='{SafeObjectName((Component)(object)item)}' PublicName='{SafeStringMember(item, "PublicName")}' ItemKey='{SafeStringMember(item, "m_itemKey")}' SerialNumber={SafeIntMember(item, "m_serialNumber", "SerialNumber")} SyncID={SafeIntMember(item, "SyncID", "_SyncID_k__BackingField")} Position={SafePosition((Component)(object)item)}"); } } } catch (Exception ex) { Runtime.LogThrottled("BigPickup index dump failed: " + ex.GetType().Name + ": " + ex.Message); } try { foreach (LG_ComputerTerminal item2 in Object.FindObjectsOfType()) { if (!((Object)(object)item2 == (Object)null)) { num2++; int runtimeIndex2 = GetRuntimeIndex("terminal", (Component?)(object)item2); string value = SafeNestedStringMember(item2, "m_serial", "Serial"); Runtime.CacheTerminalSelectorsForTerminal(item2, runtimeIndex2); Runtime.LogVerbose($"CTE Terminal Index={runtimeIndex2} TSL='{Runtime.GetTerminalTslSelectorText(item2)}' TSLFallback='[TERMINAL_{runtimeIndex2}]' Name='{SafeObjectName((Component)(object)item2)}' SerialNumber={SafeIntMember(item2, "m_serialNumber", "SerialNumber")} SerialText='{value}' ItemKey='{SafeStringMember(item2, "ItemKey", "_ItemKey_k__BackingField")}' Position={SafePosition((Component)(object)item2)}"); } } } catch (Exception ex2) { Runtime.LogThrottled("Terminal index dump failed: " + ex2.GetType().Name + ": " + ex2.Message); } if (num + num2 > 0) { _dumpedTargetIndexes = true; Runtime.LogVerbose($"CTE target index dump complete. BigPickups={num}, Terminals={num2}. Use BigPickup SPOIndex (ScanPosOverride/ECC Item Index, starts at 1) for big item triggers; use TerminalSerial/TSL for terminal triggers."); } } private static string SafeObjectName(Component source) { try { return ((Object)(object)source.gameObject != (Object)null) ? ((Object)source.gameObject).name : ((Object)source).ToString(); } catch { return ((Object)source).ToString(); } } private static string SafePosition(Component source) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { Vector3 position = source.transform.position; return $"({position.x:F2},{position.y:F2},{position.z:F2})"; } catch { return ""; } } private static int SafeIntMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && int.TryParse(obj2.ToString(), out var result)) { return result; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null && int.TryParse(obj2.ToString(), out result)) { return result; } } catch { } } return -1; } private static string SafeStringMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2.ToString() ?? string.Empty; } } catch { } } return string.Empty; } private static string SafeNestedStringMember(object obj, params string[] names) { object obj2 = null; foreach (string name in names) { try { obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 == null) { obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); } if (obj2 != null) { break; } } catch { } } if (obj2 == null) { return string.Empty; } try { object obj4 = (obj2.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? obj2.GetType().GetProperty("Text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj2); if (obj4 != null) { return obj4.ToString() ?? string.Empty; } } catch { } return obj2.ToString() ?? string.Empty; } private static bool TryGetMinimumInteractRepeatCooldown(string targetType, string triggerMode, out float cooldown) { cooldown = float.MaxValue; bool flag = false; foreach (var activeInteractTrigger in Runtime.GetActiveInteractTriggers()) { InteractTriggerRule item = activeInteractTrigger.Trigger; if (!(Runtime.NormalizeTargetType(item.TargetType) != targetType) && !(Runtime.NormalizeInteractionTriggerMode(item.TriggerMode) != triggerMode)) { flag = true; if (item.Cooldown < cooldown) { cooldown = item.Cooldown; } } } if (!flag) { return false; } if (cooldown == float.MaxValue) { cooldown = 1f; } if (cooldown < 1f) { cooldown = 1f; } return true; } private static bool IsRepeatDue(Dictionary nextDueTimes, string dueKey, float cooldown) { if (cooldown < 1f) { cooldown = 1f; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (nextDueTimes.TryGetValue(dueKey, out var value) && realtimeSinceStartup < value) { return false; } nextDueTimes[dueKey] = realtimeSinceStartup + cooldown; return true; } internal static void ProcessTerminalRepeatEvents() { if (!GameStateManager.IsInExpedition) { return; } bool flag = false; foreach (var activeInteractTrigger in Runtime.GetActiveInteractTriggers()) { InteractTriggerRule item = activeInteractTrigger.Trigger; if (!(Runtime.NormalizeTargetType(item.TargetType) != "terminal")) { string text = Runtime.NormalizeInteractionTriggerMode(item.TriggerMode); if (text == "onterminalusing" || text == "onterminalexited") { flag = true; break; } } } if (!flag || TerminalRepeatObjects.Count == 0) { return; } KeyValuePair[] array = TerminalRepeatObjects.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; string key = keyValuePair.Key; Component value = keyValuePair.Value; if ((Object)(object)value == (Object)null) { TerminalRepeatObjects.Remove(key); TerminalUseStates.Remove(key); TerminalUsingRepeatEligibleKeys.Remove(key); TerminalExitedRepeatEligibleKeys.Remove(key); TerminalEverUsedKeys.Remove(key); continue; } bool value2; bool flag2 = TerminalUseStates.TryGetValue(key, out value2) && value2; if (flag2 && TerminalUsingRepeatEligibleKeys.Contains(key)) { string text2 = "OnTerminalUsing"; string triggerMode = Runtime.NormalizeInteractionTriggerMode(text2); if (TryGetMinimumInteractRepeatCooldown("terminal", triggerMode, out var cooldown)) { string text3 = key + "::repeat::" + text2; if (IsRepeatDue(TerminalRepeatNextDueTimes, text3, cooldown)) { Runtime.FireInteractTrigger("Terminal", text2, value, null, "TerminalRepeat", text3); } } } else { if (flag2 || !TerminalExitedRepeatEligibleKeys.Contains(key)) { continue; } string text4 = "OnTerminalExited"; string triggerMode2 = Runtime.NormalizeInteractionTriggerMode(text4); if (TryGetMinimumInteractRepeatCooldown("terminal", triggerMode2, out var cooldown2)) { string text5 = key + "::repeat::" + text4; if (IsRepeatDue(TerminalRepeatNextDueTimes, text5, cooldown2)) { Runtime.FireInteractTrigger("Terminal", text4, value, null, "TerminalRepeat", text5); } } } } } internal static void ProcessBigPickupRepeatEvents() { if (!GameStateManager.IsInExpedition) { return; } bool flag = false; foreach (var activeInteractTrigger in Runtime.GetActiveInteractTriggers()) { InteractTriggerRule item = activeInteractTrigger.Trigger; if (!(Runtime.NormalizeTargetType(item.TargetType) != "bigpickup")) { string text = Runtime.NormalizeInteractionTriggerMode(item.TriggerMode); if (text == "onbigpickupheld" || text == "onbigpickupplaced") { flag = true; break; } } } if (!flag || BigPickupRepeatObjects.Count == 0) { return; } KeyValuePair[] array = BigPickupRepeatObjects.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; string key = keyValuePair.Key; CarryItemPickup_Core value = keyValuePair.Value; if ((Object)(object)value == (Object)null) { BigPickupRepeatObjects.Remove(key); BigPickupRepeatEligibleKeys.Remove(key); BigPickupHeldStates.Remove(key); } else { if (!BigPickupRepeatEligibleKeys.Contains(key) || !BigPickupHeldStates.TryGetValue(key, out var value2)) { continue; } string text2 = (value2 ? "OnBigPickupHeld" : "OnBigPickupPlaced"); string triggerMode = Runtime.NormalizeInteractionTriggerMode(text2); if (TryGetMinimumInteractRepeatCooldown("bigpickup", triggerMode, out var cooldown)) { string text3 = key + "::repeat::" + text2; if (IsRepeatDue(BigPickupRepeatNextDueTimes, text3, cooldown)) { Runtime.FireInteractTrigger("BigPickup", text2, (Component?)(object)value, null, "BigPickupRepeat", text3); } } } } } internal static void OnBigPickupStateChanged(CarryItemPickup_Core? item, ePickupItemStatus status, PlayerAgent? player, string source, bool isRecall = false) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)item == (Object)null || isRecall) { return; } bool flag = (int)status == 1; if (TryAcceptBigPickupTransition(item, flag, source)) { string text = BuildStableComponentKey("bigpickup", (Component)(object)item); BigPickupRepeatEligibleKeys.Add(text); BigPickupRepeatObjects[text] = item; string text2 = (flag ? "OnBigPickupPickup" : "OnBigPickupDrop"); int firedDispatchCount = FiredDispatchCount; int runtimeIndex = GetRuntimeIndex("BigPickup", (Component?)(object)item); int index = -1; string source2 = "Missing:ScanPosOverride.BigPickupItemIndex"; SpoIndexResolver.TryGetBigPickupSpoIndex((Component?)(object)item, out index, out source2); Runtime.LogVerbose($"Detected BigPickup state change. Event={text2}, Source={source}, SPOIndex={index}, CTERuntimeIndex={runtimeIndex}, SPOSource='{source2}', Name='{SafeObjectName((Component)(object)item)}'"); Fire("BigPickup", text2, (Component)(object)item, player, source, useStableStateKey: true); if (FiredDispatchCount == firedDispatchCount) { Runtime.LogVerbose($"BigPickup state changed but no InteractTrigger matched. SPOIndex={index}, CTERuntimeIndex={runtimeIndex}, Serial={SafeIntMember(item, "m_serialNumber", "SerialNumber")}, ItemKey='{SafeStringMember(item, "m_itemKey")}', PublicName='{SafeStringMember(item, "PublicName")}', Source={source}"); } HandleBigPickupPickupDropCycle(item, player, source, flag); } } private static void HandleBigPickupPickupDropCycle(CarryItemPickup_Core item, PlayerAgent? player, string source, bool nowHeld) { string key = BuildStableComponentKey("bigpickup", (Component)(object)item); if (!BigPickupCycleStates.TryGetValue(key, out BigPickupCycleState value)) { value = new BigPickupCycleState(); BigPickupCycleStates[key] = value; } if (nowHeld) { value.WaitingForDrop = true; } else if (value.WaitingForDrop) { value.WaitingForDrop = false; value.CompletedCycles++; int index = -1; SpoIndexResolver.TryGetBigPickupSpoIndex((Component?)(object)item, out index, out string _); Runtime.LogVerbose($"BigPickup pickup/drop behavior cycle completed. SPOIndex={index}, CTERuntimeIndex={GetRuntimeIndex("BigPickup", (Component?)(object)item)}, Cycles={value.CompletedCycles}, Source={source}"); Runtime.FireBigPickupCycleEvents((Component)(object)item, player, source, value.CompletedCycles); } } private static bool TryAcceptBigPickupTransition(CarryItemPickup_Core item, bool nowHeld, string source) { string text = BuildStableComponentKey("bigpickup", (Component)(object)item); float realtimeSinceStartup = Time.realtimeSinceStartup; if (!BigPickupHeldStates.ContainsKey(text)) { BigPickupHeldStates[text] = nowHeld; return false; } if (BigPickupHeldStates.TryGetValue(text, out var value)) { if (value == nowHeld) { return false; } } else if (!nowHeld) { BigPickupHeldStates[text] = false; return false; } string key = text + ":" + (nowHeld ? "pickup" : "drop"); if (LastBigPickupTransitionTimes.TryGetValue(key, out var value2) && realtimeSinceStartup - value2 < 0.2f) { return false; } BigPickupHeldStates[text] = nowHeld; LastBigPickupTransitionTimes[key] = realtimeSinceStartup; return true; } internal static void OnTerminalUse(Component? terminal, PlayerAgent? player, string source) { if (!((Object)(object)terminal == (Object)null) && TryAcceptTerminalTransition(terminal, player, entering: true, source, out Component canonicalTerminal)) { EnqueueTerminalEvent(canonicalTerminal, player, "OnTerminalUse", source); } } internal static void OnTerminalUnused(Component? terminal, PlayerAgent? player, string source) { if (!((Object)(object)terminal == (Object)null) && TryAcceptTerminalTransition(terminal, player, entering: false, source, out Component canonicalTerminal)) { EnqueueTerminalEvent(canonicalTerminal, player, "OnTerminalUnused", source); } } private static void EnqueueTerminalEvent(Component terminal, PlayerAgent? player, string eventName, string source) { string text = BuildStableComponentKey("terminal", terminal) + ":" + eventName; if (!PendingTerminalEventKeys.Contains(text)) { PendingTerminalEventKeys.Add(text); PendingTerminalEvents.Enqueue(new PendingTerminalEvent { Terminal = terminal, Player = player, EventName = eventName, Source = source, DueTime = Time.realtimeSinceStartup + 0.1f, Key = text }); Runtime.LogVerbose($"Queued terminal event {eventName} from {source} for {BuildStableComponentKey("terminal", terminal)}"); } } internal static void ProcessPendingTerminalEvents() { if (PendingTerminalEvents.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; int num = 0; while (PendingTerminalEvents.Count > 0 && num < 1) { PendingTerminalEvent pendingTerminalEvent = PendingTerminalEvents.Peek(); if (!(pendingTerminalEvent.DueTime > realtimeSinceStartup)) { PendingTerminalEvents.Dequeue(); PendingTerminalEventKeys.Remove(pendingTerminalEvent.Key); Fire("Terminal", pendingTerminalEvent.EventName, pendingTerminalEvent.Terminal, pendingTerminalEvent.Player, pendingTerminalEvent.Source, useStableStateKey: true); num++; continue; } break; } } private static bool TryAcceptTerminalTransition(Component source, PlayerAgent? player, bool entering, string sourceLabel, out Component canonicalTerminal) { canonicalTerminal = ResolveCanonicalTerminal(source) ?? source; string text = BuildStableComponentKey("terminal", canonicalTerminal); string text2 = text; TerminalRepeatObjects[text2] = canonicalTerminal; float realtimeSinceStartup = Time.realtimeSinceStartup; if (TerminalUseStates.TryGetValue(text2, out var value)) { if (entering && value) { Runtime.LogVerbose("Ignored duplicate terminal use transition from " + sourceLabel + " for " + text); return false; } if (!entering && !value) { Runtime.LogVerbose("Ignored duplicate terminal unused transition from " + sourceLabel + " for " + text); return false; } } else if (!entering) { TerminalUseStates[text2] = false; return false; } string key = text2 + ":" + (entering ? "use" : "unused"); if (LastTerminalTransitionTimes.TryGetValue(key, out var value2) && realtimeSinceStartup - value2 < 0.35f) { return false; } TerminalUseStates[text2] = entering; LastTerminalTransitionTimes[key] = realtimeSinceStartup; if (entering) { TerminalEverUsedKeys.Add(text2); TerminalUsingRepeatEligibleKeys.Add(text2); TerminalExitedRepeatEligibleKeys.Remove(text2); } else if (TerminalEverUsedKeys.Contains(text2)) { TerminalUsingRepeatEligibleKeys.Remove(text2); TerminalExitedRepeatEligibleKeys.Add(text2); } return true; } private static Component? ResolveCanonicalTerminal(Component source) { try { LG_ComputerTerminal val = (LG_ComputerTerminal)(object)((source is LG_ComputerTerminal) ? source : null); if (val != null) { return (Component?)(object)val; } } catch { } try { Interact_ComputerTerminal val2 = (Interact_ComputerTerminal)(object)((source is Interact_ComputerTerminal) ? source : null); if (val2 != null && (Object)(object)val2.m_terminal != (Object)null) { return (Component?)(object)val2.m_terminal; } } catch { } try { object? obj3 = TryGetObjectMember(source, "m_terminal", "Terminal", "terminal"); LG_ComputerTerminal val3 = (LG_ComputerTerminal)((obj3 is LG_ComputerTerminal) ? obj3 : null); if (val3 != null) { return (Component?)(object)val3; } } catch { } try { LG_ComputerTerminal componentInParent = source.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { return (Component?)(object)componentInParent; } } catch { } try { LG_ComputerTerminal componentInChildren = source.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { return (Component?)(object)componentInChildren; } } catch { } return null; } private static object? TryGetObjectMember(object obj, params string[] names) { foreach (string name in names) { try { object obj2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2; } obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); if (obj2 != null) { return obj2; } } catch { } } return null; } private static string BuildStableComponentKey(string targetType, Component source) { try { return targetType + ":" + ((Object)source).GetInstanceID(); } catch { return targetType + ":" + ((Object)source).GetHashCode(); } } private static void Fire(string targetType, string eventName, Component source, PlayerAgent? player, string sourceLabel, bool useStableStateKey = false) { try { string text = BuildStableComponentKey(targetType, source); string text2 = text + ":" + eventName + ":" + (((Object)(object)player != (Object)null) ? ((Object)player).GetInstanceID() : (-1)); if (!LastRawEventTimes.TryGetValue(text2, out var value) || !(Time.realtimeSinceStartup - value < 0.15f)) { LastRawEventTimes[text2] = Time.realtimeSinceStartup; int runtimeIndex = GetRuntimeIndex(targetType, source); string sourceName = BuildSourceName(source, sourceLabel) + $" Index={runtimeIndex}"; string stateKeySuffix = (useStableStateKey ? text2 : (text2 + ":" + Mathf.FloorToInt(Time.realtimeSinceStartup * 2f))); Runtime.FireInteractTrigger(targetType, eventName, source, player, sourceName, stateKeySuffix); } } catch (Exception ex) { Runtime.LogThrottled($"Interact trigger dispatch failed from {sourceLabel}: {ex.GetType().Name}: {ex.Message}"); } } private static string BuildSourceName(Component source, string sourceLabel) { try { string value = (((Object)(object)source.gameObject != (Object)null) ? ((Object)source.gameObject).name : ((Object)source).ToString()); int instanceID = ((Object)source).GetInstanceID(); return $"{sourceLabel}:{value}#{instanceID}"; } catch { return sourceLabel; } } } [HarmonyPatch(typeof(CarryItemPickup_Core), "OnSyncStateChange")] internal static class CarryItemPickup_Core_OnSyncStateChange_CTEPatch { private static void Postfix(CarryItemPickup_Core __instance, ePickupItemStatus status, pPickupPlacement placement, PlayerAgent player, bool isRecall) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) InteractTriggerManager.OnBigPickupStateChanged(__instance, status, player, "CarryItemPickup_Core.OnSyncStateChange", isRecall); } } [HarmonyPatch(typeof(CarryItemPickup_Core), "OnInteractionPickUp")] internal static class CarryItemPickup_Core_OnInteractionPickUp_CTEPatch { private static void Postfix(CarryItemPickup_Core __instance, PlayerAgent player) { InteractTriggerManager.OnBigPickupStateChanged(__instance, (ePickupItemStatus)1, player, "CarryItemPickup_Core.OnInteractionPickUp"); } } [HarmonyPatch(typeof(CarryItemPickup_Core), "OnPickedUp")] internal static class CarryItemPickup_Core_OnPickedUp_CTEPatch { private static void Postfix(CarryItemPickup_Core __instance, PlayerAgent player, InventorySlot slot, AmmoType ammoType) { InteractTriggerManager.OnBigPickupStateChanged(__instance, (ePickupItemStatus)1, player, "CarryItemPickup_Core.OnPickedUp"); } } [HarmonyPatch(typeof(CarryItemPickup_Core), "OnPickUp")] internal static class CarryItemPickup_Core_OnPickUp_CTEPatch { private static void Postfix(CarryItemPickup_Core __instance, PlayerAgent player) { InteractTriggerManager.OnBigPickupStateChanged(__instance, (ePickupItemStatus)1, player, "CarryItemPickup_Core.OnPickUp"); } } [HarmonyPatch(typeof(CarryItemPickup_Core), "OnPlacedInLevel")] internal static class CarryItemPickup_Core_OnPlacedInLevel_CTEPatch { private static void Postfix(CarryItemPickup_Core __instance, pPickupPlacement placement, PlayerAgent player, ItemCuller culler) { InteractTriggerManager.OnBigPickupStateChanged(__instance, (ePickupItemStatus)0, player, "CarryItemPickup_Core.OnPlacedInLevel"); } } [HarmonyPatch(typeof(Interact_ComputerTerminal), "TriggerInteractionAction")] internal static class Interact_ComputerTerminal_TriggerInteractionAction_CTEPatch { private static void Postfix(Interact_ComputerTerminal __instance, PlayerAgent source) { } } [HarmonyPatch(typeof(Interact_Terminal), "OnSelectedChange")] internal static class Interact_Terminal_OnSelectedChange_CTEPatch { private static void Postfix(Interact_Terminal __instance, bool selected, PlayerAgent agent, bool forceUpdate) { } } [HarmonyPatch(typeof(LG_ComputerTerminal), "OnInteract")] internal static class LG_ComputerTerminal_OnInteract_CTEPatch { private static void Postfix(LG_ComputerTerminal __instance, PlayerAgent interactionSource, bool __result) { } } [HarmonyPatch(typeof(LG_ComputerTerminal), "OnStateChange")] internal static class LG_ComputerTerminal_OnStateChange_CTEPatch { private static void Postfix(LG_ComputerTerminal __instance, pComputerTerminalState oldState, pComputerTerminalState newState, bool isRecall) { if (!isRecall) { bool flag = false; try { flag = __instance.m_hasInteractingPlayer; } catch { } PlayerAgent player = TryGetTerminalPlayer(__instance); if (flag) { InteractTriggerManager.OnTerminalUse((Component?)(object)__instance, player, "LG_ComputerTerminal.OnStateChange:hasPlayer"); } else { InteractTriggerManager.OnTerminalUnused((Component?)(object)__instance, player, "LG_ComputerTerminal.OnStateChange:noPlayer"); } } } private static PlayerAgent? TryGetTerminalPlayer(LG_ComputerTerminal terminal) { try { PlayerAgent localInteractionSource = terminal.m_localInteractionSource; if ((Object)(object)localInteractionSource != (Object)null) { return localInteractionSource; } } catch { } try { PlayerAgent syncedInteractionSource = terminal.m_syncedInteractionSource; if ((Object)(object)syncedInteractionSource != (Object)null) { return syncedInteractionSource; } } catch { } return null; } } [HarmonyPatch(typeof(LG_ComputerTerminal), "EnterFPSView")] internal static class LG_ComputerTerminal_EnterFPSView_CTEPatch { private static void Postfix(LG_ComputerTerminal __instance) { InteractTriggerManager.OnTerminalUse((Component?)(object)__instance, TryGetTerminalPlayer(__instance), "LG_ComputerTerminal.EnterFPSView"); } private static PlayerAgent? TryGetTerminalPlayer(LG_ComputerTerminal terminal) { try { PlayerAgent localInteractionSource = terminal.m_localInteractionSource; if ((Object)(object)localInteractionSource != (Object)null) { return localInteractionSource; } } catch { } try { PlayerAgent syncedInteractionSource = terminal.m_syncedInteractionSource; if ((Object)(object)syncedInteractionSource != (Object)null) { return syncedInteractionSource; } } catch { } return null; } } [HarmonyPatch(typeof(LG_ComputerTerminal), "ExitFPSView")] internal static class LG_ComputerTerminal_ExitFPSView_CTEPatch { private static void Prefix(LG_ComputerTerminal __instance) { InteractTriggerManager.OnTerminalUnused((Component?)(object)__instance, TryGetTerminalPlayer(__instance), "LG_ComputerTerminal.ExitFPSView"); } private static PlayerAgent? TryGetTerminalPlayer(LG_ComputerTerminal terminal) { try { PlayerAgent localInteractionSource = terminal.m_localInteractionSource; if ((Object)(object)localInteractionSource != (Object)null) { return localInteractionSource; } } catch { } try { PlayerAgent syncedInteractionSource = terminal.m_syncedInteractionSource; if ((Object)(object)syncedInteractionSource != (Object)null) { return syncedInteractionSource; } } catch { } return null; } } [HarmonyPatch(typeof(LG_ComputerTerminal), "DoExitFPSView")] internal static class LG_ComputerTerminal_DoExitFPSView_CTEPatch { private static void Prefix(LG_ComputerTerminal __instance) { InteractTriggerManager.OnTerminalUnused((Component?)(object)__instance, TryGetTerminalPlayer(__instance), "LG_ComputerTerminal.DoExitFPSView"); } private static PlayerAgent? TryGetTerminalPlayer(LG_ComputerTerminal terminal) { try { PlayerAgent localInteractionSource = terminal.m_localInteractionSource; if ((Object)(object)localInteractionSource != (Object)null) { return localInteractionSource; } } catch { } try { PlayerAgent syncedInteractionSource = terminal.m_syncedInteractionSource; if ((Object)(object)syncedInteractionSource != (Object)null) { return syncedInteractionSource; } } catch { } return null; } } internal static class DebugMarkerManager { private sealed class MarkerEntry { public GameObject Root; public Transform? LabelTransform; public bool Resolved; } private static readonly Dictionary Markers = new Dictionary(StringComparer.OrdinalIgnoreCase); private static float _lastRefreshTime; private static string _lastSignature = string.Empty; internal static void UpdateMarkers(List<(ConfigDocument Config, PositionTriggerRule Trigger)> activeTriggers) { //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown try { if (!activeTriggers.Any<(ConfigDocument, PositionTriggerRule)>(((ConfigDocument Config, PositionTriggerRule Trigger) t) => t.Config.Debug.Enabled && t.Config.Debug.ShowScanMarkers)) { Clear(); return; } float num = Math.Max(1f, (from t in activeTriggers where t.Config.Debug.Enabled select t.Config.Debug.RefreshInterval).DefaultIfEmpty(2f).Min()); string text = BuildSignature(activeTriggers); if (text == _lastSignature && Time.realtimeSinceStartup - _lastRefreshTime < num) { FaceLabelsToCamera(); return; } _lastSignature = text; _lastRefreshTime = Time.realtimeSinceStartup; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var (configDocument, positionTriggerRule) in activeTriggers) { if (!configDocument.Debug.Enabled || !configDocument.Debug.ShowScanMarkers || !positionTriggerRule.DebugVisible) { continue; } string text2 = configDocument.FilePath + "::" + positionTriggerRule.ID; hashSet.Add(text2); if (Markers.TryGetValue(text2, out MarkerEntry value)) { if (!value.Resolved) { DestroyMarker(text2); MarkerEntry markerEntry = CreateMarker(configDocument, positionTriggerRule); if (markerEntry != null) { Markers[text2] = markerEntry; } } } else { MarkerEntry markerEntry2 = CreateMarker(configDocument, positionTriggerRule); if (markerEntry2 != null) { Markers[text2] = markerEntry2; } } } foreach (string item in Markers.Keys.ToList()) { if (!hashSet.Contains(item)) { DestroyMarker(item); } } FaceLabelsToCamera(); } catch (Exception ex) { ManualLogSource log = Runtime.Log; if (log != null) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(30, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Debug marker update failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); } } } internal static void Clear() { foreach (string item in Markers.Keys.ToList()) { DestroyMarker(item); } _lastSignature = string.Empty; } private static string BuildSignature(List<(ConfigDocument Config, PositionTriggerRule Trigger)> activeTriggers) { return string.Join("|", from t in activeTriggers where t.Config.Debug.Enabled && t.Config.Debug.ShowScanMarkers && t.Trigger.DebugVisible select $"{t.Config.FilePath}:{t.Trigger.ID}:{t.Trigger.TriggerAreaMode}:{t.Trigger.LocalIndex}:{t.Trigger.Count}:{FormatPositionSignature(t.Trigger.Position)}:{t.Trigger.Radius:F2}:{t.Config.Debug.MarkerColor}:{t.Trigger.DebugColor}:{t.Config.Debug.MarkerAlpha:F2}"); } private static string FormatPositionSignature(PositionData? position) { if (position != null) { return $"{position.x:F2},{position.y:F2},{position.z:F2}"; } return ""; } private static MarkerEntry? CreateMarker(ConfigDocument config, PositionTriggerRule trigger) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Expected O, but got Unknown //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) Bounds bounds; string source; bool flag = Runtime.TryGetPositionTriggerDebugBounds(trigger, out bounds, out source); if (!flag) { if (!Runtime.IsTransientLookupFailure(source)) { Runtime.LogThrottled($"Debug marker for trigger '{trigger.ID}' is waiting for real bounds: {source}. No hidden placeholder marker will be created."); } return null; } Vector3 center = ((Bounds)(ref bounds)).center; string text = (trigger.TriggerAreaMode ?? "Radius").Trim().ToLowerInvariant(); int num; switch (text) { default: num = ((text == "area") ? 1 : 0); break; case "overridebigzone": case "bigzone": case "zone": case "overridearea": num = 1; break; } bool flag2 = (byte)num != 0; float num2 = Math.Max(config.Debug.MinimumRadius, Math.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z) * config.Debug.RadiusScale); Color markerColor = ParseColor((!string.IsNullOrWhiteSpace(trigger.DebugColor)) ? trigger.DebugColor : (flag2 ? "#A020F0" : config.Debug.MarkerColor), new Color(0.63f, 0.12f, 0.94f, config.Debug.MarkerAlpha)); markerColor.a = config.Debug.MarkerAlpha; if (flag2 && (((Vector3)(ref center)).sqrMagnitude < 0.05f || Mathf.Abs(center.x) >= 9000f || Mathf.Abs(center.y) >= 9000f || Mathf.Abs(center.z) >= 9000f)) { Runtime.LogThrottled($"Debug marker for trigger '{trigger.ID}' skipped: resolved {text} center is invalid ({center.x:F2},{center.y:F2},{center.z:F2})."); return null; } GameObject val = new GameObject("CTE_DebugScanMarker_" + SanitizeName(trigger.ID)); val.transform.position = center + Vector3.up * config.Debug.HeightOffset; MarkerEntry markerEntry = new MarkerEntry { Root = val, Resolved = flag }; if (!flag2) { CreateRadiusRing(val, num2, markerColor, config.Debug); } if (config.Debug.ShowNames || flag2) { GameObject val2 = new GameObject("Label"); val2.transform.SetParent(val.transform, false); val2.transform.localPosition = Vector3.up * config.Debug.LabelHeightOffset; TextMesh obj = val2.AddComponent(); obj.text = trigger.ID; obj.characterSize = (flag2 ? 0.35f : 0.25f); obj.anchor = (TextAnchor)4; obj.alignment = (TextAlignment)1; obj.color = ParseColor(config.Debug.LabelColor, Color.white); markerEntry.LabelTransform = val2.transform; } if (flag2) { Runtime.LogVerbose($"Created debug ID-only marker for trigger '{trigger.ID}' Mode={trigger.TriggerAreaMode} Source={source} Center={center} Size=({((Bounds)(ref bounds)).size.x:F1},{((Bounds)(ref bounds)).size.y:F1},{((Bounds)(ref bounds)).size.z:F1})"); } else { Runtime.LogVerbose($"Created debug marker for trigger '{trigger.ID}' Mode={trigger.TriggerAreaMode} Source={source} Center={center} DisplayRadius={num2}"); } return markerEntry; } private static Material? CreateMarkerMaterial(Color markerColor) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown Shader val = Shader.Find("Sprites/Default"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Transparent"); } if ((Object)(object)val == (Object)null) { return null; } return new Material(val) { color = markerColor }; } private static void ConfigureLine(LineRenderer line, Color markerColor, DebugOptions debug, bool loop, int pointCount, float widthScale = 1f) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) line.useWorldSpace = false; line.loop = loop; line.positionCount = pointCount; line.widthMultiplier = Math.Max(0.02f, debug.MarkerHeight * widthScale); line.startColor = markerColor; line.endColor = markerColor; Material val = CreateMarkerMaterial(markerColor); if ((Object)(object)val != (Object)null) { ((Renderer)line).material = val; } } private static void CreateRadiusRing(GameObject root, float radius, Color markerColor, DebugOptions debug) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("RadiusRing"); val.transform.SetParent(root.transform, false); val.transform.localPosition = Vector3.zero; LineRenderer val2 = val.AddComponent(); ConfigureLine(val2, markerColor, debug, loop: true, 64); for (int i = 0; i < val2.positionCount; i++) { float num = (float)i / (float)val2.positionCount * (float)Math.PI * 2f; val2.SetPosition(i, new Vector3(Mathf.Cos(num) * radius, 0f, Mathf.Sin(num) * radius)); } } private static void CreateBoundsOutline(GameObject root, Bounds bounds, Color markerColor, DebugOptions debug) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("AreaBoundsOutline"); val.transform.SetParent(root.transform, false); val.transform.localPosition = Vector3.zero; LineRenderer obj = val.AddComponent(); ConfigureLine(obj, markerColor, debug, loop: true, 4, 1.5f); float num = ((Bounds)(ref bounds)).min.x - ((Bounds)(ref bounds)).center.x; float num2 = ((Bounds)(ref bounds)).max.x - ((Bounds)(ref bounds)).center.x; float num3 = ((Bounds)(ref bounds)).min.z - ((Bounds)(ref bounds)).center.z; float num4 = ((Bounds)(ref bounds)).max.z - ((Bounds)(ref bounds)).center.z; obj.SetPosition(0, new Vector3(num, 0f, num3)); obj.SetPosition(1, new Vector3(num2, 0f, num3)); obj.SetPosition(2, new Vector3(num2, 0f, num4)); obj.SetPosition(3, new Vector3(num, 0f, num4)); } private static void CreateBoundsDotGrid(GameObject root, Bounds bounds, Color markerColor, DebugOptions debug) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) float num = Math.Max(0.5f, ((Bounds)(ref bounds)).size.x); float num2 = Math.Max(0.5f, ((Bounds)(ref bounds)).size.z); float num3 = Mathf.Clamp(Mathf.Sqrt(Math.Max(1f, num * num2) / 72f), 4f, 18f); int num4 = Mathf.Clamp(Mathf.FloorToInt(num / num3) + 1, 2, 16); int num5 = Mathf.Clamp(Mathf.FloorToInt(num2 / num3) + 1, 2, 16); while (num4 * num5 > 96) { if (num4 >= num5 && num4 > 2) { num4--; continue; } if (num5 <= 2) { break; } num5--; } float num6 = ((Bounds)(ref bounds)).min.x - ((Bounds)(ref bounds)).center.x; float num7 = ((Bounds)(ref bounds)).max.x - ((Bounds)(ref bounds)).center.x; float num8 = ((Bounds)(ref bounds)).min.z - ((Bounds)(ref bounds)).center.z; float num9 = ((Bounds)(ref bounds)).max.z - ((Bounds)(ref bounds)).center.z; float num10 = Mathf.Clamp(Mathf.Min(num, num2) * 0.0125f, 0.25f, 0.85f); int num11 = 0; for (int i = 0; i < num4; i++) { float num12 = ((num4 <= 1) ? 0.5f : ((float)i / (float)(num4 - 1))); float num13 = Mathf.Lerp(num6, num7, num12); for (int j = 0; j < num5; j++) { float num14 = ((num5 <= 1) ? 0.5f : ((float)j / (float)(num5 - 1))); float num15 = Mathf.Lerp(num8, num9, num14); GameObject val = new GameObject($"PurpleDot_{num11++:00}"); val.transform.SetParent(root.transform, false); val.transform.localPosition = new Vector3(num13, 0.03f, num15); LineRenderer val2 = val.AddComponent(); ConfigureLine(val2, markerColor, debug, loop: true, 12, 2f); for (int k = 0; k < val2.positionCount; k++) { float num16 = (float)k / (float)val2.positionCount * (float)Math.PI * 2f; val2.SetPosition(k, new Vector3(Mathf.Cos(num16) * num10, 0f, Mathf.Sin(num16) * num10)); } } } } private static void DestroyMarker(string key) { if (Markers.TryGetValue(key, out MarkerEntry value) && (Object)(object)value.Root != (Object)null) { Object.Destroy((Object)(object)value.Root); } Markers.Remove(key); } private static void FaceLabelsToCamera() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } Quaternion rotation = ((Component)main).transform.rotation; foreach (MarkerEntry value in Markers.Values) { if ((Object)(object)value.LabelTransform != (Object)null) { value.LabelTransform.rotation = rotation; } } } private static Color ParseColor(string text, Color fallback) { //IL_0014: 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) Color result = default(Color); if (!string.IsNullOrWhiteSpace(text) && ColorUtility.TryParseHtmlString(text, ref result)) { return result; } return fallback; } private static string SanitizeName(string name) { if (string.IsNullOrWhiteSpace(name)) { return "Unnamed"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } } [HarmonyPatch(typeof(WorldEventManager), "Update")] internal static class WorldEventManager_Update_Patch { private static void Postfix() { Runtime.Tick(); } } }