using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using Enemies; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Localization; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("AlarmSpawnTeller")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.0")] [assembly: AssemblyInformationalVersion("0.5.0")] [assembly: AssemblyProduct("AlarmSpawnTeller")] [assembly: AssemblyTitle("AlarmSpawnTeller")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.5.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AlarmSpawnTeller { internal enum SpawnSource { UnknownDynamic, DoorAlarm, ErrorAlarm, ScoutScream, SurvivalWave, BloodDoor } internal enum SpawnSourceKind { None, Dynamic, BloodDoor } [BepInPlugin("logic.gtfo.alarmspawnteller", "Alarm Spawn Teller", "0.5.0")] public class Plugin : BasePlugin { private const string PluginGuid = "logic.gtfo.alarmspawnteller"; private const string PluginName = "Alarm Spawn Teller"; private const string PluginVersion = "0.5.0"; internal static ManualLogSource LogSource; public override void Load() { //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Expected O, but got Unknown LogSource = ((BasePlugin)this).Log; ((BasePlugin)this).Log.LogInfo((object)"[Alarm Spawn Teller] Installing patches..."); ConfigEntry debugSourceMode = ((BasePlugin)this).Config.Bind("Diagnostics", "DebugSourceMode", false, "Enables Blood Door spawn source investigation logs. This does not enable Blood Door classification."); ConfigEntry enableBloodDoorCandidateClassification = ((BasePlugin)this).Config.Bind("Diagnostics", "EnableBloodDoorCandidateClassification", false, "Enables the experimental Blood Door candidate rule for tracker routing."); ConfigEntry bloodDoorSettingsNameKeywords = ((BasePlugin)this).Config.Bind("Diagnostics", "BloodDoorSettingsNameKeywords", "MainframeRush_ModificationWave,Blood,Door", "Comma-separated settings.name keywords used by the experimental Blood Door candidate rule."); ConfigEntry enableSpawnDataBloodDoorCandidateClassification = ((BasePlugin)this).Config.Bind("Diagnostics", "EnableSpawnDataBloodDoorCandidateClassification", true, "Enables experimental pEnemyGroupSpawnData groupType routing to the Blood Door tracker."); ConfigEntry bloodDoorSpawnDataGroupTypes = ((BasePlugin)this).Config.Bind("Diagnostics", "BloodDoorSpawnDataGroupTypes", "Hunters", "Comma-separated pEnemyGroupSpawnData groupType values used by the experimental Blood Door candidate rule."); ConfigEntry bloodDoorSpawnDataSpawnTypes = ((BasePlugin)this).Config.Bind("Diagnostics", "BloodDoorSpawnDataSpawnTypes", "OnLine,RandomInArea", "Comma-separated pEnemyGroupSpawnData spawnType values allowed by the experimental Blood Door candidate rule."); ConfigEntry excludedBloodDoorSpawnDataSpawnTypes = ((BasePlugin)this).Config.Bind("Diagnostics", "ExcludedBloodDoorSpawnDataSpawnTypes", "Birther", "Comma-separated pEnemyGroupSpawnData spawnType values excluded from the experimental Blood Door candidate rule."); ConfigEntry enableEnemySetupProbe = ((BasePlugin)this).Config.Bind("Diagnostics", "EnableEnemySetupProbe", false, "Logs matching EnemyAgent.Setup decisions without changing spawn tracking."); ConfigEntry enemySetupProbeNameKeywords = ((BasePlugin)this).Config.Bind("Diagnostics", "EnemySetupProbeNameKeywords", "Tank,BossTank,Tank_Boss,Pouncer", "Comma-separated enemy name keywords used by the EnemyAgent.Setup diagnostic probe."); ConfigEntry enableSpecialEnemyFallbackTracking = ((BasePlugin)this).Config.Bind("Diagnostics", "EnableSpecialEnemyFallbackTracking", true, "Enables experimental Dynamic tracker fallback for configured special enemies."); ConfigEntry specialEnemyFallbackNameKeywords = ((BasePlugin)this).Config.Bind("Diagnostics", "SpecialEnemyFallbackNameKeywords", "Tank,BossTank,Tank_Boss,Pouncer", "Comma-separated enemy name keywords eligible for special enemy fallback tracking."); ConfigEntry specialEnemyFallbackTarget = ((BasePlugin)this).Config.Bind("Diagnostics", "SpecialEnemyFallbackTarget", "Dynamic", "Target tracker for special enemy fallback. Only Dynamic is currently supported."); ConfigEntry specialEnemyFallbackRequireAgressiveMode = ((BasePlugin)this).Config.Bind("Diagnostics", "SpecialEnemyFallbackRequireAgressiveMode", true, "Requires spawnData mode to be Agressive or Aggressive for special enemy fallback tracking."); ConfigEntry dynamicAutoHideEnabled = ((BasePlugin)this).Config.Bind("HUD", "DynamicAutoHideEnabled", true, "Removes Dynamic Spawns enemy-type rows after that type remains at zero alive."); ConfigEntry dynamicAutoHideAfterSeconds = ((BasePlugin)this).Config.Bind("HUD", "DynamicAutoHideAfterSeconds", 5f, "Seconds a Dynamic enemy type must remain at zero alive before that type row is removed. Minimum 1 second."); EnemyGroupSpawnPatch.ConfigureSourceInvestigation(debugSourceMode, enableBloodDoorCandidateClassification, bloodDoorSettingsNameKeywords, enableSpawnDataBloodDoorCandidateClassification, bloodDoorSpawnDataGroupTypes, bloodDoorSpawnDataSpawnTypes, excludedBloodDoorSpawnDataSpawnTypes, enableEnemySetupProbe, enemySetupProbeNameKeywords, enableSpecialEnemyFallbackTracking, specialEnemyFallbackNameKeywords, specialEnemyFallbackTarget, specialEnemyFallbackRequireAgressiveMode); EnemyGroupSpawnPatch.ConfigureDynamicAutoHide(dynamicAutoHideEnabled, dynamicAutoHideAfterSeconds); Localizer.LoadLocalization(); InstallTicker(); EnemyGroupSpawnPatch.Apply(new Harmony("logic.gtfo.alarmspawnteller")); ((BasePlugin)this).Log.LogInfo((object)"Alarm Spawn Teller loaded successfully."); } private void InstallTicker() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown bool flag = default(bool); try { ClassInjector.RegisterTypeInIl2Cpp(); } catch (Exception ex) { ManualLogSource log = ((BasePlugin)this).Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(70, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] AlarmSpawnTicker registration skipped or failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); } try { ((BasePlugin)this).AddComponent(); ((BasePlugin)this).Log.LogInfo((object)"[Alarm Spawn Teller] AlarmSpawnTicker added."); } catch (Exception ex2) { ManualLogSource log2 = ((BasePlugin)this).Log; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(53, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] Failed to add AlarmSpawnTicker: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2); } log2.LogError(val2); } } } public sealed class AlarmSpawnTicker : MonoBehaviour { private enum HudDisplayState { Idle, ActiveSession, FinalSummary } private const float LanguageRefreshIntervalSeconds = 3f; private const float HudBoxWidth = 320f; private const float HudRightMargin = 30f; private const float HudBoxGap = 8f; private static readonly List PendingScanTimes = new List(); private static bool HasLoggedUpdate; private static bool IsHudDisabled; private static string DynamicHudMessage; private static string BloodDoorHudMessage; private static float DynamicHudMessageEndTime; private static GUIStyle HudStyle; private static HudDisplayState HudState; private static float NextLanguageRefreshTime; public AlarmSpawnTicker(IntPtr pointer) : base(pointer) { } internal static void ScheduleScan(float delaySeconds) { PendingScanTimes.Add(Time.time + delaySeconds); Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Scheduled delayed scan."); } internal static bool HasPendingScans() { return PendingScanTimes.Count > 0; } internal static void ShowAlarmSpawnMessage(string message, float durationSeconds = 8f) { if (!IsHudDisabled) { DynamicHudMessage = message; DynamicHudMessageEndTime = Time.time + durationSeconds; HudState = HudDisplayState.FinalSummary; } } internal static void ShowActiveAlarmSpawnMessage(string message) { if (!IsHudDisabled) { DynamicHudMessage = message; HudState = HudDisplayState.ActiveSession; } } internal static void HideAlarmSpawnMessage() { DynamicHudMessage = null; DynamicHudMessageEndTime = 0f; HudState = HudDisplayState.Idle; } internal static void ShowBloodDoorSpawnMessage(string message) { if (!IsHudDisabled) { BloodDoorHudMessage = message; } } internal static void HideBloodDoorSpawnMessage() { BloodDoorHudMessage = null; } private void Update() { float time = Time.time; if (!HasLoggedUpdate) { HasLoggedUpdate = true; EnemyGroupSpawnPatch.MarkStartupTime(); if (EnemyGroupSpawnPatch.DebugModeEnabled) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] AlarmSpawnTicker.Update is running."); } } if (Localizer.IsAutoLanguage && time >= NextLanguageRefreshTime) { NextLanguageRefreshTime = time + 3f; Localizer.RefreshAutoLanguage(); } EnemyGroupSpawnPatch.ProcessCaptureWindowTimeout(); EnemyGroupSpawnPatch.ProcessPendingAgentRecordResolves(); EnemyGroupSpawnPatch.ProcessTrackedAlarmEnemyAliveStates(); EnemyGroupSpawnPatch.ProcessDynamicAutoHide(); EnemyGroupSpawnPatch.ProcessAlarmSessionTimeout(); if (PendingScanTimes.Count == 0) { return; } for (int num = PendingScanTimes.Count - 1; num >= 0; num--) { if (!(time < PendingScanTimes[num])) { PendingScanTimes.RemoveAt(num); if (EnemyGroupSpawnPatch.DebugModeEnabled) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Running delayed scan."); } EnemyGroupSpawnPatch.ScanForNewEnemies(); } } } private void OnGUI() { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown if (IsHudDisabled) { return; } if (HudState == HudDisplayState.FinalSummary && Time.time >= DynamicHudMessageEndTime) { HudState = HudDisplayState.Idle; DynamicHudMessage = null; } try { if (HudStyle == null) { HudStyle = CreateHudStyle(); } List list = new List(2); if (HudState != HudDisplayState.Idle && HasHudStatisticsContent(DynamicHudMessage)) { list.Add(DynamicHudMessage); } if (HasHudStatisticsContent(BloodDoorHudMessage)) { list.Add(BloodDoorHudMessage); } if (list.Count != 0) { float[] array = list.Select(GetHudBoxHeight).ToArray(); float num = array.Sum() + 8f * (float)(list.Count - 1); float x = Mathf.Max(0f, (float)Screen.width - 320f - 30f); float num2 = Mathf.Max(0f, ((float)Screen.height - num) * 0.5f); for (int i = 0; i < list.Count; i++) { DrawHudBox(list[i], x, num2, array[i]); num2 += array[i] + 8f; } } } catch (Exception ex) { IsHudDisabled = true; ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(61, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] HUD overlay disabled after OnGUI error: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } } private static float GetHudBoxHeight(string message) { int num = message.Count((char character) => character == '\n') + 1; return Math.Max(70f, 32f + (float)num * 24f); } private static bool HasHudStatisticsContent(string message) { if (string.IsNullOrWhiteSpace(message)) { return false; } return message.Split('\n', StringSplitOptions.RemoveEmptyEntries).Count((string line) => !string.IsNullOrWhiteSpace(line)) >= 2; } private static void DrawHudBox(string message, float x, float y, float height) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) GUI.Box(new Rect(x, y, 320f, height), message, HudStyle); } private static GUIStyle CreateHudStyle() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0038: 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_0049: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)0, fontSize = 20, wordWrap = true, padding = new RectOffset(14, 14, 10, 10) }; val.normal.textColor = Color.white; return val; } } internal static class Localizer { private sealed class LocalizationConfig { public string Language { get; set; } public Dictionary> HudTexts { get; set; } public Dictionary EnemyNames { get; set; } } private sealed class EnemyNameEntry { public string EnemyDataBlockID { get; set; } public string EnemyDataName { get; set; } public string[] Aliases { get; set; } public string Chinese { get; set; } public string English { get; set; } public string Japanese { get; set; } } private const string AutoLanguage = "Auto"; private const string EnglishFallbackLanguage = "English"; private const string LocalizationFileName = "AlarmSpawnTeller.localization.json"; private static LocalizationConfig Config; private static bool HasLoggedAutoLanguageWarning; internal static string ConfiguredLanguage { get; private set; } = "Auto"; internal static string ResolvedLanguage { get; private set; } = "English"; internal static bool IsAutoLanguage { get; private set; } = true; internal static void LoadLocalization() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown bool flag = default(bool); try { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? AppContext.BaseDirectory, "AlarmSpawnTeller.localization.json"); if (!File.Exists(text)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(94, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Localization JSON not found beside plugin DLL: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; using built-in fallback."); } logSource.LogWarning(val); } Config = CreateMinimalFallbackConfig(); ResolveConfiguredLanguage(); } else { Config = JsonSerializer.Deserialize(File.ReadAllText(text)) ?? CreateMinimalFallbackConfig(); EnsureDefaults(Config); ResolveConfiguredLanguage(); } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(80, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Failed to load localization JSON; using built-in fallback. "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } Config = CreateMinimalFallbackConfig(); ResolveConfiguredLanguage(); } } internal unsafe static void RefreshAutoLanguage() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!IsAutoLanguage) { return; } try { ITextLocalizationService textLocalizationService = Text.TextLocalizationService; if (textLocalizationService == null) { SetAutoLanguageFallback("GTFO text localization service is not initialized."); return; } Language currentLanguage = textLocalizationService.CurrentLanguage; string text = ((object)(*(Language*)(¤tLanguage))/*cast due to .constrained prefix*/).ToString(); if (string.IsNullOrWhiteSpace(text)) { SetAutoLanguageFallback("GTFO current language was empty."); return; } string text2 = MapGameLanguage(currentLanguage) ?? NormalizeLanguage(text); if (text2 == null) { SetAutoLanguageFallback("GTFO language '" + text + "' is not supported."); return; } UpdateResolvedLanguage(text2); HasLoggedAutoLanguageWarning = false; } catch (Exception ex) { SetAutoLanguageFallback("Failed to read GTFO current language: " + ex.Message); } } internal static string GetText(string key, params object[] args) { string text = ResolveLocalizedValue(Config?.HudTexts, key, key); if (args == null || args.Length == 0) { return text; } try { return string.Format(text, args); } catch { return text; } } internal static string GetEnemyDisplayName(string rawEnemyDataName) { return GetEnemyDisplayName(null, rawEnemyDataName); } internal static string GetEnemyDisplayName(string enemyDataID, string rawEnemyDataName) { string text = StripEnemyIdPrefix(rawEnemyDataName); EnemyNameEntry enemyNameEntry = FindEnemyNameByRawName(text); if (enemyNameEntry != null) { return ResolveEnemyName(enemyNameEntry, text); } string text2 = NormalizeEnemyDataID(enemyDataID); if (!string.IsNullOrWhiteSpace(text2) && Config?.EnemyNames != null) { foreach (KeyValuePair enemyName in Config.EnemyNames) { if (string.Equals(enemyName.Value?.EnemyDataBlockID, text2, StringComparison.OrdinalIgnoreCase)) { return ResolveEnemyName(enemyName.Value, text); } } } return CleanEnemyName(text); } private static string NormalizeEnemyDataID(string enemyDataID) { if (string.IsNullOrWhiteSpace(enemyDataID)) { return null; } string text = enemyDataID.Trim(); if (!uint.TryParse(text, out var result)) { return text; } return result.ToString(); } private static EnemyNameEntry FindEnemyNameByRawName(string rawName) { if (Config?.EnemyNames == null || string.IsNullOrWhiteSpace(rawName)) { return null; } if (Config.EnemyNames.TryGetValue(rawName, out var value)) { return value; } foreach (KeyValuePair enemyName in Config.EnemyNames) { EnemyNameEntry value2 = enemyName.Value; if (value2 != null) { if (string.Equals(value2.EnemyDataName, rawName, StringComparison.OrdinalIgnoreCase)) { return value2; } if (value2.Aliases != null && value2.Aliases.Any((string alias) => !string.IsNullOrWhiteSpace(alias) && string.Equals(alias, rawName, StringComparison.OrdinalIgnoreCase))) { return value2; } } } return null; } private static string ResolveEnemyName(EnemyNameEntry entry, string fallbackRawName) { if (entry == null) { return CleanEnemyName(fallbackRawName); } string resolvedLanguage = ResolvedLanguage; string text = ((resolvedLanguage == "Chinese") ? entry.Chinese : ((!(resolvedLanguage == "Japanese")) ? entry.English : entry.Japanese)); string text2 = text; if (!string.IsNullOrWhiteSpace(text2)) { return text2; } if (!string.IsNullOrWhiteSpace(entry.English)) { return entry.English; } return CleanEnemyName(entry.EnemyDataName ?? fallbackRawName); } internal static string GetSourceDisplayName(SpawnSource source) { return GetText($"Source_{source}"); } private static string ResolveLocalizedValue(Dictionary> table, string key, string fallback) { if (table == null || string.IsNullOrWhiteSpace(key) || !table.TryGetValue(key, out var value) || value == null) { return fallback; } if (value.TryGetValue(ResolvedLanguage, out var value2) && !string.IsNullOrWhiteSpace(value2)) { return value2; } if (value.TryGetValue("English", out value2) && !string.IsNullOrWhiteSpace(value2)) { return value2; } return fallback; } private static string StripEnemyIdPrefix(string rawEnemyDataName) { string text = rawEnemyDataName ?? string.Empty; int num = text.IndexOf(" / ", StringComparison.Ordinal); if (num >= 0 && num + 3 < text.Length) { text = text.Substring(num + 3); } return text; } private static string CleanEnemyName(string rawEnemyDataName) { string text = StripEnemyIdPrefix(rawEnemyDataName); return text switch { "Striker_Big_Wave" => "Big Striker", "Shooter_Wave" => "Shooter", "Striker_Wave" => "Striker", _ => text.Replace("_Wave", string.Empty), }; } private static void EnsureDefaults(LocalizationConfig config) { LocalizationConfig localizationConfig = CreateMinimalFallbackConfig(); config.Language = (string.IsNullOrWhiteSpace(config.Language) ? localizationConfig.Language : config.Language); LocalizationConfig localizationConfig2 = config; if (localizationConfig2.HudTexts == null) { Dictionary> dictionary = (localizationConfig2.HudTexts = new Dictionary>()); } localizationConfig2 = config; if (localizationConfig2.EnemyNames == null) { Dictionary dictionary3 = (localizationConfig2.EnemyNames = new Dictionary()); } MergeMissing(config.HudTexts, localizationConfig.HudTexts); MergeMissingEnemyNames(config.EnemyNames, localizationConfig.EnemyNames); } private static void MergeMissing(Dictionary> target, Dictionary> defaults) { foreach (KeyValuePair> @default in defaults) { if (!target.TryGetValue(@default.Key, out var value) || value == null) { target[@default.Key] = new Dictionary(@default.Value); continue; } foreach (KeyValuePair item in @default.Value) { value.TryAdd(item.Key, item.Value); } } } private static void MergeMissingEnemyNames(Dictionary target, Dictionary defaults) { foreach (KeyValuePair @default in defaults) { target.TryAdd(@default.Key, @default.Value); } } private static string SerializeConfig(LocalizationConfig config) { return JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true }); } private static LocalizationConfig CreateMinimalFallbackConfig() { return new LocalizationConfig { Language = "Auto", HudTexts = new Dictionary> { ["Source_SurvivalWave"] = Text("动态刷怪", "Dynamic spawns", "動的スポーン"), ["Source_UnknownDynamic"] = Text("其他动态刷怪", "Other dynamic spawns", "その他の動的スポーン"), ["Source_DoorAlarm"] = Text("开门警报", "Door alarm", "ドアアラーム"), ["Source_ErrorAlarm"] = Text("无限警报", "Error alarm", "エラーアラーム"), ["Source_ScoutScream"] = Text("哨兵叫怪", "Scout scream", "スカウト叫び"), ["SourceSummaryLine"] = Text("{0}存活:{1} / 已刷新:{2}", "{0} alive: {1} / spawned: {2}", "{0}生存:{1} / 出現:{2}"), ["EnemyCountLine"] = Text("{0}:{1} / {2}只", "{0}: {1} / {2}", "{0}:{1} / {2}体"), ["BloodDoorSpawns"] = Text("血门刷怪", "Blood Door Spawns", "Blood Door Spawns"), ["BloodDoorAliveLine"] = Text("存活:{0} / 已生成:{1}", "Alive: {0} / Spawned: {1}", "Alive: {0} / Spawned: {1}") }, EnemyNames = new Dictionary { ["Shooter_Wave"] = EnemyName("11", "Shooter_Wave", Array.Empty(), "射手", "Shooter", "Shooter"), ["Striker_Wave"] = EnemyName("13", "Striker_Wave", Array.Empty(), "近战怪", "Striker", "Striker"), ["Striker_Big_Wave"] = EnemyName("16", "Striker_Big_Wave", Array.Empty(), "大近战", "Big Striker", "Big Striker") } }; } private static void ResolveConfiguredLanguage() { ConfiguredLanguage = (string.IsNullOrWhiteSpace(Config?.Language) ? "Auto" : Config.Language.Trim()); IsAutoLanguage = string.Equals(ConfiguredLanguage, "Auto", StringComparison.OrdinalIgnoreCase); if (IsAutoLanguage) { ResolvedLanguage = "English"; RefreshAutoLanguage(); } else { ResolvedLanguage = NormalizeLanguage(ConfiguredLanguage) ?? "English"; } } private static string NormalizeLanguage(string language) { if (string.IsNullOrWhiteSpace(language)) { return null; } switch (language.Trim().Replace("_", string.Empty).Replace("-", string.Empty) .Replace(" ", string.Empty) .ToLowerInvariant()) { case "english": case "en": case "enus": return "English"; case "chinese": case "zh": case "zhtw": case "zhcn": case "chinesesimplified": case "simplifiedchinese": case "chinesetraditional": case "traditionalchinese": return "Chinese"; case "ja": case "jajp": case "japanese": return "Japanese"; default: return null; } } private static string MapGameLanguage(Language language) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown if ((int)language != 1) { return (language - 9) switch { 3 => "Chinese", 2 => "Chinese", 0 => "Japanese", _ => null, }; } return "English"; } private static void SetAutoLanguageFallback(string reason) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown UpdateResolvedLanguage("English"); if (HasLoggedAutoLanguageWarning) { return; } HasLoggedAutoLanguageWarning = true; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(46, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(reason); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" Falling back to English."); } logSource.LogWarning(val); } } private static void UpdateResolvedLanguage(string language) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (string.Equals(ResolvedLanguage, language, StringComparison.Ordinal)) { return; } ResolvedLanguage = language; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(56, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Localization language resolved to "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ResolvedLanguage); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } logSource.LogInfo(val); } } private static EnemyNameEntry EnemyName(string enemyDataBlockID, string enemyDataName, string[] aliases, string chinese, string english, string japanese) { return new EnemyNameEntry { EnemyDataBlockID = enemyDataBlockID, EnemyDataName = enemyDataName, Aliases = (aliases ?? Array.Empty()), Chinese = chinese, English = english, Japanese = japanese }; } private static Dictionary Text(string chinese, string english, string japanese) { return new Dictionary { ["Chinese"] = chinese, ["English"] = english, ["Japanese"] = japanese }; } } internal static class EnemyGroupSpawnPatch { private sealed class SpawnCaptureRecord { internal string GroupType { get; private init; } internal string SpawnType { get; private init; } internal string PersistentGameDataID { get; private init; } internal string PersistentWavePopDataID { get; private init; } internal string SurvivalWaveReplicatorKey { get; private init; } internal string PopulationScore { get; private init; } internal string DebugEnemyMode { get; private init; } internal bool IsLikelyHibernating { get { if (!string.Equals(GroupType, "Hibernating", StringComparison.OrdinalIgnoreCase)) { return string.Equals(DebugEnemyMode, "Hibernate", StringComparison.OrdinalIgnoreCase); } return true; } } internal static SpawnCaptureRecord FromSpawnData(object spawnData) { return new SpawnCaptureRecord { GroupType = ReadSpawnDataValue(spawnData, "groupType"), SpawnType = ReadSpawnDataValue(spawnData, "spawnType"), PersistentGameDataID = ReadSpawnDataValue(spawnData, "persistentGameDataID"), PersistentWavePopDataID = ReadSpawnDataValue(spawnData, "persistentWavePopDataID"), SurvivalWaveReplicatorKey = ReadSpawnDataValue(spawnData, "survivalWaveReplicatorKey"), PopulationScore = ReadSpawnDataValue(spawnData, "populationScore"), DebugEnemyMode = ReadSpawnDataValue(spawnData, "debugEnemyMode") }; } internal string ToSummary() { return $"groupType={GroupType}; spawnType={SpawnType}; persistentGameDataID={PersistentGameDataID}; persistentWavePopDataID={PersistentWavePopDataID}; survivalWaveReplicatorKey={SurvivalWaveReplicatorKey}; populationScore={PopulationScore}; debugEnemyMode={DebugEnemyMode}"; } private static string ReadSpawnDataValue(object spawnData, string memberName) { object obj = TryGetMemberValue(spawnData, memberName) ?? TryGetMemberValue(spawnData, "m_" + memberName); if (obj != null) { return FormatValueSummary(obj); } return ""; } } private sealed class AgentCaptureRecord { internal readonly object Instance; internal string AgentKey { get; private set; } internal string GroupKey { get; private set; } internal string EnemyData { get; private set; } internal string EnemyDataID { get; private set; } internal string EnemyDataName { get; private set; } internal string EnemyDataInternalName { get; private set; } internal string EnemyDataPersistentID { get; private set; } internal string InstanceName { get; private set; } internal string PersistentGameDataID { get; private set; } internal string PersistentID { get; private set; } internal string PopulationID { get; private set; } internal string EnemyID { get; private set; } internal string Type { get; private set; } internal string Name { get; private set; } internal bool NeedsDelayedResolve { get; private set; } internal float ResolveAfterTime { get; private set; } private AgentCaptureRecord(object instance, string agentKey) { Instance = instance; AgentKey = agentKey; } internal static AgentCaptureRecord FromSetup(object instance, object spawnData, string agentKey) { AgentCaptureRecord agentCaptureRecord = new AgentCaptureRecord(instance, agentKey); agentCaptureRecord.ReadSetupSpawnDataFallback(spawnData); agentCaptureRecord.ResolveFromInstance(); return agentCaptureRecord; } internal string ToSummary() { return $"agentKey={AgentKey}; groupKey={GroupKey}; enemyDataID={EnemyDataID}; enemyDataName={EnemyDataName}; enemyDataInternalName={EnemyDataInternalName}; enemyDataPersistentID={EnemyDataPersistentID}; instanceName={InstanceName}; spawnDataEnemyData={EnemyData}; persistentGameDataID={PersistentGameDataID}; persistentID={PersistentID}; populationID={PopulationID}; enemyID={EnemyID}; type={Type}; name={Name}"; } internal void ResolveFromInstance() { EnemyDataID = FirstKnown(ReadInstanceValue("EnemyDataID"), ReadInstanceValue("enemyDataID"), EnemyDataID); object obj = TryGetMemberValue(Instance, "EnemyData") ?? TryGetMemberValue(Instance, "enemyData") ?? TryGetMemberValue(Instance, "m_enemyData"); if (obj != null) { EnemyData = FirstKnown(FormatValueSummary(obj), EnemyData); EnemyDataName = FirstKnown(ReadObjectValue(obj, "name"), EnemyDataName); EnemyDataInternalName = FirstKnown(ReadObjectValue(obj, "internalName"), EnemyDataInternalName); EnemyDataPersistentID = FirstKnown(ReadObjectValue(obj, "persistentID"), EnemyDataPersistentID); } InstanceName = FirstKnown(TryGetUnityObjectName(Instance), ReadInstanceValue("name"), InstanceName); UpdateGroupKey(); NeedsDelayedResolve = IsUnknown(EnemyDataID) && IsUnknown(EnemyDataPersistentID) && IsUnknown(EnemyDataName) && IsUnknown(EnemyDataInternalName); if (NeedsDelayedResolve && ResolveAfterTime <= 0f) { ResolveAfterTime = Time.time + 0.2f; } } private void ReadSetupSpawnDataFallback(object spawnData) { EnemyData = ReadSetupDataValue(spawnData, "enemyData"); EnemyDataID = ReadSetupDataValue(spawnData, "enemyDataID"); PersistentGameDataID = ReadSetupDataValue(spawnData, "persistentGameDataID"); PersistentID = ReadSetupDataValue(spawnData, "persistentID"); PopulationID = ReadSetupDataValue(spawnData, "populationID"); EnemyID = ReadSetupDataValue(spawnData, "enemyID"); Type = ReadSetupDataValue(spawnData, "type"); Name = ReadSetupDataValue(spawnData, "name"); UpdateGroupKey(); } private void UpdateGroupKey() { string text = FirstKnown(EnemyDataID, EnemyDataPersistentID, PersistentGameDataID, PersistentID, EnemyID); string text2 = FirstKnown(EnemyDataName, EnemyDataInternalName, InstanceName, Name, Type); if (!IsUnknown(text) && !IsUnknown(text2)) { GroupKey = text + " / " + text2; return; } GroupKey = FirstKnown(text, text2, EnemyData, AgentKey, ""); } private string ReadInstanceValue(string memberName) { return ReadObjectValue(Instance, memberName); } private static string ReadObjectValue(object source, string memberName) { object obj = TryGetMemberValue(source, memberName) ?? TryGetMemberValue(source, "m_" + memberName); if (obj != null) { return FormatValueSummary(obj); } return ""; } private static string ReadSetupDataValue(object spawnData, string memberName) { if (spawnData == null) { return ""; } object obj = TryGetMemberValue(spawnData, memberName) ?? TryGetMemberValue(spawnData, "m_" + memberName); if (obj != null) { return FormatValueSummary(obj); } return ""; } internal static string FirstKnown(params string[] values) { foreach (string text in values) { if (!string.IsNullOrWhiteSpace(text) && text != "") { return text; } } return ""; } private static bool IsUnknown(string value) { if (!string.IsNullOrWhiteSpace(value) && !(value == "")) { return value == ""; } return true; } } private sealed class SourceDumpMember { internal string Name { get; } internal object Value { get; } internal SourceDumpMember(string name, object value) { Name = name; Value = value; } } private sealed class BloodDoorCandidateEvaluation { internal bool SurvivalWavePresent { get; set; } internal bool SettingsPresent { get; set; } internal string SettingsName { get; set; } = ""; internal string SettingsPersistentID { get; set; } = ""; internal string PopulationDataID { get; set; } = ""; internal string SpawnType { get; set; } = ""; internal string OverrideWaveSpawnType { get; set; } = ""; internal string WavePauseMin { get; set; } = ""; internal string WavePauseMax { get; set; } = ""; internal bool OverrideWaveSpawnTypeMatched { get; set; } internal bool SpawnTypeMatched { get; set; } internal bool WavePauseMinMatched { get; set; } internal bool WavePauseMaxMatched { get; set; } internal bool KeywordMatched { get; set; } internal bool CandidateMatched { get; set; } } private sealed class SpawnDataBloodDoorCandidateEvaluation { internal string GroupType { get; set; } = ""; internal string DebugEnemyMode { get; set; } = ""; internal string SpawnType { get; set; } = ""; internal string PersistentGameDataID { get; set; } = ""; internal string PersistentWavePopDataID { get; set; } = ""; internal string SurvivalWaveReplicatorKey { get; set; } = ""; internal string CourseNodeIDPlusOne { get; set; } = ""; internal string AllowedSpawnTypes { get; set; } = ""; internal string ExcludedSpawnTypes { get; set; } = ""; internal bool GroupTypeMatched { get; set; } internal bool SpawnTypeMatched { get; set; } internal bool SpawnTypeExcluded { get; set; } internal bool CandidateMatched { get; set; } } private sealed class SpawnDataRouteEvaluation { internal SpawnDataBloodDoorCandidateEvaluation Candidate { get; init; } internal SpawnSourceKind CandidateSourceKind { get; init; } internal SpawnSourceKind InheritedSessionSourceKind { get; init; } internal SpawnSourceKind FinalSourceKind { get; init; } internal bool ForcedNotBloodDoor { get; init; } internal bool RouteExclusive { get; init; } internal bool SkippedDynamicBecauseBloodDoorCandidate { get; init; } internal string CandidateReason { get; init; } internal string TargetTracker { get; init; } } private sealed class SpawnPatchState { internal SpawnDataRouteEvaluation SpawnDataRoute { get; set; } } private sealed class SpecialEnemyFallbackEvaluation { internal string SpawnDataMode { get; init; } internal string GroupReplicatorKey { get; init; } internal bool KeywordMatched { get; init; } internal bool AggressiveModeMatched { get; init; } internal bool FallbackEnabled { get; init; } internal bool FallbackMatched { get; init; } internal string TargetTracker { get; init; } internal string Decision { get; init; } internal bool ShouldLog { get; init; } } private sealed class SpawnTracker { private readonly Dictionary TrackedEnemies = new Dictionary(); private readonly Dictionary TrackedTypeEntries = new Dictionary(StringComparer.Ordinal); internal string Name { get; } internal int TotalSpawned => TrackedEnemies.Count; internal int AliveCount => TrackedEnemies.Values.Count((TrackedAlarmEnemy enemy) => enemy.IsAlive); internal IEnumerable Enemies => TrackedEnemies.Values; internal IEnumerable TypeEntries => TrackedTypeEntries.Values; internal int TypeEntryCount => TrackedTypeEntries.Count; internal SpawnTracker(string name) { Name = name; } internal bool TrackEnemy(AgentCaptureRecord record, SpawnSource spawnSource) { if (record == null || string.IsNullOrWhiteSpace(record.AgentKey) || TrackedEnemies.ContainsKey(record.AgentKey)) { return false; } TrackedAlarmEnemy trackedAlarmEnemy = TrackedAlarmEnemy.FromCaptureRecord(record, spawnSource); TrackedEnemies[record.AgentKey] = trackedAlarmEnemy; AddTrackedEnemyToTypeEntry(trackedAlarmEnemy, Time.time); return true; } internal bool TryMarkDead(string uniqueKey) { if (string.IsNullOrWhiteSpace(uniqueKey) || !TrackedEnemies.TryGetValue(uniqueKey, out var value) || !value.IsAlive) { return false; } value.IsAlive = false; if (TrackedTypeEntries.TryGetValue(value.TypeKey, out var value2)) { int alive = value2.Alive; value2.Alive = Math.Max(0, value2.Alive - 1); if (alive > 0 && value2.Alive == 0 && !value2.ZeroAliveTimeValid) { value2.ZeroAliveTime = Time.time; value2.ZeroAliveTimeValid = true; } } return true; } internal bool TryMarkAlive(string uniqueKey) { if (string.IsNullOrWhiteSpace(uniqueKey) || !TrackedEnemies.TryGetValue(uniqueKey, out var value) || value.IsAlive) { return false; } value.IsAlive = true; if (TrackedTypeEntries.TryGetValue(value.TypeKey, out var value2)) { value2.Alive++; value2.ZeroAliveTime = 0f; value2.ZeroAliveTimeValid = false; } return true; } internal void UpdateEnemy(AgentCaptureRecord record) { if (record != null && TrackedEnemies.TryGetValue(record.AgentKey, out var value)) { string typeKey = value.TypeKey; bool isAlive = value.IsAlive; value.UpdateFromCaptureRecord(record); SpawnTypeEntry value2; if (!string.Equals(typeKey, value.TypeKey, StringComparison.Ordinal)) { RemoveTrackedEnemyFromTypeEntry(typeKey, isAlive); AddTrackedEnemyToTypeEntry(value, Time.time); } else if (TrackedTypeEntries.TryGetValue(value.TypeKey, out value2)) { value2.DisplayName = value.DisplayName; } } } internal IEnumerable GetPrunableTypeEntries(float now, float timeoutSeconds) { return TrackedTypeEntries.Values.Where((SpawnTypeEntry entry) => entry.Total > 0 && entry.Alive == 0 && entry.ZeroAliveTimeValid && now - entry.ZeroAliveTime >= timeoutSeconds); } internal string[] PruneTypeEntry(string typeKey) { if (string.IsNullOrWhiteSpace(typeKey) || !TrackedTypeEntries.TryGetValue(typeKey, out var value) || value.Total <= 0 || value.Alive != 0 || !value.ZeroAliveTimeValid) { return Array.Empty(); } string[] array = (from enemy in TrackedEnemies.Values where !enemy.IsAlive && string.Equals(enemy.TypeKey, typeKey, StringComparison.Ordinal) select enemy.UniqueKey).ToArray(); string[] array2 = array; foreach (string key in array2) { TrackedEnemies.Remove(key); } TrackedTypeEntries.Remove(typeKey); return array; } internal void Clear() { TrackedEnemies.Clear(); TrackedTypeEntries.Clear(); } private void AddTrackedEnemyToTypeEntry(TrackedAlarmEnemy tracked, float now) { if (!TrackedTypeEntries.TryGetValue(tracked.TypeKey, out var value)) { value = new SpawnTypeEntry { TypeKey = tracked.TypeKey, DisplayName = tracked.DisplayName }; TrackedTypeEntries[tracked.TypeKey] = value; } value.DisplayName = tracked.DisplayName; value.Total++; if (tracked.IsAlive) { value.Alive++; } value.LastAddedTime = now; value.ZeroAliveTime = 0f; value.ZeroAliveTimeValid = false; } private void RemoveTrackedEnemyFromTypeEntry(string typeKey, bool wasAlive) { if (TrackedTypeEntries.TryGetValue(typeKey, out var value)) { value.Total = Math.Max(0, value.Total - 1); if (wasAlive) { value.Alive = Math.Max(0, value.Alive - 1); } if (value.Total == 0) { TrackedTypeEntries.Remove(typeKey); } else if (value.Alive == 0 && !value.ZeroAliveTimeValid) { value.ZeroAliveTime = Time.time; value.ZeroAliveTimeValid = true; } } } } private sealed class SpawnTypeEntry { internal string TypeKey { get; set; } internal string DisplayName { get; set; } internal int Total { get; set; } internal int Alive { get; set; } internal float LastAddedTime { get; set; } internal float ZeroAliveTime { get; set; } internal bool ZeroAliveTimeValid { get; set; } } private sealed class TrackedAlarmEnemy { internal string UniqueKey { get; private set; } internal object Agent { get; private set; } internal string EnemyDataID { get; private set; } internal string EnemyDataName { get; private set; } internal string DisplayName { get; private set; } internal string TypeKey { get; private set; } internal SpawnSource SpawnSource { get; private set; } internal bool IsAlive { get; set; } internal static TrackedAlarmEnemy FromCaptureRecord(AgentCaptureRecord record, SpawnSource spawnSource) { TrackedAlarmEnemy trackedAlarmEnemy = new TrackedAlarmEnemy(); trackedAlarmEnemy.UniqueKey = record.AgentKey; trackedAlarmEnemy.Agent = record.Instance; trackedAlarmEnemy.SpawnSource = spawnSource; trackedAlarmEnemy.IsAlive = true; trackedAlarmEnemy.UpdateFromCaptureRecord(record); return trackedAlarmEnemy; } internal void UpdateFromCaptureRecord(AgentCaptureRecord record) { if (record != null) { Agent = record.Instance ?? Agent; EnemyDataID = AgentCaptureRecord.FirstKnown(record.EnemyDataID, record.EnemyDataPersistentID, record.PersistentGameDataID, EnemyDataID); EnemyDataName = AgentCaptureRecord.FirstKnown(record.EnemyDataName, record.EnemyDataInternalName, record.InstanceName, record.Name, record.Type, EnemyDataName); TypeKey = BuildStableEnemyTypeKey(EnemyDataID, EnemyDataName, record.GroupKey, UniqueKey); DisplayName = FormatEnemyNameForHud(EnemyDataID, AgentCaptureRecord.FirstKnown(EnemyDataName, record.GroupKey, UniqueKey, "")); } } private static string BuildStableEnemyTypeKey(string enemyDataID, string enemyDataName, string groupKey, string uniqueKey) { string text = AgentCaptureRecord.FirstKnown(enemyDataID); string text2 = AgentCaptureRecord.FirstKnown(enemyDataName); if (text != "" || text2 != "") { return text + "|" + text2; } return AgentCaptureRecord.FirstKnown(groupKey, uniqueKey, ""); } } private static readonly bool DebugMode = false; private const int MaxSourceDumpMembers = 80; private const int MaxSpawnDataDumpLines = 80; private const int MaxNestedSpawnDataDumpMembers = 30; private static readonly string[] SourceTypeDiscoveryKeywords = new string[11] { "Blood", "Door", "Warden", "Alarm", "SurvivalWave", "Wave", "Population", "EnemyGroup", "Spawn", "Security", "ChainedPuzzle" }; private static readonly string[] SurvivalWaveDumpKeywords = new string[16] { "Wave", "Type", "Population", "Settings", "Data", "Persistent", "ID", "Name", "Event", "Alarm", "Door", "Blood", "Area", "Zone", "Course", "Spawn" }; private static readonly string[] CourseNodeDumpKeywords = new string[8] { "Dimension", "Layer", "Zone", "Area", "Node", "Course", "Local", "Position" }; private static readonly string[] SpawnDataDumpKeywords = new string[20] { "group", "type", "spawn", "enemy", "data", "id", "persistent", "node", "course", "area", "zone", "position", "rotation", "hibernate", "event", "wave", "blood", "door", "debug", "mode" }; private static readonly string[] NestedSpawnDataDumpKeywords = new string[10] { "EnemyGroup", "SpawnData", "CourseNode", "Area", "Zone", "GameData", "Blood", "Door", "Wave", "Event" }; private const bool EnableSpawnDiagnostics = true; private const int MaxSpawnDiagnosticsPerWindow = 20; private const float SpawnDiagnosticWindowSeconds = 10f; private const float ScanDelaySeconds = 0.2f; private const float CaptureWindowSeconds = 3f; private const float AlarmSessionQuietSeconds = 8f; private const float AgentDataResolveDelaySeconds = 0.2f; private const float AliveRefreshIntervalSeconds = 0.25f; private const float ClientFallbackWarmupSeconds = 20f; private static readonly HashSet KnownDynamicEnemyIDs = new HashSet(StringComparer.Ordinal) { "27", "28", "33", "39", "40", "43" }; private static readonly HashSet KnownEnemyIds = new HashSet(); private static readonly List ActiveCaptureRecords = new List(); private static readonly List ActiveAgentCaptureRecords = new List(); private static readonly SpawnTracker DynamicSpawnTracker = new SpawnTracker("Dynamic"); private static readonly SpawnTracker BloodDoorSpawnTracker = new SpawnTracker("BloodDoor"); private static readonly Dictionary EnemyTrackerByKey = new Dictionary(); private static readonly HashSet CapturedAgentKeys = new HashSet(); private static int SpawnDiagnosticsThisWindow; private static float SpawnDiagnosticWindowStart; private static bool IsCaptureWindowOpen; private static float CaptureEndTime; private static bool IsAlarmSessionOpen; private static bool IsSpawnDataBloodDoorRouteExclusive; private static float AlarmSessionEndTime; private static float NextAliveRefreshTime; private static float StartupTime = -1f; private static SpawnSource CurrentSessionSpawnSource = SpawnSource.UnknownDynamic; private static int NextFallbackAgentKey; private static Type EnemyAgentType; private static Type EnemyUpdateManagerType; private static MethodInfo FindObjectsOfTypeMethod; private static MethodInfo FindObjectsOfTypeAllMethod; private static MethodInfo GetInstanceIdMethod; private static PropertyInfo UnityObjectNameProperty; private static bool HasLoggedEnemyAgentShape; private static bool HasLoggedAliveProbeFailure; private static bool HasLoggedSpawnDiagnosticStack; private static bool HasRunSourceTypeDiscovery; private static int SourceInvestigationEventIndex; private static ConfigEntry DebugSourceModeConfig; private static ConfigEntry EnableBloodDoorCandidateClassificationConfig; private static ConfigEntry BloodDoorSettingsNameKeywordsConfig; private static ConfigEntry EnableSpawnDataBloodDoorCandidateClassificationConfig; private static ConfigEntry BloodDoorSpawnDataGroupTypesConfig; private static ConfigEntry BloodDoorSpawnDataSpawnTypesConfig; private static ConfigEntry ExcludedBloodDoorSpawnDataSpawnTypesConfig; private static ConfigEntry EnableEnemySetupProbeConfig; private static ConfigEntry EnemySetupProbeNameKeywordsConfig; private static ConfigEntry EnableSpecialEnemyFallbackTrackingConfig; private static ConfigEntry SpecialEnemyFallbackNameKeywordsConfig; private static ConfigEntry SpecialEnemyFallbackTargetConfig; private static ConfigEntry SpecialEnemyFallbackRequireAgressiveModeConfig; private static ConfigEntry DynamicAutoHideEnabledConfig; private static ConfigEntry DynamicAutoHideAfterSecondsConfig; internal static bool DebugModeEnabled => DebugMode; private static bool DebugSourceModeEnabled => DebugSourceModeConfig?.Value ?? false; private static bool BloodDoorCandidateClassificationEnabled => EnableBloodDoorCandidateClassificationConfig?.Value ?? false; private static bool SpawnDataBloodDoorCandidateClassificationEnabled => EnableSpawnDataBloodDoorCandidateClassificationConfig?.Value ?? false; private static bool EnemySetupProbeEnabled => EnableEnemySetupProbeConfig?.Value ?? false; private static bool SpecialEnemyFallbackTrackingEnabled => EnableSpecialEnemyFallbackTrackingConfig?.Value ?? false; private static bool DynamicAutoHideEnabled => DynamicAutoHideEnabledConfig?.Value ?? true; private static float DynamicAutoHideAfterSeconds => Math.Max(1f, DynamicAutoHideAfterSecondsConfig?.Value ?? 5f); internal static void ConfigureDynamicAutoHide(ConfigEntry dynamicAutoHideEnabled, ConfigEntry dynamicAutoHideAfterSeconds) { DynamicAutoHideEnabledConfig = dynamicAutoHideEnabled; DynamicAutoHideAfterSecondsConfig = dynamicAutoHideAfterSeconds; } internal static void ConfigureSourceInvestigation(ConfigEntry debugSourceMode, ConfigEntry enableBloodDoorCandidateClassification, ConfigEntry bloodDoorSettingsNameKeywords, ConfigEntry enableSpawnDataBloodDoorCandidateClassification, ConfigEntry bloodDoorSpawnDataGroupTypes, ConfigEntry bloodDoorSpawnDataSpawnTypes, ConfigEntry excludedBloodDoorSpawnDataSpawnTypes, ConfigEntry enableEnemySetupProbe, ConfigEntry enemySetupProbeNameKeywords, ConfigEntry enableSpecialEnemyFallbackTracking, ConfigEntry specialEnemyFallbackNameKeywords, ConfigEntry specialEnemyFallbackTarget, ConfigEntry specialEnemyFallbackRequireAgressiveMode) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown DebugSourceModeConfig = debugSourceMode; EnableBloodDoorCandidateClassificationConfig = enableBloodDoorCandidateClassification; BloodDoorSettingsNameKeywordsConfig = bloodDoorSettingsNameKeywords; EnableSpawnDataBloodDoorCandidateClassificationConfig = enableSpawnDataBloodDoorCandidateClassification; BloodDoorSpawnDataGroupTypesConfig = bloodDoorSpawnDataGroupTypes; BloodDoorSpawnDataSpawnTypesConfig = bloodDoorSpawnDataSpawnTypes; ExcludedBloodDoorSpawnDataSpawnTypesConfig = excludedBloodDoorSpawnDataSpawnTypes; EnableEnemySetupProbeConfig = enableEnemySetupProbe; EnemySetupProbeNameKeywordsConfig = enemySetupProbeNameKeywords; EnableSpecialEnemyFallbackTrackingConfig = enableSpecialEnemyFallbackTracking; SpecialEnemyFallbackNameKeywordsConfig = specialEnemyFallbackNameKeywords; SpecialEnemyFallbackTargetConfig = specialEnemyFallbackTarget; SpecialEnemyFallbackRequireAgressiveModeConfig = specialEnemyFallbackRequireAgressiveMode; if (DebugSourceModeEnabled) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(122, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SourceInvestigation] BloodDoor investigation build enabled. survivalWaveCandidateEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(BloodDoorCandidateClassificationEnabled); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnDataCandidateEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(SpawnDataBloodDoorCandidateClassificationEnabled); } logSource.LogInfo(val); } } internal static void Apply(Harmony harmony) { //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Expected O, but got Unknown //IL_02cd: Expected O, but got Unknown //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Installing Enemies.EnemyGroup.Spawn patch..."); } EnemyAgentType = typeof(EnemyAgent); EnemyUpdateManagerType = typeof(EnemyUpdateManager); RunSourceTypeDiscoveryOnce(); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val; if (EnemyAgentType == null) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find Enemies.EnemyAgent; spawn patch will still install, but enemy scan cannot run yet."); } else if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(44, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found EnemyAgent type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(EnemyAgentType.FullName); } logSource.LogInfo(val); } if (EnemyUpdateManagerType == null) { if (DebugMode) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find Enemies.EnemyUpdateManager; manager diagnostics unavailable."); } } else if (DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(52, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found EnemyUpdateManager type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(EnemyUpdateManagerType.FullName); } logSource2.LogInfo(val); } if (!InitializeUnityObjectAccess() && DebugMode) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Unity object reflection helpers unavailable; spawn patch will still install, but enemy scan/name fallback may be limited."); } Type typeFromHandle = typeof(EnemyGroup); if (typeFromHandle == null) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find Enemies.EnemyGroup."); return; } if (DebugMode) { ManualLogSource logSource3 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(44, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found EnemyGroup type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(typeFromHandle.FullName); } logSource3.LogInfo(val); } MethodInfo[] array = (from methodInfo4 in typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where methodInfo4.Name == "Spawn" select methodInfo4).ToArray(); if (array.Length == 0) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find any Enemies.EnemyGroup.Spawn overloads."); LogSpawnLikeMethods(typeFromHandle); return; } MethodInfo[] array2; if (DebugMode) { array2 = array; foreach (MethodInfo method in array2) { ManualLogSource logSource4 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(43, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found Spawn overload: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(method)); } logSource4.LogInfo(val); } } if (!array.Any(HasSurvivalWaveParameter)) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find Enemies.EnemyGroup.Spawn overload with a SurvivalWave parameter."); } MethodInfo methodInfo = AccessTools.Method(typeof(EnemyGroupSpawnPatch), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(EnemyGroupSpawnPatch), "Postfix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Plugin.LogSource.LogError((object)"[Alarm Spawn Teller] Could not find local prefix/postfix methods for Harmony patch."); return; } int num2 = 0; array2 = array; foreach (MethodInfo methodInfo3 in array2) { try { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo), new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num2++; if (DebugMode) { ManualLogSource logSource5 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(72, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Patched Spawn overload for diagnostics/statistics: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(methodInfo3)); } logSource5.LogInfo(val); } } catch (Exception ex) { ManualLogSource logSource6 = Plugin.LogSource; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(54, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] Failed to patch Spawn overload "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(FormatMethodSignature(methodInfo3)); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex); } logSource6.LogError(val2); } } ManualLogSource logSource7 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(57, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Patched "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" EnemyGroup.Spawn overloads."); } logSource7.LogInfo(val); PatchEnemyAgentSetup(harmony); if (DebugMode) { SnapshotExistingEnemies("initial"); } } private static void LogSpawnLikeMethods(Type enemyGroupType) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown MethodInfo[] array = (from methodInfo in enemyGroupType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where methodInfo.Name.Contains("Spawn", StringComparison.OrdinalIgnoreCase) select methodInfo).ToArray(); if (array.Length == 0) { Plugin.LogSource.LogWarning((object)"No method names containing Spawn were found on Enemies.EnemyGroup."); return; } MethodInfo[] array2 = array; bool flag = default(bool); foreach (MethodInfo method in array2) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Found Enemies.EnemyGroup spawn-like method: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(method)); } logSource.LogInfo(val); } } private static void PatchEnemyAgentSetup(Harmony harmony) { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown if (EnemyAgentType == null) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Cannot patch EnemyAgent.Setup because Enemies.EnemyAgent was not found."); return; } MethodInfo[] array = (from methodInfo3 in EnemyAgentType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where methodInfo3.Name == "Setup" select methodInfo3).ToArray(); if (array.Length == 0) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not find any Enemies.EnemyAgent.Setup overloads."); return; } bool flag2 = default(bool); MethodInfo[] array2; BepInExInfoLogInterpolatedStringHandler val; if (DebugMode) { array2 = array; foreach (MethodInfo method in array2) { bool flag = HasEnemySpawnDataParameter(method); ManualLogSource logSource = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(76, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found EnemyAgent.Setup overload: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(method)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; has pEnemySpawnData="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag); } logSource.LogInfo(val); } } MethodInfo methodInfo = AccessTools.Method(typeof(EnemyGroupSpawnPatch), "EnemyAgentSetupPostfix", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.LogSource.LogError((object)"[Alarm Spawn Teller] Could not find local EnemyAgentSetupPostfix method."); return; } int num2 = 0; array2 = array; foreach (MethodInfo methodInfo2 in array2) { try { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num2++; if (DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(56, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Patched EnemyAgent.Setup overload: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(methodInfo2)); } logSource2.LogInfo(val); } } catch (Exception ex) { ManualLogSource logSource3 = Plugin.LogSource; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(65, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] Failed to patch EnemyAgent.Setup overload "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(FormatMethodSignature(methodInfo2)); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex); } logSource3.LogError(val2); } } ManualLogSource logSource4 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(57, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Patched "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" EnemyAgent.Setup overloads."); } logSource4.LogInfo(val); } private static bool InitializeUnityObjectAccess() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Type typeFromHandle = typeof(Object); if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(49, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Found Unity object type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(typeFromHandle.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(typeFromHandle.Assembly.GetName().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("]"); } logSource.LogInfo(val); } FindObjectsOfTypeMethod = typeFromHandle.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return method.Name == "FindObjectsOfType" && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type); }); GetInstanceIdMethod = typeFromHandle.GetMethod("GetInstanceID", BindingFlags.Instance | BindingFlags.Public); UnityObjectNameProperty = typeFromHandle.GetProperty("name", BindingFlags.Instance | BindingFlags.Public); FindObjectsOfTypeAllMethod = typeof(Resources).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return method.Name == "FindObjectsOfTypeAll" && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type); }); if (FindObjectsOfTypeMethod == null || FindObjectsOfTypeAllMethod == null || GetInstanceIdMethod == null) { if (DebugMode) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] Could not initialize Unity object reflection helpers."); } return false; } if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Initialized Unity object reflection helpers."); } return true; } private static bool HasSurvivalWaveParameter(MethodBase method) { return method.GetParameters().Any(IsSurvivalWaveParameter); } private static bool HasEnemySpawnDataParameter(MethodBase method) { return method.GetParameters().Any(IsEnemySpawnDataParameter); } private static bool IsSurvivalWaveParameter(ParameterInfo parameter) { string? obj = parameter.ParameterType.FullName ?? parameter.ParameterType.Name; string text = parameter.Name ?? string.Empty; if (!obj.Contains("SurvivalWave", StringComparison.OrdinalIgnoreCase)) { return text.Contains("survivalWave", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsEnemySpawnDataParameter(ParameterInfo parameter) { if (!IsParameterNamedOrTyped(parameter, "pEnemySpawnData") && !IsParameterNamedOrTyped(parameter, "enemySpawnData")) { return IsParameterNamedOrTyped(parameter, "spawnData"); } return true; } private static bool IsEnemyGroupSpawnDataParameter(ParameterInfo parameter) { return IsParameterNamedOrTyped(parameter, "pEnemyGroupSpawnData"); } private static object GetSurvivalWaveArgument(MethodBase originalMethod, object[] args) { if (originalMethod == null || args == null) { return null; } ParameterInfo[] parameters = originalMethod.GetParameters(); for (int i = 0; i < parameters.Length && i < args.Length; i++) { if (IsSurvivalWaveParameter(parameters[i])) { return args[i]; } } return null; } private static bool IsAlarmWaveSpawn(MethodBase originalMethod, object[] args) { if (GetSurvivalWaveArgument(originalMethod, args) == null) { return false; } if (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "groupType"), out var _, out var value)) { return string.Equals(value?.ToString(), "Survival", StringComparison.OrdinalIgnoreCase); } return false; } private static SpawnSource InferSpawnSource(MethodBase originalMethod, object[] args) { object survivalWaveArgument = GetSurvivalWaveArgument(originalMethod, args); if (survivalWaveArgument != null && BloodDoorCandidateClassificationEnabled && EvaluateBloodDoorCandidate(survivalWaveArgument).CandidateMatched) { return SpawnSource.BloodDoor; } if (SpawnDataBloodDoorCandidateClassificationEnabled && TryGetParameterValue(originalMethod, args, IsEnemyGroupSpawnDataParameter, out var _, out var value) && EvaluateSpawnDataBloodDoorCandidate(value).CandidateMatched) { return SpawnSource.BloodDoor; } if (IsAlarmWaveSpawn(originalMethod, args)) { return SpawnSource.SurvivalWave; } if (survivalWaveArgument != null) { return SpawnSource.SurvivalWave; } return SpawnSource.UnknownDynamic; } private static string FormatSpawnSourceLabel(SpawnSource source) { return Localizer.GetSourceDisplayName(source); } private static bool TryGetSpawnDataArgument(MethodBase originalMethod, object[] args, out object spawnData) { ParameterInfo parameter; return TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "pEnemyGroupSpawnData") || IsParameterNamedOrTyped(parameter2, "spawnData"), out parameter, out spawnData); } private static bool TryGetEnemySpawnDataArgument(MethodBase originalMethod, object[] args, out object spawnData) { ParameterInfo parameter; return TryGetParameterValue(originalMethod, args, IsEnemySpawnDataParameter, out parameter, out spawnData); } private static bool TryGetParameterValue(MethodBase originalMethod, object[] args, Func predicate, out ParameterInfo parameter, out object value) { parameter = null; value = null; if (originalMethod == null || args == null) { return false; } ParameterInfo[] parameters = originalMethod.GetParameters(); for (int i = 0; i < parameters.Length && i < args.Length; i++) { if (predicate(parameters[i])) { parameter = parameters[i]; value = args[i]; return true; } } return false; } private static bool IsParameterNamedOrTyped(ParameterInfo parameter, string keyword) { string? obj = parameter.ParameterType.FullName ?? parameter.ParameterType.Name; string text = parameter.Name ?? string.Empty; if (!obj.Contains(keyword, StringComparison.OrdinalIgnoreCase)) { return text.Contains(keyword, StringComparison.OrdinalIgnoreCase); } return true; } private static void LogSpawnDiagnostic(MethodBase originalMethod, object[] args) { //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Expected O, but got Unknown //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Expected O, but got Unknown if (!DebugMode || !CanLogSpawnDiagnostic()) { return; } string text = ((originalMethod == null) ? "" : FormatMethodSignature(originalMethod)); ParameterInfo parameter; object value; string text2 = (TryGetParameterValue(originalMethod, args, IsSurvivalWaveParameter, out parameter, out value) ? $"present, null={value == null}" : "not present"); object value2; string text3 = (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "spawnType"), out parameter, out value2) ? FormatValueSummary(value2) : ""); object value3; string text4 = (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "groupType"), out parameter, out value3) ? FormatValueSummary(value3) : ""); object value4; string text5 = (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "populationScore"), out parameter, out value4) ? FormatValueSummary(value4) : ""); object value5; string text6 = (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "persistentGameDataID"), out parameter, out value5) ? FormatValueSummary(value5) : ""); SpawnSource spawnSource = InferSpawnSource(originalMethod, args); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(177, 9, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Spawn diagnostic: overload="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; survivalWave="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text4); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; populationScore="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text5); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; persistentGameDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text6); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; activeSession="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(IsAlarmSessionOpen); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; activeCapture="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(IsCaptureWindowOpen); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; inferredSource="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnSource); } logSource.LogInfo(val); if (TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "pEnemyGroupSpawnData") || IsParameterNamedOrTyped(parameter2, "spawnData"), out parameter, out var value6)) { ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(49, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Spawn diagnostic spawnData: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatObjectMembersSummary(value6)); } logSource2.LogInfo(val); } if (!HasLoggedSpawnDiagnosticStack) { HasLoggedSpawnDiagnosticStack = true; ManualLogSource logSource3 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(45, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Spawn diagnostic stack: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Environment.StackTrace); } logSource3.LogInfo(val); } } private static void LogSourceInvestigation(MethodBase originalMethod, object[] args, SpawnPatchState patchState) { //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Expected O, but got Unknown //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Expected O, but got Unknown //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Expected O, but got Unknown if (!DebugSourceModeEnabled && !BloodDoorCandidateClassificationEnabled && !SpawnDataBloodDoorCandidateClassificationEnabled) { return; } int num = Interlocked.Increment(ref SourceInvestigationEventIndex); bool flag4 = default(bool); try { TryGetParameterValue(originalMethod, args, IsSurvivalWaveParameter, out var parameter, out var value); object value2; bool flag = TryGetParameterValue(originalMethod, args, IsEnemyGroupSpawnDataParameter, out parameter, out value2); if (value != null) { LogBloodDoorCandidate(num, value); } if (flag) { SpawnDataRouteEvaluation route = patchState?.SpawnDataRoute ?? ResolveSpawnDataRoute(value2); LogSpawnDataBloodDoorCandidate(num, route); } if (DebugSourceModeEnabled) { RunSourceTypeDiscoveryOnce(); string text = ((originalMethod == null) ? "" : FormatMethodSignature(originalMethod)); string parameterSummary = GetParameterSummary(originalMethod, args, "groupType"); string parameterSummary2 = GetParameterSummary(originalMethod, args, "spawnType"); string parameterSummary3 = GetParameterSummary(originalMethod, args, "persistentGameDataID"); string parameterSummary4 = GetParameterSummary(originalMethod, args, "populationScore"); string parameterSummary5 = GetParameterSummary(originalMethod, args, "position"); string parameterSummary6 = GetParameterSummary(originalMethod, args, "rotation"); TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "courseNode"), out parameter, out var value3); TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, "targetReplicator"), out parameter, out var value4); SpawnSource spawnSource = InferSpawnSource(originalMethod, args); SpawnSourceKind spawnSourceKind = GetSpawnSourceKind(CurrentSessionSpawnSource); SpawnTracker trackerForSource = GetTrackerForSource(CurrentSessionSpawnSource); SpawnDataRouteEvaluation obj = patchState?.SpawnDataRoute; bool flag2 = obj?.RouteExclusive ?? false; bool flag3 = obj?.SkippedDynamicBecauseBloodDoorCandidate ?? false; string text2 = obj?.TargetTracker ?? trackerForSource.Name; ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(254, 14, ref flag4); if (flag4) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SourceInvestigation] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; method="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; persistentGameDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; populationScore="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary4); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; survivalWavePresent="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(value != null); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; survivalWaveType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatRuntimeType(value)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; position="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary5); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; rotation="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(parameterSummary6); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; courseNodePresent="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(value3 != null); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; courseNodeType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatRuntimeType(value3)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; targetReplicatorPresent="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(value4 != null); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; inferredSource="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnSource); } logSource.LogInfo(val); ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(173, 7, ref flag4); if (flag4) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SpawnRoute] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; currentSessionSource="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; currentSourceKind="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnSourceKind); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; tracker="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; routeExclusive="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; skippedDynamicBecauseBloodDoorCandidate="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; bloodDoorClassificationEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(BloodDoorCandidateClassificationEnabled); } logSource2.LogInfo(val); DumpFocusedMembers("[AST SurvivalWaveDump]", num, "survivalWave", value, (IReadOnlyCollection)(object)SurvivalWaveDumpKeywords); DumpSurvivalWaveRelatedObjects(num, value); DumpFocusedMembers("[AST CourseNodeDump]", num, "courseNode", value3, (IReadOnlyCollection)(object)CourseNodeDumpKeywords); if (flag) { DumpEnemyGroupSpawnData(num, value2); } } } catch (Exception ex) { ManualLogSource logSource3 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(48, 3, ref flag4); if (flag4) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[AST SourceInvestigation] eventIndex="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; failed="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } logSource3.LogWarning(val2); } } private static void LogSpawnDataBloodDoorCandidate(int eventIndex, SpawnDataRouteEvaluation route) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown SpawnDataBloodDoorCandidateEvaluation candidate = route.Candidate; ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(444, 21, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SpawnDataBloodDoorCandidate] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.GroupType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; debugEnemyMode="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.DebugEnemyMode); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.SpawnType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; persistentGameDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.PersistentGameDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; persistentWavePopDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.PersistentWavePopDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; survivalWaveReplicatorKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.SurvivalWaveReplicatorKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; courseNodeIDPlusOne="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.CourseNodeIDPlusOne); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupTypeMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.GroupTypeMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnTypeMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.SpawnTypeMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnTypeExcluded="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.SpawnTypeExcluded); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; allowedSpawnTypes="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.AllowedSpawnTypes); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; excludedSpawnTypes="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.ExcludedSpawnTypes); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; candidateMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(candidate.CandidateMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; candidateSourceKind="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.CandidateSourceKind); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; inheritedSourceKind="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.InheritedSessionSourceKind); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; forcedNotBloodDoor="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.ForcedNotBloodDoor); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; candidateReason="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.CandidateReason); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; finalSourceKind="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.FinalSourceKind); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; targetTracker="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(route.TargetTracker); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; classificationEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(SpawnDataBloodDoorCandidateClassificationEnabled); } logSource.LogInfo(val); } private static SpawnDataRouteEvaluation ResolveSpawnDataRoute(object spawnData) { SpawnDataBloodDoorCandidateEvaluation spawnDataBloodDoorCandidateEvaluation = EvaluateSpawnDataBloodDoorCandidate(spawnData); SpawnSourceKind spawnSourceKind = (IsAlarmSessionOpen ? GetSpawnSourceKind(CurrentSessionSpawnSource) : SpawnSourceKind.None); SpawnSourceKind candidateSourceKind = (spawnDataBloodDoorCandidateEvaluation.CandidateMatched ? SpawnSourceKind.BloodDoor : SpawnSourceKind.None); bool forcedNotBloodDoor = !spawnDataBloodDoorCandidateEvaluation.CandidateMatched; SpawnSourceKind spawnSourceKind2 = (spawnDataBloodDoorCandidateEvaluation.CandidateMatched ? SpawnSourceKind.BloodDoor : (spawnSourceKind switch { SpawnSourceKind.BloodDoor => SpawnSourceKind.Dynamic, SpawnSourceKind.Dynamic => SpawnSourceKind.Dynamic, _ => SpawnSourceKind.None, })); SpawnDataRouteEvaluation spawnDataRouteEvaluation = new SpawnDataRouteEvaluation(); spawnDataRouteEvaluation.Candidate = spawnDataBloodDoorCandidateEvaluation; spawnDataRouteEvaluation.CandidateSourceKind = candidateSourceKind; spawnDataRouteEvaluation.InheritedSessionSourceKind = spawnSourceKind; spawnDataRouteEvaluation.FinalSourceKind = spawnSourceKind2; spawnDataRouteEvaluation.ForcedNotBloodDoor = forcedNotBloodDoor; spawnDataRouteEvaluation.RouteExclusive = spawnDataBloodDoorCandidateEvaluation.CandidateMatched; spawnDataRouteEvaluation.SkippedDynamicBecauseBloodDoorCandidate = spawnDataBloodDoorCandidateEvaluation.CandidateMatched; spawnDataRouteEvaluation.CandidateReason = GetSpawnDataCandidateReason(spawnDataBloodDoorCandidateEvaluation); SpawnDataRouteEvaluation spawnDataRouteEvaluation2 = spawnDataRouteEvaluation; spawnDataRouteEvaluation2.TargetTracker = spawnSourceKind2 switch { SpawnSourceKind.BloodDoor => BloodDoorSpawnTracker.Name, SpawnSourceKind.Dynamic => DynamicSpawnTracker.Name, _ => "", }; return spawnDataRouteEvaluation; } private static string GetSpawnDataCandidateReason(SpawnDataBloodDoorCandidateEvaluation candidate) { if (candidate == null) { return "candidate unavailable"; } if (candidate.SpawnTypeExcluded) { return "spawn type excluded"; } if (!SpawnDataBloodDoorCandidateClassificationEnabled) { return "classification disabled"; } if (!candidate.GroupTypeMatched) { return "group type not matched"; } if (!candidate.SpawnTypeMatched) { return "spawn type not allowed"; } if (!candidate.CandidateMatched) { return "candidate conditions not met"; } return "candidate matched"; } private static void ApplySpawnDataRoute(SpawnDataRouteEvaluation route) { if (route != null) { IsSpawnDataBloodDoorRouteExclusive = route.Candidate.CandidateMatched; if (!route.Candidate.CandidateMatched && CurrentSessionSpawnSource == SpawnSource.BloodDoor) { CurrentSessionSpawnSource = SpawnSource.UnknownDynamic; } } } private static SpawnDataBloodDoorCandidateEvaluation EvaluateSpawnDataBloodDoorCandidate(object spawnData) { SpawnDataBloodDoorCandidateEvaluation result = new SpawnDataBloodDoorCandidateEvaluation(); if (spawnData == null) { return result; } result.GroupType = FormatCandidateValue(GetFirstMemberValue(spawnData, "groupType", "GroupType", "m_groupType")); result.DebugEnemyMode = FormatCandidateValue(GetFirstMemberValue(spawnData, "debugEnemyMode", "DebugEnemyMode", "m_debugEnemyMode")); result.SpawnType = FormatCandidateValue(GetFirstMemberValue(spawnData, "spawnType", "SpawnType", "m_spawnType")); result.PersistentGameDataID = FormatCandidateValue(GetFirstMemberValue(spawnData, "persistentGameDataID", "PersistentGameDataID", "m_persistentGameDataID")); result.PersistentWavePopDataID = FormatCandidateValue(GetFirstMemberValue(spawnData, "persistentWavePopDataID", "PersistentWavePopDataID", "m_persistentWavePopDataID")); result.SurvivalWaveReplicatorKey = FormatCandidateValue(GetFirstMemberValue(spawnData, "survivalWaveReplicatorKey", "SurvivalWaveReplicatorKey", "m_survivalWaveReplicatorKey")); result.CourseNodeIDPlusOne = FormatCandidateValue(GetFirstMemberValue(spawnData, "courseNodeIDPlusOne", "CourseNodeIDPlusOne", "m_courseNodeIDPlusOne")); string[] configuredValues = GetConfiguredValues(BloodDoorSpawnDataGroupTypesConfig?.Value); string[] configuredValues2 = GetConfiguredValues(BloodDoorSpawnDataSpawnTypesConfig?.Value); string[] configuredValues3 = GetConfiguredValues(ExcludedBloodDoorSpawnDataSpawnTypesConfig?.Value); result.AllowedSpawnTypes = FormatConfiguredValues(configuredValues2); result.ExcludedSpawnTypes = FormatConfiguredValues(configuredValues3); result.GroupTypeMatched = configuredValues.Length != 0 && !string.IsNullOrWhiteSpace(result.GroupType) && result.GroupType != "" && configuredValues.Any((string groupType) => string.Equals(groupType, result.GroupType, StringComparison.OrdinalIgnoreCase)); result.SpawnTypeMatched = configuredValues2.Length != 0 && !string.IsNullOrWhiteSpace(result.SpawnType) && result.SpawnType != "" && configuredValues2.Any((string spawnType) => string.Equals(spawnType, result.SpawnType, StringComparison.OrdinalIgnoreCase)); result.SpawnTypeExcluded = configuredValues3.Length != 0 && !string.IsNullOrWhiteSpace(result.SpawnType) && result.SpawnType != "" && configuredValues3.Any((string spawnType) => string.Equals(spawnType, result.SpawnType, StringComparison.OrdinalIgnoreCase)); result.CandidateMatched = SpawnDataBloodDoorCandidateClassificationEnabled && result.GroupTypeMatched && result.SpawnTypeMatched && !result.SpawnTypeExcluded; return result; } private static string[] GetConfiguredValues(string configuredValues) { if (string.IsNullOrWhiteSpace(configuredValues)) { return Array.Empty(); } return (from value in configuredValues.Split(',', StringSplitOptions.RemoveEmptyEntries) select value.Trim() into value where !string.IsNullOrWhiteSpace(value) select value).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } private static string FormatConfiguredValues(string[] configuredValues) { if (configuredValues != null && configuredValues.Length != 0) { return string.Join(",", configuredValues); } return ""; } private static void LogBloodDoorCandidate(int eventIndex, object survivalWave) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown BloodDoorCandidateEvaluation bloodDoorCandidateEvaluation = EvaluateBloodDoorCandidate(survivalWave); SpawnSourceKind spawnSourceKind = ((bloodDoorCandidateEvaluation.CandidateMatched && BloodDoorCandidateClassificationEnabled) ? SpawnSourceKind.BloodDoor : GetSpawnSourceKind(CurrentSessionSpawnSource)); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(239, 12, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST BloodDoorCandidate] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; settings.name="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.SettingsName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; settings.persistentID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.SettingsPersistentID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; populationDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.PopulationDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.SpawnType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; overrideWaveSpawnType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.OverrideWaveSpawnType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; wavePauseMin="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.WavePauseMin); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; wavePauseMax="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.WavePauseMax); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; keywordMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.KeywordMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; candidateMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(bloodDoorCandidateEvaluation.CandidateMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; finalSourceKind="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnSourceKind); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; classificationEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(BloodDoorCandidateClassificationEnabled); } logSource.LogInfo(val); } private static BloodDoorCandidateEvaluation EvaluateBloodDoorCandidate(object survivalWave) { BloodDoorCandidateEvaluation result = new BloodDoorCandidateEvaluation(); if (survivalWave == null) { return result; } result.SurvivalWavePresent = true; if (!TryGetMatchingMemberValue(survivalWave, (string text) => text.Contains("Settings", StringComparison.OrdinalIgnoreCase), out var _, out var value) || value == null) { return result; } result.SettingsPresent = true; result.SettingsName = FormatCandidateValue(GetFirstMemberValue(value, "name", "Name", "m_name")); result.SettingsPersistentID = FormatCandidateValue(GetFirstMemberValue(value, "persistentID", "PersistentID", "m_persistentID")); result.PopulationDataID = FormatCandidateValue(GetFirstMemberValue(value, "m_populationDataID", "populationDataID", "PopulationDataID") ?? GetFirstMemberValue(survivalWave, "m_populationDataID", "populationDataID", "PopulationDataID")); object firstMemberValue = GetFirstMemberValue(value, "m_overrideWaveSpawnType", "overrideWaveSpawnType", "OverrideWaveSpawnType"); object firstMemberValue2 = GetFirstMemberValue(value, "m_survivalWaveSpawnType", "survivalWaveSpawnType", "SurvivalWaveSpawnType"); object firstMemberValue3 = GetFirstMemberValue(value, "m_wavePauseMin", "wavePauseMin", "WavePauseMin"); object firstMemberValue4 = GetFirstMemberValue(value, "m_wavePauseMax", "wavePauseMax", "WavePauseMax"); result.SpawnType = FormatCandidateValue(firstMemberValue2); result.OverrideWaveSpawnType = FormatCandidateValue(firstMemberValue); result.WavePauseMin = FormatCandidateValue(firstMemberValue3); result.WavePauseMax = FormatCandidateValue(firstMemberValue4); result.OverrideWaveSpawnTypeMatched = TryConvertBool(firstMemberValue, out var result2) && result2; result.SpawnTypeMatched = string.Equals(firstMemberValue2?.ToString(), "FromElevatorDirection", StringComparison.OrdinalIgnoreCase); result.WavePauseMinMatched = TryConvertDouble(firstMemberValue3, out var result3) && Math.Abs(result3) < 0.0001; result.WavePauseMaxMatched = TryConvertDouble(firstMemberValue4, out var result4) && Math.Abs(result4) < 0.0001; string[] bloodDoorSettingsNameKeywords = GetBloodDoorSettingsNameKeywords(); result.KeywordMatched = bloodDoorSettingsNameKeywords.Length != 0 && !string.IsNullOrWhiteSpace(result.SettingsName) && result.SettingsName != "" && bloodDoorSettingsNameKeywords.Any((string keyword) => result.SettingsName.Contains(keyword, StringComparison.OrdinalIgnoreCase)); result.CandidateMatched = result.SurvivalWavePresent && result.SettingsPresent && result.OverrideWaveSpawnTypeMatched && result.SpawnTypeMatched && result.WavePauseMinMatched && result.WavePauseMaxMatched && result.KeywordMatched; return result; } private static string[] GetBloodDoorSettingsNameKeywords() { string text = BloodDoorSettingsNameKeywordsConfig?.Value; if (string.IsNullOrWhiteSpace(text)) { return Array.Empty(); } return (from keyword in text.Split(',', StringSplitOptions.RemoveEmptyEntries) select keyword.Trim() into keyword where !string.IsNullOrWhiteSpace(keyword) select keyword).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } private static object GetFirstMemberValue(object source, params string[] memberNames) { if (source == null || memberNames == null) { return null; } foreach (string memberName in memberNames) { object obj = TryGetMemberValue(source, memberName); if (obj != null) { return obj; } } return null; } private static bool TryConvertBool(object value, out bool result) { result = false; if (value is bool flag) { result = flag; return true; } if (value != null) { return bool.TryParse(value.ToString(), out result); } return false; } private static bool TryConvertDouble(object value, out double result) { result = 0.0; if (value == null) { return false; } try { result = Convert.ToDouble(value, CultureInfo.InvariantCulture); return true; } catch { return double.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out result); } } private static string FormatCandidateValue(object value) { if (value != null) { return FormatValueSummary(value); } return ""; } private static string GetParameterSummary(MethodBase originalMethod, object[] args, string keyword) { if (!TryGetParameterValue(originalMethod, args, (ParameterInfo parameter2) => IsParameterNamedOrTyped(parameter2, keyword), out var _, out var value)) { return ""; } return FormatValueSummary(value); } private static void DumpEnemyGroupSpawnData(int eventIndex, object spawnData) { //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown int remainingLines = 80; bool flag = default(bool); if (spawnData == null) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(45, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SpawnDataDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; value="); } logSource.LogInfo(val); return; } try { LogSpawnDataLine(eventIndex, "context=spawnData; runtimeType=" + FormatRuntimeType(spawnData), ref remainingLines); SourceDumpMember[] array = ReadMatchingMembers(spawnData, (IReadOnlyCollection)(object)SpawnDataDumpKeywords).Take(Math.Max(0, remainingLines)).ToArray(); foreach (SourceDumpMember sourceDumpMember in array) { try { if (!LogSpawnDataLine(eventIndex, $"context=spawnData; member={sourceDumpMember.Name}; value={FormatValueSummary(sourceDumpMember.Value)}; runtimeType={FormatRuntimeType(sourceDumpMember.Value)}", ref remainingLines)) { break; } } catch (Exception ex) { if (!LogSpawnDataLine(eventIndex, $"context=spawnData; member={sourceDumpMember.Name}; value=", ref remainingLines)) { break; } } if (remainingLines > 0 && ShouldDumpNestedSpawnDataMember(sourceDumpMember.Name, sourceDumpMember.Value)) { DumpNestedSpawnDataMember(eventIndex, sourceDumpMember.Name, sourceDumpMember.Value, ref remainingLines); } } } catch (Exception ex2) { ManualLogSource logSource2 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(46, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[AST SpawnDataDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; dumpFailed="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2.GetType().Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2.Message); } logSource2.LogWarning(val2); } } private static void DumpNestedSpawnDataMember(int eventIndex, string parentMemberName, object value, ref int remainingLines) { if (value == null || remainingLines <= 0) { return; } LogSpawnDataLine(eventIndex, "context=nested; parent=" + parentMemberName + "; runtimeType=" + FormatRuntimeType(value), ref remainingLines); foreach (SourceDumpMember item in ReadMatchingMembers(value, (IReadOnlyCollection)(object)SpawnDataDumpKeywords).Take(Math.Min(30, Math.Max(0, remainingLines)))) { try { if (!LogSpawnDataLine(eventIndex, $"context=nested; parent={parentMemberName}; member={item.Name}; value={FormatValueSummary(item.Value)}; runtimeType={FormatRuntimeType(item.Value)}", ref remainingLines)) { break; } } catch (Exception ex) { if (!LogSpawnDataLine(eventIndex, $"context=nested; parent={parentMemberName}; member={item.Name}; value=", ref remainingLines)) { break; } } } } private static IReadOnlyList ReadMatchingMembers(object value, IReadOnlyCollection keywords) { if (value == null) { return Array.Empty(); } List list = new List(); Type type = value.GetType(); foreach (FieldInfo item in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy((FieldInfo field) => field.Name, StringComparer.OrdinalIgnoreCase)) { if (ContainsSourceKeyword(item.Name, keywords)) { try { list.Add(new SourceDumpMember(item.Name, item.GetValue(value))); } catch (Exception ex) { list.Add(new SourceDumpMember(item.Name, "")); } } } foreach (PropertyInfo property in (from propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where propertyInfo.GetIndexParameters().Length == 0 select propertyInfo).OrderBy((PropertyInfo propertyInfo) => propertyInfo.Name, StringComparer.OrdinalIgnoreCase)) { if (ContainsSourceKeyword(property.Name, keywords) && !list.Any((SourceDumpMember member) => string.Equals(member.Name, property.Name, StringComparison.OrdinalIgnoreCase))) { try { list.Add(new SourceDumpMember(property.Name, property.GetValue(value))); } catch (Exception ex2) { list.Add(new SourceDumpMember(property.Name, "")); } } } return list; } private static bool ShouldDumpNestedSpawnDataMember(string memberName, object value) { if (value == null || IsSimpleDumpValue(value)) { return false; } string value2 = value.GetType().FullName ?? value.GetType().Name; if (!ContainsSourceKeyword(memberName, NestedSpawnDataDumpKeywords)) { return ContainsSourceKeyword(value2, NestedSpawnDataDumpKeywords); } return true; } private static bool IsSimpleDumpValue(object value) { if (value == null) { return true; } Type type = value.GetType(); if (!type.IsPrimitive && !type.IsEnum && !(value is string)) { return value is decimal; } return true; } private static bool LogSpawnDataLine(int eventIndex, string message, ref int remainingLines) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (remainingLines <= 0) { return false; } ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SpawnDataDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(message); } logSource.LogInfo(val); remainingLines--; return true; } private static string FormatRuntimeType(object value) { return value?.GetType().FullName ?? ""; } private static void DumpSurvivalWaveRelatedObjects(int eventIndex, object survivalWave) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Expected O, but got Unknown if (survivalWave == null) { return; } bool flag = default(bool); if (TryGetMatchingMemberValue(survivalWave, (string text) => text.Contains("Settings", StringComparison.OrdinalIgnoreCase), out var memberName, out var value)) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(75, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=settings; member="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(memberName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; runtimeType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatRuntimeType(value)); } logSource.LogInfo(val); DumpFocusedMembers("[AST SurvivalWaveDump]", eventIndex, "settings", value, (IReadOnlyCollection)(object)SurvivalWaveDumpKeywords); } else { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(71, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=settings; value="); } logSource2.LogInfo(val); } string memberName2; object value2; bool flag2 = TryGetMatchingMemberValue(survivalWave, (string text) => text.Contains("PopulationDataID", StringComparison.OrdinalIgnoreCase), out memberName2, out value2); if (!flag2 && value != null) { flag2 = TryGetMatchingMemberValue(value, (string text) => text.Contains("PopulationDataID", StringComparison.OrdinalIgnoreCase), out memberName2, out value2); } if (flag2) { ManualLogSource logSource3 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(77, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=populationDataID; member="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(memberName2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; value="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueSummary(value2)); } logSource3.LogInfo(val); } else { ManualLogSource logSource4 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(79, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=populationDataID; value="); } logSource4.LogInfo(val); } string memberName3; object value3; bool flag3 = TryGetMatchingMemberValue(survivalWave, (string text) => text.Contains("Population", StringComparison.OrdinalIgnoreCase) && !text.Contains("ID", StringComparison.OrdinalIgnoreCase) && !text.Contains("Score", StringComparison.OrdinalIgnoreCase), out memberName3, out value3); if (!flag3 && value != null) { flag3 = TryGetMatchingMemberValue(value, (string text) => text.Contains("Population", StringComparison.OrdinalIgnoreCase) && !text.Contains("ID", StringComparison.OrdinalIgnoreCase) && !text.Contains("Score", StringComparison.OrdinalIgnoreCase), out memberName3, out value3); } if (flag3) { ManualLogSource logSource5 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(77, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=population; member="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(memberName3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; runtimeType="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatRuntimeType(value3)); } logSource5.LogInfo(val); DumpFocusedMembers("[AST SurvivalWaveDump]", eventIndex, "population", value3, (IReadOnlyCollection)(object)SurvivalWaveDumpKeywords); } else { ManualLogSource logSource6 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(73, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SurvivalWaveDump] eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context=population; value="); } logSource6.LogInfo(val); } } private static bool TryGetMatchingMemberValue(object source, Func namePredicate, out string memberName, out object value) { memberName = null; value = null; if (source == null || namePredicate == null) { return false; } Type type = source.GetType(); foreach (FieldInfo item in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy((FieldInfo field) => field.Name, StringComparer.OrdinalIgnoreCase)) { if (namePredicate(item.Name)) { try { memberName = item.Name; value = item.GetValue(source); return true; } catch { } } } foreach (PropertyInfo item2 in (from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where property.GetIndexParameters().Length == 0 select property).OrderBy((PropertyInfo property) => property.Name, StringComparer.OrdinalIgnoreCase)) { if (namePredicate(item2.Name)) { try { memberName = item2.Name; value = item2.GetValue(source); return true; } catch { } } } return false; } private static void DumpFocusedMembers(string prefix, int eventIndex, string context, object value, IReadOnlyCollection keywords) { //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030e: 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 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Expected O, but got Unknown //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown bool flag = default(bool); if (value == null) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(36, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; value="); } logSource.LogInfo(val); return; } try { Type type = value.GetType(); List<(string, Func)> list = new List<(string, Func)>(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { if (ContainsSourceKeyword(field.Name, keywords)) { list.Add((field.Name, () => field.GetValue(value))); } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo property in properties) { if (property.GetIndexParameters().Length == 0 && ContainsSourceKeyword(property.Name, keywords)) { list.Add((property.Name, () => property.GetValue(value))); } } (string, Func)[] array = (from @group in list.GroupBy<(string, Func), string>(((string Name, Func Read) member) => member.Name, StringComparer.OrdinalIgnoreCase) select @group.First()).OrderBy<(string, Func), string>(((string Name, Func Read) member) => member.Name, StringComparer.OrdinalIgnoreCase).Take(80).ToArray(); ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(47, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; type="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(type.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; matchingMembers="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(array.Length); } logSource2.LogInfo(val); (string, Func)[] array2 = array; for (int i = 0; i < array2.Length; i++) { (string, Func) tuple = array2[i]; try { ManualLogSource logSource3 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(25, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tuple.Item1); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueSummary(tuple.Item2())); } logSource3.LogInfo(val); } catch (Exception ex) { ManualLogSource logSource4 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(33, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tuple.Item1); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("=(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(">"); } logSource4.LogInfo(val); } } if (list.Count > 80) { ManualLogSource logSource5 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(34, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; truncated="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(list.Count - 80); } logSource5.LogInfo(val); } } catch (Exception ex2) { ManualLogSource logSource6 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(39, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" eventIndex="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(eventIndex); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; context="); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(context); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; dump failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2.GetType().Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2.Message); } logSource6.LogWarning(val2); } } private static bool ContainsSourceKeyword(string value, IEnumerable keywords) { if (!string.IsNullOrWhiteSpace(value)) { return keywords.Any((string keyword) => value.Contains(keyword, StringComparison.OrdinalIgnoreCase)); } return false; } private static void RunSourceTypeDiscoveryOnce() { //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown if (!DebugSourceModeEnabled || HasRunSourceTypeDiscovery) { return; } HasRunSourceTypeDiscovery = true; bool flag = default(bool); try { List<(string, string)> list = new List<(string, string)>(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { foreach (Type loadableType in GetLoadableTypes(assembly)) { string text = loadableType?.FullName; if (ContainsSourceKeyword(text, SourceTypeDiscoveryKeywords)) { list.Add((assembly.GetName().Name ?? "", text)); } } } (string, string)[] array = list.Distinct().OrderBy<(string, string), string>(((string AssemblyName, string TypeName) match) => match.AssemblyName, StringComparer.OrdinalIgnoreCase).ThenBy<(string, string), string>(((string AssemblyName, string TypeName) match) => match.TypeName, StringComparer.OrdinalIgnoreCase) .ToArray(); ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(37, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST TypeDiscovery] matches="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(list.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; output="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(array.Length); } logSource.LogInfo(val); (string, string)[] array2 = array; for (int i = 0; i < array2.Length; i++) { (string, string) tuple = array2[i]; ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(36, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST TypeDiscovery] assembly="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tuple.Item1); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; type="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tuple.Item2); } logSource2.LogInfo(val); } } catch (Exception ex) { ManualLogSource logSource3 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(30, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[AST TypeDiscovery] failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } logSource3.LogWarning(val2); } } private static IEnumerable GetLoadableTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type type) => type != null); } catch { return Array.Empty(); } } private static bool CanLogSpawnDiagnostic() { float time = Time.time; if (time - SpawnDiagnosticWindowStart > 10f) { SpawnDiagnosticWindowStart = time; SpawnDiagnosticsThisWindow = 0; } if (SpawnDiagnosticsThisWindow >= 20) { return false; } SpawnDiagnosticsThisWindow++; return true; } private static void OpenOrExtendCaptureWindow(MethodBase originalMethod) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown float num = Time.time + 3f; bool flag = default(bool); if (!IsCaptureWindowOpen) { IsCaptureWindowOpen = true; CaptureEndTime = num; ActiveCaptureRecords.Clear(); if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(63, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm spawn capture window opened until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CaptureEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource.LogInfo(val); } return; } float captureEndTime = CaptureEndTime; CaptureEndTime = Math.Max(CaptureEndTime, num); if (CaptureEndTime > captureEndTime && DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(65, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm spawn capture window extended until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CaptureEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource2.LogInfo(val); } } internal static void ProcessCaptureWindowTimeout() { if (IsCaptureWindowOpen && !(Time.time < CaptureEndTime)) { IsCaptureWindowOpen = false; if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Alarm spawn capture window timed out and closed"); LogCaptureSummary(); } ActiveCaptureRecords.Clear(); } } private static void CaptureSpawnData(object spawnData) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown if (!IsCaptureWindowOpen || spawnData == null) { return; } SpawnCaptureRecord spawnCaptureRecord = SpawnCaptureRecord.FromSpawnData(spawnData); bool flag = default(bool); if (spawnCaptureRecord.IsLikelyHibernating) { if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(60, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Capture skipped hibernating spawnData: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnCaptureRecord.ToSummary()); } logSource.LogInfo(val); } return; } ActiveCaptureRecords.Add(spawnCaptureRecord); if (DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(47, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Captured alarm spawnData: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnCaptureRecord.ToSummary()); } logSource2.LogInfo(val); } } private static void OpenOrExtendAlarmSession(MethodBase originalMethod, object[] args) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown float num = Time.time + 8f; SpawnSource spawnSource = InferSpawnSource(originalMethod, args); bool flag = default(bool); if (!IsAlarmSessionOpen) { IsAlarmSessionOpen = true; AlarmSessionEndTime = num; CurrentSessionSpawnSource = spawnSource; ActiveAgentCaptureRecords.Clear(); CapturedAgentKeys.Clear(); UpdateAlarmHud(); if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Alarm session opened"); ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(61, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm session extended until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AlarmSessionEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource.LogInfo(val); } return; } float alarmSessionEndTime = AlarmSessionEndTime; AlarmSessionEndTime = Math.Max(AlarmSessionEndTime, num); if (spawnSource == SpawnSource.BloodDoor) { CurrentSessionSpawnSource = SpawnSource.BloodDoor; } else if (CurrentSessionSpawnSource == SpawnSource.UnknownDynamic && spawnSource != SpawnSource.UnknownDynamic) { CurrentSessionSpawnSource = spawnSource; } if (DebugMode && AlarmSessionEndTime > alarmSessionEndTime) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(61, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm session extended until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AlarmSessionEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource2.LogInfo(val); } } internal static void ProcessAlarmSessionTimeout() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown if (!IsAlarmSessionOpen || Time.time < AlarmSessionEndTime) { return; } IsAlarmSessionOpen = false; if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm session closed with total "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ActiveAgentCaptureRecords.Count); } logSource.LogInfo(val); } LogAgentCaptureSummary(); ActiveAgentCaptureRecords.Clear(); CapturedAgentKeys.Clear(); UpdateAlarmHud(); } internal static void ProcessPendingAgentRecordResolves() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown if (!IsAlarmSessionOpen) { return; } float time = Time.time; bool flag = default(bool); foreach (AgentCaptureRecord activeAgentCaptureRecord in ActiveAgentCaptureRecords) { if (!activeAgentCaptureRecord.NeedsDelayedResolve || time < activeAgentCaptureRecord.ResolveAfterTime) { continue; } activeAgentCaptureRecord.ResolveFromInstance(); UpdateTrackedEnemy(activeAgentCaptureRecord); UpdateAlarmHud(); if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(54, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Delayed EnemyAgent data resolve: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(activeAgentCaptureRecord.ToSummary()); } logSource.LogInfo(val); } } } private static void CaptureEnemyAgentSetup(object instance, MethodBase originalMethod, object[] args) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Expected O, but got Unknown //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Expected O, but got Unknown //IL_043a: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Expected O, but got Unknown //IL_04e2: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Expected O, but got Unknown //IL_048b: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Expected O, but got Unknown string agentInstanceKey = GetAgentInstanceKey(instance); TryGetEnemySpawnDataArgument(originalMethod, args, out var spawnData); AgentCaptureRecord agentCaptureRecord = AgentCaptureRecord.FromSetup(instance, spawnData, agentInstanceKey); bool isAlarmSessionOpen = IsAlarmSessionOpen; SpawnSource currentSessionSpawnSource = CurrentSessionSpawnSource; string targetTrackerName = (isAlarmSessionOpen ? GetTrackerForSource(currentSessionSpawnSource).Name : ""); bool dedupeSkipped = false; bool ignored = false; bool tracked = false; string decision = "not processed"; try { bool flag = default(bool); if (!IsAlarmSessionOpen && DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(80, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent.Setup with no active session: agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentInstanceKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; method="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource.LogInfo(val); } if (EnemyTrackerByKey.TryGetValue(agentInstanceKey, out var value)) { targetTrackerName = value.Name; dedupeSkipped = true; ignored = true; decision = "tracked dedupe"; LogSpecialEnemyFallbackDedupeIfRelevant(agentCaptureRecord, spawnData, targetTrackerName); if (DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(111, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent.Setup capture skipped by tracked dedupe: source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentInstanceKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dedupeSkipped=True"); } logSource2.LogInfo(val); } return; } if (IsSpawnDataBloodDoorRouteExclusive) { SpecialEnemyFallbackEvaluation specialEnemyFallbackEvaluation = EvaluateSpecialEnemyFallback(agentCaptureRecord, spawnData); if (specialEnemyFallbackEvaluation.KeywordMatched) { if (TryTrackSpecialEnemyFallback(agentCaptureRecord, spawnData, out targetTrackerName, out dedupeSkipped, out tracked, out decision)) { ignored = dedupeSkipped || !tracked; return; } ignored = true; decision = specialEnemyFallbackEvaluation.Decision; targetTrackerName = specialEnemyFallbackEvaluation.TargetTracker; } else { TrackBloodDoorCandidateNormalEnemy(agentCaptureRecord, originalMethod, spawnData, out targetTrackerName, out dedupeSkipped, out tracked, out decision); ignored = dedupeSkipped || !tracked; } return; } bool flag2 = false; if (!IsAlarmSessionOpen) { string enemyDataName = AgentCaptureRecord.FirstKnown(agentCaptureRecord.EnemyDataName, agentCaptureRecord.EnemyDataInternalName, agentCaptureRecord.GroupKey); bool flag3 = LooksLikeDynamicSpawnFromSetup(instance, spawnData, agentCaptureRecord.EnemyDataID, enemyDataName); if (DebugMode) { ManualLogSource logSource3 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(101, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] LooksLikeDynamicSpawnFromSetup result: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.EnemyDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.EnemyDataName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentInstanceKey); } logSource3.LogInfo(val); } if (!flag3) { if (TryTrackSpecialEnemyFallback(agentCaptureRecord, spawnData, out targetTrackerName, out dedupeSkipped, out tracked, out decision)) { ignored = dedupeSkipped || !tracked; return; } ignored = true; decision = "client fallback rejected"; return; } OpenOrExtendClientFallbackSession(originalMethod); flag2 = true; } targetTrackerName = GetTrackerForSource(CurrentSessionSpawnSource).Name; if (!CapturedAgentKeys.Add(agentInstanceKey)) { dedupeSkipped = true; ignored = true; decision = "session dedupe"; if (DebugMode) { ManualLogSource logSource4 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(111, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent.Setup capture skipped by session dedupe: source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentInstanceKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dedupeSkipped=True"); } logSource4.LogInfo(val); } return; } ActiveAgentCaptureRecords.Add(agentCaptureRecord); SpawnTracker trackerForSource = GetTrackerForSource(CurrentSessionSpawnSource); targetTrackerName = trackerForSource.Name; if (!trackerForSource.TrackEnemy(agentCaptureRecord, CurrentSessionSpawnSource)) { dedupeSkipped = true; ignored = true; decision = "tracker rejected duplicate"; return; } EnemyTrackerByKey[agentInstanceKey] = trackerForSource; ExtendAlarmSessionFromAgentSetup(originalMethod); UpdateAlarmHud(); tracked = true; decision = "tracked"; if (DebugMode) { ManualLogSource logSource5 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(106, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Captured agent: enemyDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.EnemyDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.EnemyDataName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentInstanceKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dedupeSkipped=False"); } logSource5.LogInfo(val); ManualLogSource logSource6 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(51, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Tracked enemy added: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.GroupKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); } logSource6.LogInfo(val); if (flag2) { ManualLogSource logSource7 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(67, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Client fallback tracked enemy added: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(agentCaptureRecord.GroupKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); } logSource7.LogInfo(val); } } if (DebugMode && spawnData != null) { ManualLogSource logSource8 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(57, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent.Setup spawnData summary: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatFocusedObjectMembersSummary(spawnData)); } logSource8.LogInfo(val); } } finally { LogEnemySetupProbe(agentCaptureRecord, spawnData, isAlarmSessionOpen, currentSessionSpawnSource, targetTrackerName, dedupeSkipped, ignored, tracked, decision); } } private static void TrackBloodDoorCandidateNormalEnemy(AgentCaptureRecord record, MethodBase originalMethod, object spawnData, out string targetTrackerName, out bool dedupeSkipped, out bool tracked, out string decision) { targetTrackerName = BloodDoorSpawnTracker.Name; dedupeSkipped = false; tracked = false; decision = "BloodDoor candidate normal enemy"; if (!CapturedAgentKeys.Add(record.AgentKey)) { dedupeSkipped = true; decision = "BloodDoor session dedupe"; } else if (!BloodDoorSpawnTracker.TrackEnemy(record, SpawnSource.BloodDoor)) { dedupeSkipped = true; decision = "BloodDoor tracker rejected duplicate"; } else { EnemyTrackerByKey[record.AgentKey] = BloodDoorSpawnTracker; ActiveAgentCaptureRecords.Add(record); if (IsAlarmSessionOpen) { ExtendAlarmSessionFromAgentSetup(originalMethod); } UpdateAlarmHud(); tracked = true; decision = "tracked by exclusive BloodDoor candidate route"; } SpecialEnemyFallbackEvaluation evaluation = EvaluateSpecialEnemyFallback(record, spawnData); LogSpecialEnemyFallback(record, evaluation, targetTrackerName, dedupeSkipped, tracked, decision, skippedBecauseBloodDoorCandidateNormalEnemy: true); } private static bool TryTrackSpecialEnemyFallback(AgentCaptureRecord record, object spawnData, out string targetTrackerName, out bool dedupeSkipped, out bool tracked, out string decision) { targetTrackerName = ""; dedupeSkipped = false; tracked = false; decision = "special fallback not matched"; SpecialEnemyFallbackEvaluation specialEnemyFallbackEvaluation = EvaluateSpecialEnemyFallback(record, spawnData); if (!specialEnemyFallbackEvaluation.ShouldLog) { return false; } targetTrackerName = specialEnemyFallbackEvaluation.TargetTracker; if (!specialEnemyFallbackEvaluation.FallbackMatched) { decision = specialEnemyFallbackEvaluation.Decision; LogSpecialEnemyFallback(record, specialEnemyFallbackEvaluation, targetTrackerName, dedupeSkipped, tracked, decision); return false; } targetTrackerName = DynamicSpawnTracker.Name; if (EnemyTrackerByKey.ContainsKey(record.AgentKey)) { dedupeSkipped = true; decision = "tracked dedupe"; LogSpecialEnemyFallback(record, specialEnemyFallbackEvaluation, targetTrackerName, dedupeSkipped, tracked, decision); return true; } if (!DynamicSpawnTracker.TrackEnemy(record, SpawnSource.UnknownDynamic)) { dedupeSkipped = true; decision = "tracker rejected duplicate"; LogSpecialEnemyFallback(record, specialEnemyFallbackEvaluation, targetTrackerName, dedupeSkipped, tracked, decision); return true; } EnemyTrackerByKey[record.AgentKey] = DynamicSpawnTracker; UpdateAlarmHud(); tracked = true; decision = "tracked by special fallback"; LogSpecialEnemyFallback(record, specialEnemyFallbackEvaluation, targetTrackerName, dedupeSkipped, tracked, decision); return true; } private static void LogSpecialEnemyFallbackDedupeIfRelevant(AgentCaptureRecord record, object spawnData, string targetTrackerName) { SpecialEnemyFallbackEvaluation specialEnemyFallbackEvaluation = EvaluateSpecialEnemyFallback(record, spawnData); if (specialEnemyFallbackEvaluation.ShouldLog) { LogSpecialEnemyFallback(record, specialEnemyFallbackEvaluation, targetTrackerName, dedupeSkipped: true, tracked: false, "tracked dedupe"); } } private static SpecialEnemyFallbackEvaluation EvaluateSpecialEnemyFallback(AgentCaptureRecord record, object spawnData) { string[] keywords = GetConfiguredValues(SpecialEnemyFallbackNameKeywordsConfig?.Value); string[] source = new string[3] { record?.EnemyDataName, record?.EnemyDataInternalName, record?.InstanceName }; bool flag = keywords.Length != 0 && source.Any((string name) => !string.IsNullOrWhiteSpace(name) && keywords.Any((string keyword) => name.Contains(keyword, StringComparison.OrdinalIgnoreCase))); string text = ReadSpawnDataMode(spawnData); bool flag2 = ContainsAny(text, "Agressive", "Aggressive"); bool flag3 = SpecialEnemyFallbackRequireAgressiveModeConfig?.Value ?? true; bool flag4 = string.Equals(SpecialEnemyFallbackTargetConfig?.Value?.Trim(), "Dynamic", StringComparison.OrdinalIgnoreCase); bool fallbackMatched = SpecialEnemyFallbackTrackingEnabled && flag && (!flag3 || flag2) && flag4; string decision = ((!SpecialEnemyFallbackTrackingEnabled) ? "fallback disabled" : ((keywords.Length == 0) ? "keyword list empty" : ((!flag) ? "keyword not matched" : ((flag3 && !flag2) ? "aggressive mode required" : ((!flag4) ? "unsupported target" : "fallback matched"))))); SpecialEnemyFallbackEvaluation specialEnemyFallbackEvaluation = new SpecialEnemyFallbackEvaluation(); specialEnemyFallbackEvaluation.SpawnDataMode = text; specialEnemyFallbackEvaluation.GroupReplicatorKey = FormatCandidateValue(GetFirstMemberValue(spawnData, "groupReplicatorKey", "GroupReplicatorKey", "m_groupReplicatorKey")); specialEnemyFallbackEvaluation.KeywordMatched = flag; specialEnemyFallbackEvaluation.AggressiveModeMatched = flag2; specialEnemyFallbackEvaluation.FallbackEnabled = SpecialEnemyFallbackTrackingEnabled; specialEnemyFallbackEvaluation.FallbackMatched = fallbackMatched; specialEnemyFallbackEvaluation.TargetTracker = (flag4 ? DynamicSpawnTracker.Name : ""); specialEnemyFallbackEvaluation.Decision = decision; specialEnemyFallbackEvaluation.ShouldLog = flag; return specialEnemyFallbackEvaluation; } private static void LogSpecialEnemyFallback(AgentCaptureRecord record, SpecialEnemyFallbackEvaluation evaluation, string targetTrackerName, bool dedupeSkipped, bool tracked, string decision, bool skippedBecauseBloodDoorCandidateNormalEnemy = false) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(283, 14, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST SpecialEnemyFallback] enemyDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; instanceName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.InstanceName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnDataMode="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.SpawnDataMode); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupReplicatorKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.GroupReplicatorKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; keywordMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.KeywordMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; aggressiveModeMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.AggressiveModeMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; fallbackEnabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.FallbackEnabled); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; fallbackMatched="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(evaluation.FallbackMatched); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; targetTracker="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(targetTrackerName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dedupeSkipped="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(dedupeSkipped); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; tracked="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tracked); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; skippedBecauseBloodDoorCandidateNormalEnemy="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(skippedBecauseBloodDoorCandidateNormalEnemy); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; decision="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(decision); } logSource.LogInfo(val); } private static void LogEnemySetupProbe(AgentCaptureRecord record, object spawnData, bool activeSessionAtEntry, SpawnSource sourceAtEntry, string targetTrackerName, bool dedupeSkipped, bool ignored, bool tracked, string decision) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if (!EnemySetupProbeEnabled || record == null) { return; } string[] keywords = GetConfiguredValues(EnemySetupProbeNameKeywordsConfig?.Value); if (keywords.Length != 0 && new string[3] { record.EnemyDataName, record.EnemyDataInternalName, record.InstanceName }.Any((string name) => !string.IsNullOrWhiteSpace(name) && keywords.Any((string keyword) => name.Contains(keyword, StringComparison.OrdinalIgnoreCase)))) { string text = ReadSpawnDataMode(spawnData); string text2 = FormatCandidateValue(GetFirstMemberValue(spawnData, "groupReplicatorKey", "GroupReplicatorKey", "m_groupReplicatorKey")); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(298, 17, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST EnemySetupProbe] enemyDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataInternalName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataInternalName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyDataPersistentID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.EnemyDataPersistentID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; instanceName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.InstanceName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; spawnDataMode="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; groupReplicatorKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; activeSessionAtEntry="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(activeSessionAtEntry); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; activeSessionNow="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(IsAlarmSessionOpen); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; sourceAtEntry="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(sourceAtEntry); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; currentSource="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; targetTracker="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(targetTrackerName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; agentKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(record.AgentKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dedupeSkipped="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(dedupeSkipped); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; ignored="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ignored); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; tracked="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tracked); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; decision="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(decision); } logSource.LogInfo(val); } } private static void ExtendAlarmSessionFromAgentSetup(MethodBase originalMethod) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown float alarmSessionEndTime = AlarmSessionEndTime; AlarmSessionEndTime = Math.Max(AlarmSessionEndTime, Time.time + 8f); if (DebugMode && AlarmSessionEndTime > alarmSessionEndTime) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(61, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Alarm session extended until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AlarmSessionEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource.LogInfo(val); } } private static void OpenOrExtendClientFallbackSession(MethodBase originalMethod) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown float num = Time.time + 8f; bool flag = default(bool); if (!IsAlarmSessionOpen) { IsAlarmSessionOpen = true; AlarmSessionEndTime = num; CurrentSessionSpawnSource = SpawnSource.SurvivalWave; ActiveAgentCaptureRecords.Clear(); CapturedAgentKeys.Clear(); UpdateAlarmHud(); if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(69, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Client fallback session opened until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AlarmSessionEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource.LogInfo(val); } return; } float alarmSessionEndTime = AlarmSessionEndTime; AlarmSessionEndTime = Math.Max(AlarmSessionEndTime, num); if (DebugMode && AlarmSessionEndTime > alarmSessionEndTime) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(71, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Client fallback session extended until "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AlarmSessionEndTime, "0.00"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": source="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(CurrentSessionSpawnSource); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(originalMethod)); } logSource2.LogInfo(val); } } private static bool LooksLikeDynamicSpawnFromSetup(object agent, object spawnData, string enemyDataID, string enemyDataName) { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown bool flag = default(bool); try { MarkStartupTime(); if (Time.time - StartupTime < 20f) { LogClientFallbackDecision("rejected: warmup"); return false; } string text = ReadSpawnDataMode(spawnData); if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(46, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] spawnData mode detected: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); } logSource.LogInfo(val); } if (ContainsAny(text, "Hibernate", "Hibernating")) { LogClientFallbackDecision("rejected: hibernate"); return false; } if (!ContainsAny(text, "Agressive", "Aggressive")) { LogClientFallbackDecision("rejected: mode not aggressive"); return false; } bool flag2 = IsKnownDynamicEnemyID(enemyDataID); string text2 = AgentCaptureRecord.FirstKnown(enemyDataName, GetEnemyDisplayName(agent)); bool flag3 = ContainsAny(text2, "_Wave", "Bullrush"); if (DebugMode) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(107, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Client fallback dynamic check: enemyDataID="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(enemyDataID); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; idLooksDynamic="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; name="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; nameLooksDynamic="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag3); } logSource2.LogInfo(val); } if (!flag2 && !flag3) { LogClientFallbackDecision("rejected: name not dynamic"); return false; } LogClientFallbackDecision("accepted"); return true; } catch (Exception ex) { if (DebugMode) { ManualLogSource logSource3 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(60, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] LooksLikeDynamicSpawnFromSetup failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource3.LogInfo(val); } return false; } } private static bool IsKnownDynamicEnemyID(string enemyDataID) { if (string.IsNullOrWhiteSpace(enemyDataID)) { return false; } if (!uint.TryParse(enemyDataID.Trim(), out var result)) { return KnownDynamicEnemyIDs.Contains(enemyDataID.Trim()); } return KnownDynamicEnemyIDs.Contains(result.ToString()); } internal static void MarkStartupTime() { if (StartupTime < 0f) { StartupTime = Time.time; } } private static void LogClientFallbackDecision(string decision) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(37, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Client fallback "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(decision); } logSource.LogInfo(val); } } private static string ReadSpawnDataMode(object spawnData) { if (spawnData == null) { return ""; } string[] array = new string[5] { "mode", "Mode", "m_mode", "debugEnemyMode", "DebugEnemyMode" }; foreach (string memberName in array) { object obj = TryGetMemberValue(spawnData, memberName); if (obj != null) { return FormatValueSummary(obj); } } return ""; } private static bool ContainsAny(string value, params string[] needles) { if (string.IsNullOrWhiteSpace(value)) { return false; } return needles.Any((string needle) => value.Contains(needle, StringComparison.OrdinalIgnoreCase)); } private static void LogAgentCaptureSummary() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown if (ActiveAgentCaptureRecords.Count == 0) { if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Alarm capture closed with 0 EnemyAgent.Setup captures."); } if (DynamicSpawnTracker.TotalSpawned == 0 && BloodDoorSpawnTracker.TotalSpawned == 0) { AlarmSpawnTicker.HideAlarmSpawnMessage(); AlarmSpawnTicker.HideBloodDoorSpawnMessage(); } return; } IGrouping[] array = (from record in ActiveAgentCaptureRecords group record by record.GroupKey into @group orderby @group.Key select @group).ToArray(); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(30, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ActiveAgentCaptureRecords.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" enemies spawned"); } logSource.LogInfo(val); IGrouping[] array2 = array; foreach (IGrouping grouping in array2) { ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(4, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("- "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(grouping.Key); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(grouping.Count()); } logSource2.LogInfo(val); } if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Alarm spawn final log emitted"); } } internal static void ProcessTrackedAlarmEnemyAliveStates() { if (EnemyTrackerByKey.Count != 0 && !(Time.time < NextAliveRefreshTime)) { RefreshTrackedAlarmEnemyAliveStates(); } } internal static void ProcessDynamicAutoHide() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown if (!DynamicAutoHideEnabled || DynamicSpawnTracker.TypeEntryCount <= 0) { return; } float dynamicAutoHideAfterSeconds = DynamicAutoHideAfterSeconds; float time = Time.time; SpawnTypeEntry[] array = DynamicSpawnTracker.GetPrunableTypeEntries(time, dynamicAutoHideAfterSeconds).ToArray(); bool flag2 = default(bool); foreach (SpawnTypeEntry spawnTypeEntry in array) { int total = spawnTypeEntry.Total; int alive = spawnTypeEntry.Alive; float num = time - spawnTypeEntry.ZeroAliveTime; string[] array2 = DynamicSpawnTracker.PruneTypeEntry(spawnTypeEntry.TypeKey); foreach (string key in array2) { EnemyTrackerByKey.Remove(key); } bool flag = DynamicSpawnTracker.TypeEntryCount == 0; UpdateAlarmHud(); if (DebugSourceModeEnabled) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(281, 12, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[AST DynamicAutoHide] enabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DynamicAutoHideEnabled); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; timeoutSeconds="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(dynamicAutoHideAfterSeconds, "0.##"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; enemyTypeKey="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnTypeEntry.TypeKey); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; displayName="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnTypeEntry.DisplayName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; typeTotalBefore="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(total); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; typeAliveBefore="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(alive); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; zeroAliveTimeValid="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(spawnTypeEntry.ZeroAliveTimeValid); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; secondsSinceZeroAlive="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num, "0.##"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; removedEntry=True; remainingTypeCount="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DynamicSpawnTracker.TypeEntryCount); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dynamicTotalAfter="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DynamicSpawnTracker.TotalSpawned); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; dynamicAliveAfter="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DynamicSpawnTracker.AliveCount); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; hiddenBox="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; reason=type zero alive timeout"); } logSource.LogInfo(val); } } } private static void RefreshTrackedAlarmEnemyAliveStates() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown NextAliveRefreshTime = Time.time + 0.25f; bool flag = false; bool flag3 = default(bool); foreach (SpawnTracker tracker in GetTrackers()) { TrackedAlarmEnemy[] array = tracker.Enemies.ToArray(); foreach (TrackedAlarmEnemy trackedAlarmEnemy in array) { bool isAlive = trackedAlarmEnemy.IsAlive; bool flag2 = IsTrackedEnemyAlive(trackedAlarmEnemy); if (isAlive == flag2) { continue; } if (!flag2) { tracker.TryMarkDead(trackedAlarmEnemy.UniqueKey); } else { tracker.TryMarkAlive(trackedAlarmEnemy.UniqueKey); } flag = true; if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(63, 4, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Enemy alive state changed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(trackedAlarmEnemy.DisplayName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(isAlive); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; tracker="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(tracker.Name); } logSource.LogInfo(val); } } } if (BloodDoorSpawnTracker.TotalSpawned > 0 && BloodDoorSpawnTracker.AliveCount <= 0) { ClearTracker(BloodDoorSpawnTracker); IsSpawnDataBloodDoorRouteExclusive = false; AlarmSpawnTicker.HideBloodDoorSpawnMessage(); flag = true; } if (flag) { UpdateAlarmHud(); if (DebugMode) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn Teller] Alive count updated"); } } } private static void UpdateAlarmHud() { List list; if (DynamicSpawnTracker.TotalSpawned == 0) { AlarmSpawnTicker.HideAlarmSpawnMessage(); } else { list = new List(); list.Add(Localizer.GetText("SourceSummaryLine", Localizer.GetSourceDisplayName(SpawnSource.SurvivalWave), DynamicSpawnTracker.AliveCount, DynamicSpawnTracker.TotalSpawned)); List list2 = list; list2.AddRange(from entry in DynamicSpawnTracker.TypeEntries orderby entry.DisplayName select Localizer.GetText("EnemyCountLine", entry.DisplayName, entry.Alive, entry.Total)); AlarmSpawnTicker.ShowActiveAlarmSpawnMessage(string.Join("\n", list2)); } if (BloodDoorSpawnTracker.AliveCount <= 0) { AlarmSpawnTicker.HideBloodDoorSpawnMessage(); return; } list = new List(); list.Add(Localizer.GetText("BloodDoorSpawns")); list.Add(Localizer.GetText("BloodDoorAliveLine", BloodDoorSpawnTracker.AliveCount, BloodDoorSpawnTracker.TotalSpawned)); List list3 = list; list3.AddRange(from tracked in BloodDoorSpawnTracker.Enemies group tracked by tracked.DisplayName into @group orderby @group.Key select Localizer.GetText("EnemyCountLine", @group.Key, @group.Count((TrackedAlarmEnemy tracked) => tracked.IsAlive), @group.Count())); AlarmSpawnTicker.ShowBloodDoorSpawnMessage(string.Join("\n", list3)); } private static int GetSpawnSourceSortOrder(SpawnSource source) { return source switch { SpawnSource.DoorAlarm => 0, SpawnSource.SurvivalWave => 1, SpawnSource.ErrorAlarm => 2, SpawnSource.ScoutScream => 3, _ => 4, }; } private static void UpdateTrackedEnemy(AgentCaptureRecord record) { if (record != null && !string.IsNullOrWhiteSpace(record.AgentKey) && EnemyTrackerByKey.TryGetValue(record.AgentKey, out var value)) { value.UpdateEnemy(record); } } private static IEnumerable GetTrackers() { yield return DynamicSpawnTracker; yield return BloodDoorSpawnTracker; } private static SpawnTracker GetTrackerForSource(SpawnSource source) { if (GetSpawnSourceKind(source) != SpawnSourceKind.BloodDoor) { return DynamicSpawnTracker; } return BloodDoorSpawnTracker; } private static SpawnSourceKind GetSpawnSourceKind(SpawnSource source) { if (source != SpawnSource.BloodDoor) { return SpawnSourceKind.Dynamic; } return SpawnSourceKind.BloodDoor; } private static int ClearTracker(SpawnTracker tracker) { if (tracker == null) { return 0; } string[] array = tracker.Enemies.Select((TrackedAlarmEnemy enemy) => enemy.UniqueKey).ToArray(); string[] array2 = array; foreach (string key in array2) { EnemyTrackerByKey.Remove(key); } tracker.Clear(); return array.Length; } private static bool IsTrackedEnemyAlive(TrackedAlarmEnemy tracked) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown if (tracked == null || tracked.Agent == null) { return false; } try { if (TryReadBoolMember(tracked.Agent, out var result, "Alive", "IsAlive", "alive", "isAlive", "m_alive", "m_isAlive")) { return result; } if (TryReadBoolMember(tracked.Agent, out var result2, "Dead", "IsDead", "dead", "isDead", "m_dead", "m_isDead")) { return !result2; } } catch (Exception ex) { if (DebugMode && !HasLoggedAliveProbeFailure) { HasLoggedAliveProbeFailure = true; ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Enemy alive state probe failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogInfo(val); } } return tracked.IsAlive; } private static bool TryReadBoolMember(object source, out bool result, params string[] memberNames) { result = false; foreach (string memberName in memberNames) { object obj = TryGetMemberValue(source, memberName); if (obj is bool flag) { result = flag; return true; } if (obj != null && bool.TryParse(obj.ToString(), out var result2)) { result = result2; return true; } } return false; } private static string FormatEnemyNameForHud(string groupKey) { return Localizer.GetEnemyDisplayName(groupKey); } private static string FormatEnemyNameForHud(string enemyDataID, string groupKey) { return Localizer.GetEnemyDisplayName(enemyDataID, groupKey); } private static void LogCaptureSummary() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Capture] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ActiveCaptureRecords.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" spawnData captured"); } logSource.LogInfo(val); foreach (IGrouping item in from record in ActiveCaptureRecords group record by record.PersistentGameDataID ?? "" into @group orderby @group.Key select @group) { ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(25, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("- persistentGameDataID "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item.Key); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item.Count()); } logSource2.LogInfo(val); } } private static void SnapshotExistingEnemies(string reason) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if (!DebugMode) { return; } object[] array = FindEnemyAgents(); int num = 0; object[] array2 = array; for (int i = 0; i < array2.Length; i++) { if (TryGetInstanceId(array2[i], out var instanceId) && KnownEnemyIds.Add(instanceId)) { num++; } } ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(50, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Tracked "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(KnownEnemyIds.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" existing EnemyAgent instances ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(reason); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", added "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")."); } logSource.LogInfo(val); } private static void ScheduleDelayedScan() { if (DebugMode) { AlarmSpawnTicker.ScheduleScan(0.2f); } } internal static void ScanForNewEnemies() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown Dictionary dictionary = new Dictionary(); int num = 0; object[] array = FindEnemyAgents(); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent total: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(array.Length); } logSource.LogInfo(val); object[] array2 = array; foreach (object obj in array2) { if (TryGetInstanceId(obj, out var instanceId) && KnownEnemyIds.Add(instanceId)) { num++; string enemyDisplayName = GetEnemyDisplayName(obj); dictionary.TryGetValue(enemyDisplayName, out var value); dictionary[enemyDisplayName] = value + 1; } } if (num == 0) { Plugin.LogSource.LogInfo((object)"[Alarm Spawn] 0 enemies spawned"); return; } ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(30, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" enemies spawned"); } logSource2.LogInfo(val); foreach (KeyValuePair item in from pair in dictionary orderby pair.Value descending, pair.Key select pair) { ManualLogSource logSource3 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(4, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("- "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item.Key); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item.Value); } logSource3.LogInfo(val); } } private static object[] FindEnemyAgents() { if (EnemyAgentType == null) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] EnemyAgent type unavailable; cannot enumerate EnemyAgent instances."); return Array.Empty(); } LogEnemyAgentShape(); List list = new List(); list.AddRange(FindEnemyAgentsWithUnityMethod("FindObjectsOfType", FindObjectsOfTypeMethod)); list.AddRange(FindEnemyAgentsWithUnityMethod("Resources.FindObjectsOfTypeAll", FindObjectsOfTypeAllMethod)); list.AddRange(FindEnemyAgentsFromEnemyUpdateManager()); return DistinctEnemies(list); } private static object[] FindEnemyAgentsWithUnityMethod(string label, MethodInfo method) { //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown bool flag = default(bool); if (method == null) { ManualLogSource logSource = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(54, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent scan via "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(label); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": unavailable"); } logSource.LogWarning(val); return Array.Empty(); } try { object obj = method.Invoke(null, new object[1] { EnemyAgentType }); if (obj is Array source) { object[] array = (from object enemy in source where enemy != null select enemy).ToArray(); ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(43, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] EnemyAgent scan via "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(label); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(array.Length); } logSource2.LogInfo(val2); return array; } ManualLogSource logSource3 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(52, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent scan via "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(label); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": returned "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueType(obj)); } logSource3.LogWarning(val); } catch (Exception ex) { ManualLogSource logSource4 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(50, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent scan via "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(label); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource4.LogWarning(val); } return Array.Empty(); } private static object[] FindEnemyAgentsFromEnemyUpdateManager() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown if (EnemyUpdateManagerType == null) { Plugin.LogSource.LogWarning((object)"[Alarm Spawn Teller] EnemyUpdateManager diagnostics skipped: type unavailable."); return Array.Empty(); } object[] array = FindEnemyUpdateManagerInstances(); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(61, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyUpdateManager instance candidates: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(array.Length); } logSource.LogInfo(val); List list = new List(); object[] array2 = array; foreach (object manager in array2) { list.AddRange(LogEnemyUpdateManagerMembers(manager)); } ManualLogSource logSource2 = Plugin.LogSource; val = new BepInExInfoLogInterpolatedStringHandler(72, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent scan via EnemyUpdateManager candidates: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(list.Count); } logSource2.LogInfo(val); return list.Where((object enemy) => enemy != null).ToArray(); } private static object[] FindEnemyUpdateManagerInstances() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown List list = new List(); PropertyInfo[] properties = EnemyUpdateManagerType.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); bool flag = default(bool); foreach (PropertyInfo propertyInfo in properties) { if (!IsInstanceMemberName(propertyInfo.Name) || propertyInfo.GetIndexParameters().Length != 0) { continue; } try { object value = propertyInfo.GetValue(null); ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(81, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyUpdateManager static property candidate: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(propertyInfo.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", value type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueType(value)); } logSource.LogInfo(val); AddIfEnemyUpdateManager(list, value); } catch (Exception ex) { ManualLogSource logSource2 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(73, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] Failed to read EnemyUpdateManager static property "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(propertyInfo.Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } logSource2.LogWarning(val2); } } FieldInfo[] fields = EnemyUpdateManagerType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!IsInstanceMemberName(fieldInfo.Name)) { continue; } try { object value2 = fieldInfo.GetValue(null); ManualLogSource logSource3 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(78, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyUpdateManager static field candidate: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(fieldInfo.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", value type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueType(value2)); } logSource3.LogInfo(val); AddIfEnemyUpdateManager(list, value2); } catch (Exception ex2) { ManualLogSource logSource4 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(70, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[Alarm Spawn Teller] Failed to read EnemyUpdateManager static field "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(fieldInfo.Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex2.Message); } logSource4.LogWarning(val2); } } return DistinctObjects(list); } private static object[] LogEnemyUpdateManagerMembers(object manager) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown List list = new List(); FieldInfo[] fields = EnemyUpdateManagerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); bool flag = default(bool); foreach (FieldInfo fieldInfo in fields) { if (!IsEnemyCollectionCandidateName(fieldInfo.Name)) { continue; } try { object value = fieldInfo.GetValue(manager); LogCandidateCollection("field: " + fieldInfo.Name, value, list); } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(63, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Failed to read EnemyUpdateManager field "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(fieldInfo.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } } PropertyInfo[] properties = EnemyUpdateManagerType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (!IsEnemyCollectionCandidateName(propertyInfo.Name) || propertyInfo.GetIndexParameters().Length != 0) { continue; } try { object value2 = propertyInfo.GetValue(manager); LogCandidateCollection("property: " + propertyInfo.Name, value2, list); } catch (Exception ex2) { ManualLogSource logSource2 = Plugin.LogSource; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(66, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Failed to read EnemyUpdateManager property "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(propertyInfo.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex2.Message); } logSource2.LogWarning(val); } } return list.ToArray(); } private static void LogCandidateCollection(string label, object value, List enemies) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown int? count; string[] elementTypes; List source = TryEnumerateElements(value, out count, out elementTypes); int num = source.Count(IsEnemyAgentLike); ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(106, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyUpdateManager candidate "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(label); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", value type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueType(value)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", count: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatCount(count)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", element types: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatElementTypes(elementTypes)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", enemy agents: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); } logSource.LogInfo(val); enemies.AddRange(source.Where(IsEnemyAgentLike)); } private static List TryEnumerateElements(object value, out int? count, out string[] elementTypes) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown count = TryReadCount(value); List list = new List(); if (value == null) { elementTypes = Array.Empty(); return list; } if (value is Array array) { list.AddRange(from object element in array where element != null select element); int valueOrDefault = count.GetValueOrDefault(); if (!count.HasValue) { valueOrDefault = array.Length; count = valueOrDefault; } elementTypes = GetElementTypes(list); return list; } if (value is IEnumerable enumerable && !(value is string)) { try { foreach (object item in enumerable) { if (item != null) { list.Add(item); } } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(54, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Failed to enumerate collection "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatValueType(value)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } } elementTypes = GetElementTypes(list); return list; } private static int? TryReadCount(object value) { if (value == null) { return null; } string[] array = new string[6] { "Count", "count", "Length", "length", "size", "_size" }; foreach (string memberName in array) { object obj = TryGetMemberValue(value, memberName); if (obj is int) { return (int)obj; } } return null; } private static bool IsEnemyAgentLike(object value) { if (value == null || EnemyAgentType == null) { return false; } Type type = value.GetType(); if (!EnemyAgentType.IsAssignableFrom(type) && !string.Equals(type.FullName, EnemyAgentType.FullName, StringComparison.Ordinal)) { return type.FullName?.Contains("EnemyAgent", StringComparison.OrdinalIgnoreCase) ?? false; } return true; } private static object[] DistinctEnemies(IEnumerable enemies) { List list = new List(); HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (object item in enemies.Where((object enemy) => enemy != null)) { if (TryGetInstanceId(item, out var instanceId)) { if (hashSet.Add(instanceId)) { list.Add(item); } } else if (hashSet2.Add(item.GetHashCode())) { list.Add(item); } } return list.ToArray(); } private static object[] DistinctObjects(IEnumerable values) { HashSet hashSet = new HashSet(); List list = new List(); foreach (object item in values.Where((object value) => value != null)) { if (hashSet.Add(item.GetHashCode())) { list.Add(item); } } return list.ToArray(); } private static void AddIfEnemyUpdateManager(List managers, object value) { if (value != null) { Type type = value.GetType(); if (EnemyUpdateManagerType.IsAssignableFrom(type) || string.Equals(type.FullName, EnemyUpdateManagerType.FullName, StringComparison.Ordinal)) { managers.Add(value); } } } private static bool IsInstanceMemberName(string name) { if (!name.Equals("Current", StringComparison.OrdinalIgnoreCase) && !name.Equals("Instance", StringComparison.OrdinalIgnoreCase) && !name.Equals("s_instance", StringComparison.OrdinalIgnoreCase) && !name.Equals("m_instance", StringComparison.OrdinalIgnoreCase) && !name.Contains("instance", StringComparison.OrdinalIgnoreCase)) { return name.Contains("current", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsEnemyCollectionCandidateName(string name) { string[] array = new string[8] { "enemy", "enemies", "agent", "agents", "node", "update", "member", "list" }; foreach (string value in array) { if (name.Contains(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void LogEnemyAgentShape() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown if (HasLoggedEnemyAgentShape || EnemyAgentType == null) { return; } HasLoggedEnemyAgentShape = true; bool flag = default(bool); foreach (FieldInfo item in from field in EnemyAgentType.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where IsEnemyAgentShapeCandidateName(field.Name) select field) { ManualLogSource logSource = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(57, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent field candidate: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatTypeName(item.FieldType)); } logSource.LogInfo(val); } foreach (PropertyInfo item2 in from property in EnemyAgentType.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where IsEnemyAgentShapeCandidateName(property.Name) select property) { ManualLogSource logSource2 = Plugin.LogSource; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(60, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent property candidate: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(item2.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", type: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatTypeName(item2.PropertyType)); } logSource2.LogInfo(val); } } private static bool IsEnemyAgentShapeCandidateName(string name) { string[] array = new string[6] { "EnemyData", "Data", "name", "type", "prefab", "archetype" }; foreach (string value in array) { if (name.Contains(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static string[] GetElementTypes(IEnumerable elements) { return (from element in elements where element != null select element.GetType().FullName ?? element.GetType().Name).Distinct().Take(8).ToArray(); } private static string FormatValueType(object value) { object obj; if (value != null) { obj = value.GetType().FullName; if (obj == null) { return value.GetType().Name; } } else { obj = ""; } return (string)obj; } private static string FormatValueSummary(object value) { if (value == null) { return ""; } Type type = value.GetType(); if (type.IsPrimitive || type.IsEnum || value is string || value is decimal) { return value.ToString(); } return $"{FormatValueType(value)}:{value}"; } private static string FormatObjectMembersSummary(object value) { if (value == null) { return ""; } List list = new List(); Type type = value.GetType(); foreach (FieldInfo item in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Take(16)) { try { list.Add(item.Name + "=" + FormatValueSummary(item.GetValue(value))); } catch (Exception ex) { list.Add(item.Name + "="); } } foreach (PropertyInfo item2 in (from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where property.GetIndexParameters().Length == 0 select property).Take(16)) { if (list.Count >= 24) { break; } try { list.Add(item2.Name + "=" + FormatValueSummary(item2.GetValue(value))); } catch (Exception ex2) { list.Add(item2.Name + "="); } } if (list.Count == 0) { return FormatValueType(value) + " has no readable fields/properties"; } return FormatValueType(value) + " { " + string.Join("; ", list) + " }"; } private static string FormatFocusedObjectMembersSummary(object value) { if (value == null) { return ""; } List list = new List(); string[] array = new string[12] { "enemyData", "enemyDataID", "persistentGameDataID", "persistentID", "populationID", "enemyID", "type", "name", "group", "mode", "spawn", "debug" }; foreach (string keyword in array) { AddFocusedMemberSummaries(value, keyword, list); } if (list.Count == 0) { return FormatObjectMembersSummary(value); } return FormatValueType(value) + " { " + string.Join("; ", list.Distinct()) + " }"; } private static void AddFocusedMemberSummaries(object value, string keyword, List summaries) { Type type = value.GetType(); foreach (FieldInfo item in from field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where field.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase) select field) { try { summaries.Add(item.Name + "=" + FormatValueSummary(item.GetValue(value))); } catch (Exception ex) { summaries.Add(item.Name + "="); } } foreach (PropertyInfo item2 in from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where property.GetIndexParameters().Length == 0 && property.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase) select property) { try { summaries.Add(item2.Name + "=" + FormatValueSummary(item2.GetValue(value))); } catch (Exception ex2) { summaries.Add(item2.Name + "="); } } } private static string FormatCount(int? count) { if (!count.HasValue) { return ""; } return count.Value.ToString(); } private static string FormatElementTypes(string[] elementTypes) { if (elementTypes.Length != 0) { return string.Join(", ", elementTypes); } return ""; } private static bool TryGetInstanceId(object unityObject, out int instanceId) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown instanceId = 0; if (GetInstanceIdMethod == null) { return false; } try { if (GetInstanceIdMethod.Invoke(unityObject, Array.Empty()) is int num) { instanceId = num; return true; } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(38, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to read EnemyAgent InstanceID: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } return false; } private static string GetAgentInstanceKey(object instance) { if (instance != null) { object obj = TryGetMemberValue(instance, "Pointer") ?? TryGetMemberValue(instance, "m_CachedPtr"); if (obj != null) { string text = obj.ToString(); if (!string.IsNullOrWhiteSpace(text) && text != "0") { return "ptr:" + text; } } if (!TryGetInstanceId(instance, out var instanceId)) { int hashCode = RuntimeHelpers.GetHashCode(instance); if (hashCode == 0) { return $"fallback:{++NextFallbackAgentKey}"; } return $"runtime:{hashCode}"; } return $"instance:{instanceId}"; } return $"fallback-null-{++NextFallbackAgentKey}"; } private static string GetEnemyDisplayName(object enemy) { if (enemy == null) { return ""; } object obj = TryGetMemberValue(enemy, "EnemyData") ?? TryGetMemberValue(enemy, "enemyData") ?? TryGetMemberValue(enemy, "m_enemyData") ?? TryGetMemberValue(enemy, "EnemyDataBlock") ?? TryGetMemberValue(enemy, "m_enemyDataBlock"); if (obj != null) { string text = TryReadReadableName(obj); if (!string.IsNullOrWhiteSpace(text)) { return text; } } string text2 = TryGetUnityObjectName(enemy); if (!string.IsNullOrWhiteSpace(text2)) { return text2 + " (fallback: Unity object name)"; } return (enemy.GetType().FullName ?? enemy.GetType().Name) + " (fallback: runtime type)"; } private static object TryGetMemberValue(object source, string memberName) { if (source == null) { return null; } Type type = source.GetType(); try { PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)propertyInfo == null) { propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); } if (propertyInfo != null && propertyInfo.GetIndexParameters().Length == 0) { return propertyInfo.GetValue(source); } FieldInfo fieldInfo = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)fieldInfo == null) { fieldInfo = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); } if (fieldInfo != null) { return fieldInfo.GetValue(source); } } catch { } return null; } private static string TryReadReadableName(object source) { string[] array = new string[7] { "Name", "name", "publicName", "PublicName", "m_name", "persistentID", "PersistentID" }; foreach (string memberName in array) { object obj = TryGetMemberValue(source, memberName); if (obj != null) { return obj.ToString(); } } return source.GetType().FullName ?? source.GetType().Name; } private static string TryGetUnityObjectName(object unityObject) { if (unityObject == null) { return null; } try { return UnityObjectNameProperty?.GetValue(unityObject)?.ToString(); } catch { return null; } } private static string FormatMethodSignature(MethodBase method) { IEnumerable values = from parameter in method.GetParameters() select FormatTypeName(parameter.ParameterType) + " " + parameter.Name; string value = ((method is MethodInfo methodInfo) ? FormatTypeName(methodInfo.ReturnType) : "void"); return $"{value} {method.DeclaringType?.FullName}.{method.Name}({string.Join(", ", values)})"; } private static string FormatTypeName(Type type) { if (!type.IsGenericType) { return type.FullName ?? type.Name; } string text = type.FullName ?? type.Name; int num = text.IndexOf('`'); if (num >= 0) { text = text.Substring(0, num); } IEnumerable values = type.GetGenericArguments().Select(FormatTypeName); return text + "<" + string.Join(", ", values) + ">"; } private static void Prefix(MethodBase __originalMethod, object[] __args, out SpawnPatchState __state) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown __state = new SpawnPatchState(); if (TryGetSpawnDataArgument(__originalMethod, __args, out var spawnData)) { CaptureSpawnData(spawnData); } if (TryGetParameterValue(__originalMethod, __args, IsEnemyGroupSpawnDataParameter, out var _, out var value)) { __state.SpawnDataRoute = ResolveSpawnDataRoute(value); ApplySpawnDataRoute(__state.SpawnDataRoute); } bool flag = IsAlarmWaveSpawn(__originalMethod, __args); object survivalWaveArgument = GetSurvivalWaveArgument(__originalMethod, __args); SpawnSource spawnSource = InferSpawnSource(__originalMethod, __args); if (spawnSource == SpawnSource.BloodDoor) { OpenOrExtendAlarmSession(__originalMethod, __args); } if (survivalWaveArgument == null) { return; } if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; bool flag2 = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Spawn prefix hit: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(__originalMethod)); } logSource.LogInfo(val); } if (flag && spawnSource != SpawnSource.BloodDoor) { OpenOrExtendAlarmSession(__originalMethod, __args); } if (flag && !AlarmSpawnTicker.HasPendingScans()) { SnapshotExistingEnemies("before alarm spawn"); } } private static void Postfix(MethodBase __originalMethod, object[] __args, SpawnPatchState __state) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown LogSourceInvestigation(__originalMethod, __args, __state); LogSpawnDiagnostic(__originalMethod, __args); if (GetSurvivalWaveArgument(__originalMethod, __args) == null) { return; } if (DebugMode) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(40, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] Spawn postfix hit: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(FormatMethodSignature(__originalMethod)); } logSource.LogInfo(val); } if (IsAlarmWaveSpawn(__originalMethod, __args)) { ScheduleDelayedScan(); } } private static void EnemyAgentSetupPostfix(object __instance, MethodBase __originalMethod, object[] __args) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown try { CaptureEnemyAgentSetup(__instance, __originalMethod, __args); } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(54, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Alarm Spawn Teller] EnemyAgent.Setup capture failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logSource.LogWarning(val); } } } }