Decompiled source of BossRules v1.0.6

BossRules.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using SoftReferenceableAssets;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BossRules")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("BossRules")]
[assembly: AssemblyCopyright("Copyright (c) 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("E72C88F7-8DC7-45B4-A56E-5E25F182EF28")]
[assembly: AssemblyFileVersion("1.0.6")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BossRules
{
	[BepInPlugin("sighsorry.BossRules", "BossRules", "1.0.6")]
	public sealed class BossRulesPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private sealed class ConfigurationManagerAttributes
		{
			public int? Order;
		}

		internal const string ModName = "BossRules";

		internal const string ModVersion = "1.0.6";

		internal const string Author = "sighsorry";

		internal const string ModGUID = "sighsorry.BossRules";

		internal const string AltarYamlFileName = "BossRules.altar.yml";

		internal const string AltarReferenceYamlFileName = "BossRules.altar.reference.yml";

		internal const string RulesYamlFileName = "BossRules.yml";

		internal const string ForsakenPowersYamlFileName = "BossRules.forsakenPowers.yml";

		private const float FileReloadDebounceSeconds = 0.25f;

		internal static readonly ManualLogSource BossRulesLogger = Logger.CreateLogSource("BossRules");

		private static ConfigSync? _configSync;

		private readonly Harmony _harmony = new Harmony("sighsorry.BossRules");

		private CustomSyncedValue<string> _syncedAltarYaml;

		private CustomSyncedValue<string> _syncedRulesYaml;

		private CustomSyncedValue<string> _syncedForsakenPowersYaml;

		private FileSystemWatcher? _watcher;

		private float _reloadDueAt = -1f;

		private int _altarYamlReloadRequested;

		private int _rulesYamlReloadRequested;

		private int _forsakenPowersYamlReloadRequested;

		private bool _reloadAltarYaml;

		private bool _reloadRulesYaml;

		private bool _reloadForsakenPowersYaml;

		private ConfigEntry<Toggle> _lockConfiguration;

		internal static ConfigSync ConfigSync => _configSync ?? throw new InvalidOperationException("ServerSync has not been initialized yet.");

		internal static string ConfigDirectoryPath => Path.Combine(Paths.ConfigPath, "BossRules");

		internal static string AltarYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.altar.yml");

		internal static string AltarReferenceYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.altar.reference.yml");

		internal static string RulesYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.yml");

		internal static string ForsakenPowersYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.forsakenPowers.yml");

		internal static bool IsSourceOfTruth => ConfigSync.IsSourceOfTruth;

		internal static bool IsRuntimeServer()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}

		private void Awake()
		{
			EnsureServerSyncInitialized();
			Directory.CreateDirectory(ConfigDirectoryPath);
			AltarConfigurationFiles.EnsureDefaultFiles();
			BossRuleConfigurationFiles.EnsureDefaultFile();
			ForsakenPowerConfigurationFiles.EnsureDefaultFile();
			BindConfiguration();
			_syncedAltarYaml = new CustomSyncedValue<string>(ConfigSync, "altar-yaml", "", 50);
			_syncedAltarYaml.ValueChanged += HandleSyncedAltarYamlChanged;
			_syncedRulesYaml = new CustomSyncedValue<string>(ConfigSync, "rules-yaml", "", 60);
			_syncedRulesYaml.ValueChanged += HandleSyncedRulesYamlChanged;
			_syncedForsakenPowersYaml = new CustomSyncedValue<string>(ConfigSync, "forsaken-powers-yaml", "", 65);
			_syncedForsakenPowersYaml.ValueChanged += HandleSyncedForsakenPowersYamlChanged;
			ConfigSync.SourceOfTruthChanged += HandleSourceOfTruthChanged;
			LoadLocalAltarYamlAndPublish("startup");
			LoadLocalRulesYamlAndPublish("startup");
			LoadLocalForsakenPowersYamlAndPublish("startup");
			_harmony.PatchAll(typeof(BossRulesPlugin).Assembly);
			Localizer.Initialize(this);
			BossStonePerPlayerRuntime.EnsureRpcRegistered();
			DespawnRulesManager.EnsureMessageRpcRegistered();
			BossRulesConsoleCommands.Register();
			InitializeWatcher();
			((BaseUnityPlugin)this).Config.Save();
		}

		private void Update()
		{
			ProcessQueuedYamlReload();
			Localizer.ProcessDeferredLoad();
			DataForgeStatusEffectBridge.ProcessDeferredSubscription();
			AltarRuntime.ProcessPendingAltarSummonMarkers();
			AltarRuntime.ProcessPendingQueenDungeonRooms();
			AltarRuntime.ProcessDeferredReapply();
			AltarReferenceGenerator.TryAutoRefreshReferenceConfigurationFile();
			ForsakenPowerRuntime.ProcessDeferredApply();
			BossStonePerPlayerRuntime.EnsureRpcRegistered();
			BossStonePerPlayerRuntime.ProcessPendingSacrificeRequests();
			BossStonePerPlayerRuntime.ProcessPendingResetRequests();
			DespawnRulesManager.EnsureMessageRpcRegistered();
			DespawnRulesManager.ExecuteServerTick();
			BossTamedPressureRuntime.ExecuteServerTick();
		}

		private void OnDestroy()
		{
			if (_configSync != null)
			{
				ConfigSync.SourceOfTruthChanged -= HandleSourceOfTruthChanged;
			}
			_syncedAltarYaml.ValueChanged -= HandleSyncedAltarYamlChanged;
			_syncedRulesYaml.ValueChanged -= HandleSyncedRulesYamlChanged;
			_syncedForsakenPowersYaml.ValueChanged -= HandleSyncedForsakenPowersYamlChanged;
			_watcher?.Dispose();
			_watcher = null;
			Interlocked.Exchange(ref _altarYamlReloadRequested, 0);
			Interlocked.Exchange(ref _rulesYamlReloadRequested, 0);
			Interlocked.Exchange(ref _forsakenPowersYamlReloadRequested, 0);
			ClearPendingYamlReloads();
			AltarRuntime.Shutdown();
			BossStonePerPlayerRuntime.Shutdown();
			DespawnRulesManager.ShutdownMessages();
			ForsakenPowerSelectionRuntime.Shutdown();
			AltarReferenceGenerator.ShutdownAutoRefresh();
			BossRulesManager.ClearRuntimeState();
			BossRulesRuntime.Reset();
			ForsakenPowerRuntime.Reset();
			DataForgeStatusEffectBridge.Shutdown();
			Localizer.Shutdown();
			_harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Config.Save();
		}

		private static void EnsureServerSyncInitialized()
		{
			if (_configSync == null)
			{
				_configSync = new ConfigSync("sighsorry.BossRules")
				{
					DisplayName = "BossRules",
					CurrentVersion = "1.0.6",
					MinimumRequiredVersion = "1.0.6"
				};
			}
		}

		private void BindConfiguration()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				_lockConfiguration = BindConfigEntry("1 - General", "Lock Configuration", Toggle.On, "If on, synced configuration can be changed by server admins only.", synchronizedSetting: true, 200);
				BossRulesConfig.Bind(this);
				ConfigSync.AddLockingConfigEntry<Toggle>(_lockConfiguration);
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		internal ConfigEntry<T> BindConfigEntry<T>(string group, string name, T value, string description, bool synchronizedSetting = true, int? configManagerOrder = null)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), (AcceptableValueBase)null, BuildConfigDescriptionTags(configManagerOrder));
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private static object[] BuildConfigDescriptionTags(int? configManagerOrder)
		{
			if (!configManagerOrder.HasValue)
			{
				return Array.Empty<object>();
			}
			return new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = configManagerOrder.Value
				}
			};
		}

		private void InitializeWatcher()
		{
			_watcher = new FileSystemWatcher(ConfigDirectoryPath, "*.yml")
			{
				IncludeSubdirectories = false,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime)
			};
			_watcher.Changed += QueueYamlReload;
			_watcher.Created += QueueYamlReload;
			_watcher.Deleted += QueueYamlReload;
			_watcher.Renamed += QueueYamlReload;
			_watcher.EnableRaisingEvents = true;
		}

		private void QueueYamlReload(object sender, FileSystemEventArgs args)
		{
			QueueYamlReloadForFile(Path.GetFileName(args.FullPath));
			if (args is RenamedEventArgs e)
			{
				QueueYamlReloadForFile(Path.GetFileName(e.OldFullPath));
			}
		}

		private void QueueYamlReloadForFile(string fileName)
		{
			if (string.Equals(fileName, "BossRules.altar.yml", StringComparison.OrdinalIgnoreCase))
			{
				Interlocked.Exchange(ref _altarYamlReloadRequested, 1);
			}
			else if (string.Equals(fileName, "BossRules.yml", StringComparison.OrdinalIgnoreCase))
			{
				Interlocked.Exchange(ref _rulesYamlReloadRequested, 1);
			}
			else if (string.Equals(fileName, "BossRules.forsakenPowers.yml", StringComparison.OrdinalIgnoreCase))
			{
				Interlocked.Exchange(ref _forsakenPowersYamlReloadRequested, 1);
			}
		}

		private void ProcessQueuedYamlReload()
		{
			bool flag = false;
			if (Interlocked.Exchange(ref _altarYamlReloadRequested, 0) != 0)
			{
				_reloadAltarYaml = true;
				flag = true;
			}
			if (Interlocked.Exchange(ref _rulesYamlReloadRequested, 0) != 0)
			{
				_reloadRulesYaml = true;
				flag = true;
			}
			if (Interlocked.Exchange(ref _forsakenPowersYamlReloadRequested, 0) != 0)
			{
				_reloadForsakenPowersYaml = true;
				flag = true;
			}
			if (flag)
			{
				_reloadDueAt = (IsSourceOfTruth ? (Time.realtimeSinceStartup + 0.25f) : (-1f));
			}
			if (!IsSourceOfTruth)
			{
				_reloadDueAt = -1f;
				ClearPendingYamlReloads();
			}
			else if (!(_reloadDueAt < 0f) && !(Time.realtimeSinceStartup < _reloadDueAt))
			{
				_reloadDueAt = -1f;
				bool reloadAltarYaml = _reloadAltarYaml;
				bool reloadRulesYaml = _reloadRulesYaml;
				bool reloadForsakenPowersYaml = _reloadForsakenPowersYaml;
				ClearPendingYamlReloads();
				if (reloadAltarYaml)
				{
					LoadLocalAltarYamlAndPublish("file change");
				}
				if (reloadRulesYaml)
				{
					LoadLocalRulesYamlAndPublish("file change");
				}
				if (reloadForsakenPowersYaml)
				{
					LoadLocalForsakenPowersYamlAndPublish("file change");
				}
			}
		}

		private void ClearPendingYamlReloads()
		{
			_reloadAltarYaml = false;
			_reloadRulesYaml = false;
			_reloadForsakenPowersYaml = false;
		}

		private void HandleSourceOfTruthChanged(bool sourceOfTruth)
		{
			if (sourceOfTruth)
			{
				AltarReferenceGenerator.ResetAutoRefresh();
				LoadLocalAltarYamlAndPublish("authority change");
				LoadLocalRulesYamlAndPublish("authority change");
				LoadLocalForsakenPowersYamlAndPublish("authority change");
			}
			else
			{
				ApplyAltarYaml(_syncedAltarYaml.Value ?? "", "server sync");
				ApplyRulesYaml(_syncedRulesYaml.Value ?? "", "server sync");
				ApplyForsakenPowersYaml(_syncedForsakenPowersYaml.Value ?? "", "server sync");
			}
		}

		private void HandleSyncedAltarYamlChanged()
		{
			if (!IsSourceOfTruth)
			{
				ApplyAltarYaml(_syncedAltarYaml.Value ?? "", "server sync");
			}
		}

		private void HandleSyncedRulesYamlChanged()
		{
			if (!IsSourceOfTruth)
			{
				ApplyRulesYaml(_syncedRulesYaml.Value ?? "", "server sync");
			}
		}

		private void HandleSyncedForsakenPowersYamlChanged()
		{
			if (!IsSourceOfTruth)
			{
				ApplyForsakenPowersYaml(_syncedForsakenPowersYaml.Value ?? "", "server sync");
			}
		}

		private void LoadLocalAltarYamlAndPublish(string source)
		{
			AltarConfigurationFiles.EnsureDefaultFiles();
			string text;
			try
			{
				text = File.ReadAllText(AltarYamlFilePath);
			}
			catch (Exception ex)
			{
				BossRulesLogger.LogError((object)("Failed to read " + AltarYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (ApplyAltarYaml(text, source) && IsSourceOfTruth)
			{
				_syncedAltarYaml.AssignLocalValue(text);
			}
		}

		private void LoadLocalRulesYamlAndPublish(string source)
		{
			BossRuleConfigurationFiles.EnsureDefaultFile();
			string text;
			try
			{
				text = File.ReadAllText(RulesYamlFilePath);
			}
			catch (Exception ex)
			{
				BossRulesLogger.LogError((object)("Failed to read " + RulesYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (ApplyRulesYaml(text, source) && IsSourceOfTruth)
			{
				_syncedRulesYaml.AssignLocalValue(text);
			}
		}

		private void LoadLocalForsakenPowersYamlAndPublish(string source)
		{
			ForsakenPowerConfigurationFiles.EnsureDefaultFile();
			string text;
			try
			{
				text = File.ReadAllText(ForsakenPowersYamlFilePath);
			}
			catch (Exception ex)
			{
				BossRulesLogger.LogError((object)("Failed to read " + ForsakenPowersYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (ApplyForsakenPowersYaml(text, source) && IsSourceOfTruth)
			{
				_syncedForsakenPowersYaml.AssignLocalValue(text);
			}
		}

		private bool ApplyAltarYaml(string yaml, string source)
		{
			string text = yaml ?? "";
			BossRulesDebugLog.Client($"Applying altar YAML source={source} bytes={text.Length}.");
			if (!AltarConfiguration.TryParse(text, source, out IReadOnlyList<AltarConfigurationEntry> entries))
			{
				return false;
			}
			BossRulesDebugLog.Client($"Parsed altar YAML source={source} entries={entries.Count}.");
			AltarRuntime.Reload(entries);
			return true;
		}

		private bool ApplyRulesYaml(string yaml, string source)
		{
			if (!BossRuleConfiguration.TryParse(yaml, source, out BossRuleConfigurationState state))
			{
				return false;
			}
			BossRulesRuntime.Reload(state);
			return true;
		}

		private bool ApplyForsakenPowersYaml(string yaml, string source)
		{
			if (!ForsakenPowerConfiguration.TryParse(yaml, source, out IReadOnlyList<ForsakenPowerDefinition> entries))
			{
				return false;
			}
			ForsakenPowerRuntime.Configure(entries);
			return true;
		}
	}
	internal sealed class AltarConfigurationEntry
	{
		[YamlMember(Order = 1)]
		public string Prefab { get; set; } = "";

		[YamlMember(Order = 2)]
		public bool Enabled { get; set; } = true;

		[YamlMember(Order = 3)]
		public AltarOfferingBowlDefinition? OfferingBowl { get; set; }

		[YamlMember(Order = 4)]
		public List<AltarItemStandDefinition>? ItemStands { get; set; }
	}
	internal sealed class AltarOfferingBowlDefinition
	{
		[YamlMember(Order = 1)]
		public string? BossItem { get; set; }

		[YamlMember(Order = 2)]
		public int? BossItems { get; set; }

		[YamlMember(Order = 3)]
		public string? BossPrefab { get; set; }

		[YamlMember(Order = 4)]
		public string? ItemPrefab { get; set; }

		[YamlMember(Order = 5)]
		public string? SetGlobalKey { get; set; }

		[YamlMember(Order = 6)]
		public bool? RenderSpawnAreaGizmos { get; set; }

		[YamlMember(Order = 7)]
		public bool? AlertOnSpawn { get; set; }

		[YamlMember(Order = 8)]
		public float? SpawnBossDelay { get; set; }

		[YamlMember(Order = 9)]
		public FloatRangeDefinition? SpawnBossDistance { get; set; }

		[YamlMember(Order = 10)]
		public float? SpawnBossMaxYDistance { get; set; }

		[YamlMember(Order = 11)]
		public int? GetSolidHeightMargin { get; set; }

		[YamlMember(Order = 12)]
		public bool? EnableSolidHeightCheck { get; set; }

		[YamlMember(Order = 13)]
		public float? SpawnPointClearingRadius { get; set; }

		[YamlMember(Order = 14)]
		public float? SpawnYOffset { get; set; }

		[YamlMember(Order = 15)]
		public bool? UseItemStands { get; set; }

		[YamlMember(Order = 16)]
		public string? ItemStandPrefix { get; set; }

		[YamlMember(Order = 17)]
		public float? ItemStandMaxRange { get; set; }

		[YamlMember(Order = 18)]
		public float? RespawnMinutes { get; set; }
	}
	internal sealed class AltarItemStandDefinition
	{
		[YamlMember(Order = 1)]
		public string? Path { get; set; }

		[YamlMember(Order = 2)]
		public bool? CanBeRemoved { get; set; }

		[YamlMember(Order = 3)]
		public bool? AutoAttach { get; set; }

		[YamlMember(Order = 4)]
		public string? OrientationType { get; set; }

		[YamlMember(Order = 5)]
		public List<string>? SupportedTypes { get; set; }

		[YamlMember(Order = 6)]
		public List<string>? SupportedItems { get; set; }

		[YamlMember(Order = 7)]
		public List<string>? UnsupportedItems { get; set; }

		[YamlMember(Order = 8)]
		public float? PowerActivationDelay { get; set; }

		[YamlMember(Order = 9)]
		public string? GuardianPower { get; set; }
	}
	internal static class AltarConfiguration
	{
		private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		internal static bool TryParse(string yaml, string source, out IReadOnlyList<AltarConfigurationEntry> entries)
		{
			entries = Array.Empty<AltarConfigurationEntry>();
			try
			{
				List<AltarConfigurationEntry> list = (string.IsNullOrWhiteSpace(yaml) ? new List<AltarConfigurationEntry>() : Deserializer.Deserialize<List<AltarConfigurationEntry>>(yaml));
				entries = Normalize(list ?? new List<AltarConfigurationEntry>());
				BossRulesPlugin.BossRulesLogger.LogInfo((object)$"Loaded altar YAML from {source}: {entries.Count} entries.");
				return true;
			}
			catch (Exception ex)
			{
				BossRulesPlugin.BossRulesLogger.LogError((object)("Rejected altar YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
		}

		private static IReadOnlyList<AltarConfigurationEntry> Normalize(List<AltarConfigurationEntry> entries)
		{
			foreach (AltarConfigurationEntry entry in entries)
			{
				entry.Prefab = (entry.Prefab ?? "").Trim();
				NormalizeOfferingBowl(entry.OfferingBowl);
				NormalizeItemStands(entry.ItemStands);
			}
			return entries.Where((AltarConfigurationEntry entry) => entry.Prefab.Length > 0).ToList();
		}

		private static void NormalizeOfferingBowl(AltarOfferingBowlDefinition? definition)
		{
			if (definition != null)
			{
				definition.BossItem = NormalizeOptionalString(definition.BossItem);
				definition.BossPrefab = NormalizeOptionalString(definition.BossPrefab);
				definition.ItemPrefab = NormalizeOptionalString(definition.ItemPrefab);
				definition.SetGlobalKey = NormalizeOptionalString(definition.SetGlobalKey);
				definition.ItemStandPrefix = NormalizeOptionalString(definition.ItemStandPrefix);
			}
		}

		private static void NormalizeItemStands(List<AltarItemStandDefinition>? definitions)
		{
			if (definitions == null)
			{
				return;
			}
			foreach (AltarItemStandDefinition definition in definitions)
			{
				definition.Path = NormalizeOptionalString(definition.Path);
				definition.OrientationType = NormalizeOptionalString(definition.OrientationType);
				definition.SupportedTypes = NormalizeStringList(definition.SupportedTypes);
				definition.SupportedItems = NormalizeStringList(definition.SupportedItems);
				definition.UnsupportedItems = NormalizeStringList(definition.UnsupportedItems);
				definition.GuardianPower = NormalizeOptionalString(definition.GuardianPower);
			}
		}

		private static List<string>? NormalizeStringList(List<string>? values)
		{
			List<string> list = (from value in values?.Select((string value) => (value ?? "").Trim())
				where value.Length > 0
				select value).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
			if (list == null || list.Count <= 0)
			{
				return null;
			}
			return list;
		}

		private static string? NormalizeOptionalString(string? value)
		{
			string text = (value ?? "").Trim();
			if (text.Length != 0)
			{
				return text;
			}
			return null;
		}
	}
	internal static class AltarConfigurationFiles
	{
		internal static void EnsureDefaultFiles()
		{
			EnsureTextFile(BossRulesPlugin.AltarYamlFilePath, BuildDefaultAltarYaml());
			EnsureTextFile(BossRulesPlugin.AltarReferenceYamlFilePath, BuildReferencePlaceholderYaml());
		}

		private static void EnsureTextFile(string path, string content)
		{
			if (!File.Exists(path))
			{
				File.WriteAllText(path, content);
				BossRulesPlugin.BossRulesLogger.LogInfo((object)("Created " + path + "."));
			}
		}

		private static string BuildDefaultAltarYaml()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# BossRules altar overrides");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# This file owns boss altar OfferingBowl and boss ItemStand edits.");
			stringBuilder.AppendLine("# It is intentionally inert by default.");
			stringBuilder.AppendLine("# Copy rows from BossRules.altar.reference.yml and uncomment only the fields you want to override.");
			stringBuilder.AppendLine("# Unless noted otherwise, null/empty/omitted override fields keep the current prefab value.");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# offeringBowl");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# - prefab: Bonemass");
			stringBuilder.AppendLine("#   enabled: true # Default true when omitted.");
			stringBuilder.AppendLine("#   offeringBowl:");
			stringBuilder.AppendLine("#     bossItem: null # ex) WitheredBone. Required direct offering item prefab.");
			stringBuilder.AppendLine("#     bossItems: null # ex) 10. Number of bossItem items required; clamped to at least 1 when set.");
			stringBuilder.AppendLine("#     bossPrefab: null # ex) Bonemass. Boss character prefab spawned after a valid offering.");
			stringBuilder.AppendLine("#     itemPrefab: null # ex) Wishbone. Optional item reward prefab instead of spawning a boss.");
			stringBuilder.AppendLine("#     setGlobalKey: null # ex) defeated_bonemass. Optional global key set after a valid offering.");
			stringBuilder.AppendLine("#     renderSpawnAreaGizmos: null # ex) false. True draws the boss spawn search area while selected.");
			stringBuilder.AppendLine("#     alertOnSpawn: null # ex) false. True calls BaseAI.Alert() on the spawned boss.");
			stringBuilder.AppendLine("#     spawnBossDelay: null # ex) 5. Seconds to wait before spawning; clamped to at least 0.");
			stringBuilder.AppendLine("#     spawnBossDistance: null # ex) 0~40 or {min: 0, max: 40}. Each side can be overridden separately.");
			stringBuilder.AppendLine("#     spawnBossMaxYDistance: null # ex) 9999. Vertical spawn search distance; clamped to at least 0.");
			stringBuilder.AppendLine("#     getSolidHeightMargin: null # ex) 1000. Terrain raycast margin; clamped to at least 0.");
			stringBuilder.AppendLine("#     enableSolidHeightCheck: null # ex) true. True requires valid ground height.");
			stringBuilder.AppendLine("#     spawnPointClearingRadius: null # ex) 0. Clearing radius before boss spawn; clamped to at least 0.");
			stringBuilder.AppendLine("#     spawnYOffset: null # ex) 1. Vertical offset added to the chosen spawn position.");
			stringBuilder.AppendLine("#     useItemStands: null # ex) true. True uses nearby ItemStands instead of direct UseItem offerings.");
			stringBuilder.AppendLine("#     itemStandPrefix: null # ex) Boss. Object-name prefix used to select nearby ItemStands.");
			stringBuilder.AppendLine("#     itemStandMaxRange: null # ex) 20. Max scan distance for nearby ItemStands; clamped to at least 0.");
			stringBuilder.AppendLine("#     respawnMinutes: null # Null/omitted becomes 0, disabling BossRules altar cooldown. Set >0 for cooldown minutes.");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# itemStands");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# - prefab: StartTemple");
			stringBuilder.AppendLine("#   enabled: true # Default true when omitted.");
			stringBuilder.AppendLine("#   itemStands:");
			stringBuilder.AppendLine("#   - path: null # Null/empty/omitted targets all relevant stands. ex) BossStone_Eikthyr[0]/itemstand[0] targets one reference path.");
			stringBuilder.AppendLine("#     canBeRemoved: null # ex) true. True allows players to remove the attached item.");
			stringBuilder.AppendLine("#     autoAttach: null # ex) false. True automatically attaches compatible dropped items.");
			stringBuilder.AppendLine("#     orientationType: null # ex) Vertical. ItemStand.Orientation name.");
			stringBuilder.AppendLine("#     supportedTypes: [] # ex) [OneHandedWeapon, TwoHandedWeapon]. ItemDrop.ItemType names.");
			stringBuilder.AppendLine("#     supportedItems: [] # ex) [TrophyDeer]. Explicitly allowed item prefabs.");
			stringBuilder.AppendLine("#     unsupportedItems: [] # ex) [TrophyDeer]. Explicitly blocked item prefabs.");
			stringBuilder.AppendLine("#     powerActivationDelay: null # ex) 2. Seconds before guardianPower activates; clamped to at least 0.");
			stringBuilder.AppendLine("#     guardianPower: null # ex) GP_Eikthyr. StatusEffect prefab granted when used.");
			return stringBuilder.ToString();
		}

		private static string BuildReferencePlaceholderYaml()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# BossRules altar reference");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# This file is generated automatically after ZoneSystem location prefabs load.");
			stringBuilder.AppendLine("[]");
			return stringBuilder.ToString();
		}
	}
	internal sealed class AltarReferenceEntry
	{
		[YamlMember(Order = 1)]
		public string Prefab { get; set; } = "";

		[YamlIgnore]
		public string PrefabAssetId { get; set; } = "";

		[YamlIgnore]
		public string SourcePrefabName { get; set; } = "";

		[YamlIgnore]
		public string OwnerName { get; set; } = "Unknown / Untracked";

		[YamlMember(Order = 2)]
		public AltarOfferingBowlDefinition? OfferingBowl { get; set; }

		[YamlMember(Order = 3)]
		public List<AltarReferenceItemStandDefinition>? ItemStands { get; set; }
	}
	internal sealed class AltarReferenceItemStandDefinition
	{
		[YamlMember(Order = 1)]
		public string? Path { get; set; }

		[YamlMember(Order = 2)]
		public bool? CanBeRemoved { get; set; }

		[YamlMember(Order = 3)]
		public bool? AutoAttach { get; set; }

		[YamlMember(Order = 4)]
		public string? OrientationType { get; set; }

		[YamlMember(Order = 5)]
		public FlowStringListDefinition? SupportedTypes { get; set; }

		[YamlMember(Order = 6)]
		public FlowStringListDefinition? SupportedItems { get; set; }

		[YamlMember(Order = 7)]
		public FlowStringListDefinition? UnsupportedItems { get; set; }

		[YamlMember(Order = 8)]
		public float? PowerActivationDelay { get; set; }

		[YamlMember(Order = 9)]
		public string? GuardianPower { get; set; }
	}
	internal sealed class FlowStringListDefinition : IYamlConvertible
	{
		public List<string> Values { get; set; } = new List<string>();

		public FlowStringListDefinition()
		{
		}

		public FlowStringListDefinition(IEnumerable<string> values)
		{
			Values = (from value in values
				where !string.IsNullOrWhiteSpace(value)
				select value.Trim()).ToList();
		}

		void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
		{
			Values.Clear();
			parser.Consume<SequenceStart>();
			SequenceEnd @event;
			while (!parser.Accept<SequenceEnd>(out @event))
			{
				Values.Add((parser.Consume<YamlDotNet.Core.Events.Scalar>().Value ?? "").Trim());
			}
			parser.Consume<SequenceEnd>();
		}

		void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
		{
			emitter.Emit(new SequenceStart(null, null, isImplicit: true, SequenceStyle.Flow));
			foreach (string value in Values)
			{
				emitter.Emit(new YamlDotNet.Core.Events.Scalar(value));
			}
			emitter.Emit(new SequenceEnd());
		}
	}
	internal static class AltarReferenceGenerator
	{
		private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build();

		private const float AutoRefreshIdleRetryDelaySeconds = 1f;

		private const float AutoRefreshRetryDelaySeconds = 5f;

		private static readonly HashSet<string> DuplicateComponentWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static bool _autoRefreshDone;

		private static AltarOfferingBowlDefinition? _queenDungeonReferenceDefinition;

		private static bool _queenDungeonReferenceCaptureAttempted;

		private static bool _queenDungeonReferenceCaptureFailed;

		private static bool _queenDungeonReferenceWarningLogged;

		private static bool _hasSeenZoneSystem;

		private static float _nextAutoRefreshAttemptAt;

		internal static void ResetAutoRefresh()
		{
			ResetAutoRefreshState();
			AltarPrefabOwnerResolver.ResetRuntimeSnapshot(allowCacheLoad: false);
		}

		internal static void ResetForZoneSystem()
		{
			ResetAutoRefreshState();
			AltarPrefabOwnerResolver.ResetRuntimeSnapshot(!_hasSeenZoneSystem);
			_hasSeenZoneSystem = true;
		}

		internal static void ShutdownAutoRefresh()
		{
			ResetAutoRefreshState();
			AltarPrefabOwnerResolver.ResetRuntimeSnapshot(allowCacheLoad: false);
			_hasSeenZoneSystem = false;
		}

		private static void ResetAutoRefreshState()
		{
			_autoRefreshDone = false;
			_queenDungeonReferenceDefinition = null;
			_queenDungeonReferenceCaptureAttempted = false;
			_queenDungeonReferenceCaptureFailed = false;
			_queenDungeonReferenceWarningLogged = false;
			_nextAutoRefreshAttemptAt = 0f;
		}

		internal static void TryAutoRefreshReferenceConfigurationFile()
		{
			if (_autoRefreshDone || !BossRulesPlugin.IsSourceOfTruth)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextAutoRefreshAttemptAt)
			{
				return;
			}
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				_nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f;
				return;
			}
			if (ZoneSystem.instance.m_locations == null || ZoneSystem.instance.m_locations.Count == 0)
			{
				_nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f;
				return;
			}
			if (!IsQueenDungeonReferenceDataReady())
			{
				_nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f;
				return;
			}
			try
			{
				int entryCount;
				string content = BuildReferenceConfigurationContent(out entryCount);
				if (_queenDungeonReferenceCaptureFailed)
				{
					_nextAutoRefreshAttemptAt = realtimeSinceStartup + 5f;
					return;
				}
				if (entryCount == 0)
				{
					_nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f;
					return;
				}
				WriteReferenceConfigurationFile(content);
				_autoRefreshDone = true;
			}
			catch (Exception exception)
			{
				_nextAutoRefreshAttemptAt = Time.realtimeSinceStartup + 5f;
				BossRulesPlugin.BossRulesLogger.LogWarning((object)$"Failed to update altar reference configuration at {BossRulesPlugin.AltarReferenceYamlFilePath}. {FormatException(exception)} Retrying in {5f:0.#}s.");
			}
		}

		private static string FormatException(Exception exception)
		{
			Exception ex = exception;
			while (ex is TargetInvocationException && ex.InnerException != null)
			{
				ex = ex.InnerException;
			}
			if (ex != exception)
			{
				return exception.GetType().Name + ": " + exception.Message + " Inner " + ex.GetType().Name + ": " + ex.Message + ".";
			}
			return exception.GetType().Name + ": " + exception.Message + ".";
		}

		private static string BuildReferenceConfigurationContent(out int entryCount)
		{
			_queenDungeonReferenceDefinition = null;
			_queenDungeonReferenceCaptureAttempted = false;
			_queenDungeonReferenceCaptureFailed = false;
			List<AltarReferenceEntry> list = CaptureReferenceEntries().ToList();
			entryCount = list.Count;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# BossRules altar reference");
			stringBuilder.AppendLine("# Generated from loaded ZoneSystem location prefabs and supported nested dungeon rooms.");
			stringBuilder.AppendLine("# Copy rows to BossRules.altar.yml to override.");
			stringBuilder.AppendLine("# This file is overwritten automatically.");
			stringBuilder.Append(SerializeReferenceEntries(list));
			return stringBuilder.ToString();
		}

		private static string SerializeReferenceEntries(IReadOnlyList<AltarReferenceEntry> entries)
		{
			if (entries.Count == 0)
			{
				return "[]" + Environment.NewLine;
			}
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = false;
			foreach (IGrouping<string, AltarReferenceEntry> item in entries.OrderBy((AltarReferenceEntry entry) => AltarPrefabOwnerResolver.GetOwnerSortBucket(AltarPrefabOwnerResolver.NormalizeOwnerName(entry.OwnerName))).ThenBy<AltarReferenceEntry, string>((AltarReferenceEntry entry) => AltarPrefabOwnerResolver.NormalizeOwnerName(entry.OwnerName), StringComparer.OrdinalIgnoreCase).ThenBy<AltarReferenceEntry, string>((AltarReferenceEntry entry) => entry.Prefab, StringComparer.OrdinalIgnoreCase)
				.GroupBy<AltarReferenceEntry, string>((AltarReferenceEntry entry) => AltarPrefabOwnerResolver.NormalizeOwnerName(entry.OwnerName), StringComparer.OrdinalIgnoreCase))
			{
				if (flag)
				{
					stringBuilder.AppendLine();
				}
				stringBuilder.Append("# ===== ").Append(item.Key).AppendLine(" =====");
				foreach (AltarReferenceEntry item2 in item)
				{
					stringBuilder.AppendLine(SerializeReferenceEntry(item2));
				}
				flag = true;
			}
			return stringBuilder.ToString();
		}

		private static string SerializeReferenceEntry(AltarReferenceEntry entry)
		{
			string text = Serializer.Serialize(new AltarReferenceEntry[1] { entry }).TrimEnd('\r', '\n');
			if (!RequiresQuotedPrefabScalar(entry.Prefab))
			{
				return text;
			}
			int num = text.IndexOfAny(new char[2] { '\r', '\n' });
			string text2 = ((num >= 0) ? text.Substring(num) : "");
			string text3 = EscapeDoubleQuotedYamlScalar(entry.Prefab);
			return "- prefab: \"" + text3 + "\"" + text2;
		}

		private static bool RequiresQuotedPrefabScalar(string prefab)
		{
			return (prefab ?? "").IndexOf(':') >= 0;
		}

		private static string EscapeDoubleQuotedYamlScalar(string value)
		{
			return (value ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"");
		}

		private static List<AltarReferenceEntry> CaptureReferenceEntries()
		{
			List<AltarReferenceEntry> list = new List<AltarReferenceEntry>();
			List<ZoneLocation> list2 = new List<ZoneLocation>();
			HashSet<string> capturedPrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			DuplicateComponentWarnings.Clear();
			try
			{
				foreach (ZoneLocation location in ZoneSystem.instance.m_locations)
				{
					if (TryCaptureReferenceEntry(location, capturedPrefabs, list2, out AltarReferenceEntry entry) && entry != null)
					{
						list.Add(entry);
					}
				}
				AltarPrefabOwnerSnapshot snapshot = AltarPrefabOwnerResolver.GetSnapshot(list.Select((AltarReferenceEntry altarReferenceEntry) => new AltarPrefabOwnerSource(altarReferenceEntry.Prefab, altarReferenceEntry.PrefabAssetId, altarReferenceEntry.SourcePrefabName)));
				foreach (AltarReferenceEntry item in list)
				{
					item.OwnerName = snapshot.GetOwnerName(item.Prefab);
				}
				return list;
			}
			finally
			{
				Exception ex = null;
				for (int num = list2.Count - 1; num >= 0; num--)
				{
					try
					{
						list2[num].m_prefab.Release();
					}
					catch (Exception ex2)
					{
						if (ex == null)
						{
							ex = ex2;
						}
					}
				}
				if (ex != null)
				{
					BossRulesPlugin.BossRulesLogger.LogWarning((object)("Failed to release one or more altar reference location prefabs. " + FormatException(ex)));
				}
			}
		}

		private static bool TryCaptureReferenceEntry(ZoneLocation location, HashSet<string> capturedPrefabs, ICollection<ZoneLocation> loadedLocationPrefabs, out AltarReferenceEntry? entry)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			entry = null;
			if (location == null || !location.m_prefab.IsValid)
			{
				return false;
			}
			string zoneLocationPrefabName = AltarLocationResolver.GetZoneLocationPrefabName(location);
			if (zoneLocationPrefabName.Length == 0 || !capturedPrefabs.Add(zoneLocationPrefabName))
			{
				return false;
			}
			location.m_prefab.Load();
			loadedLocationPrefabs.Add(location);
			return TryCaptureLoadedReferenceEntry(location, zoneLocationPrefabName, out entry);
		}

		private static bool TryCaptureLoadedReferenceEntry(ZoneLocation location, string prefabName, out AltarReferenceEntry? entry)
		{
			entry = null;
			GameObject rootPrefab = location.m_prefab.Asset;
			if ((Object)(object)rootPrefab == (Object)null)
			{
				return false;
			}
			OfferingBowl[] componentsInChildren = rootPrefab.GetComponentsInChildren<OfferingBowl>(true);
			ItemStand[] componentsInChildren2 = rootPrefab.GetComponentsInChildren<ItemStand>(true);
			WarnDuplicateComponent(prefabName, "OfferingBowl", componentsInChildren.Length);
			AltarOfferingBowlDefinition altarOfferingBowlDefinition = ((componentsInChildren.Length != 0) ? ConvertReferenceOfferingBowl(AltarRuntime.CaptureOfferingBowlSnapshot(componentsInChildren[0])) : null);
			if (altarOfferingBowlDefinition == null && QueenDungeonAltarSupport.IsSupportedLocationPrefab(prefabName))
			{
				altarOfferingBowlDefinition = TryCaptureQueenDungeonOfferingBowl();
			}
			if (altarOfferingBowlDefinition == null && componentsInChildren2.Length == 0)
			{
				return false;
			}
			entry = new AltarReferenceEntry
			{
				Prefab = prefabName,
				PrefabAssetId = (((AssetID)(ref location.m_prefab.m_assetID)).IsValid ? ((object)Unsafe.As<AssetID, AssetID>(ref location.m_prefab.m_assetID)/*cast due to .constrained prefix*/).ToString() : ""),
				SourcePrefabName = (((Object)rootPrefab).name ?? ""),
				OfferingBowl = altarOfferingBowlDefinition,
				ItemStands = ((componentsInChildren2.Length != 0) ? (from itemStand in componentsInChildren2
					where (Object)(object)itemStand != (Object)null
					select ConvertReferenceItemStand(rootPrefab.transform, itemStand)).OrderBy<AltarReferenceItemStandDefinition, string>((AltarReferenceItemStandDefinition itemStand) => itemStand.Path, StringComparer.Ordinal).ToList() : null)
			};
			if (entry.OfferingBowl == null)
			{
				List<AltarReferenceItemStandDefinition> itemStands = entry.ItemStands;
				if (itemStands != null)
				{
					return itemStands.Count > 0;
				}
				return false;
			}
			return true;
		}

		private static bool IsQueenDungeonReferenceDataReady()
		{
			if (!ZoneSystem.instance.m_locations.Any((ZoneLocation location) => QueenDungeonAltarSupport.IsSupportedLocationPrefab(AltarLocationResolver.GetZoneLocationPrefabName(location))))
			{
				return true;
			}
			if ((Object)(object)DungeonDB.instance == (Object)null || DungeonDB.GetRooms().Count == 0)
			{
				return false;
			}
			bool num = DungeonDB.GetRooms().Any(QueenDungeonAltarSupport.IsTargetRoom);
			if (!num)
			{
				WarnQueenDungeonReference("Dungeon room 'dvergr_new_bossroom_ENTRANCE02' was not registered. Waiting to capture the 'Mistlands_DvergrBossEntrance1' altar reference.");
			}
			return num;
		}

		private static AltarOfferingBowlDefinition? TryCaptureQueenDungeonOfferingBowl()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (_queenDungeonReferenceCaptureAttempted)
			{
				return _queenDungeonReferenceDefinition;
			}
			_queenDungeonReferenceCaptureAttempted = true;
			RoomData val = ((IEnumerable<RoomData>)DungeonDB.GetRooms()).FirstOrDefault((Func<RoomData, bool>)QueenDungeonAltarSupport.IsTargetRoom);
			if (val == null)
			{
				MarkQueenDungeonReferenceCaptureFailed("Dungeon room 'dvergr_new_bossroom_ENTRANCE02' was not registered. The 'Mistlands_DvergrBossEntrance1' altar reference could not be captured.");
				return null;
			}
			try
			{
				val.m_prefab.Load();
				GameObject asset = val.m_prefab.Asset;
				if ((Object)(object)asset == (Object)null)
				{
					MarkQueenDungeonReferenceCaptureFailed("Dungeon room 'dvergr_new_bossroom_ENTRANCE02' could not be loaded. The 'Mistlands_DvergrBossEntrance1' altar reference could not be captured.");
					return null;
				}
				OfferingBowl[] array = asset.GetComponentsInChildren<OfferingBowl>(true).Where(QueenDungeonAltarSupport.IsTargetOfferingBowl).ToArray();
				if (array.Length != 1)
				{
					MarkQueenDungeonReferenceCaptureFailed("Dungeon room 'dvergr_new_bossroom_ENTRANCE02' contains " + string.Format("{0} matching '{1}' components; expected exactly one. ", array.Length, "offeraltar_queen") + "The 'Mistlands_DvergrBossEntrance1' altar reference could not be captured.");
					return null;
				}
				_queenDungeonReferenceDefinition = ConvertReferenceOfferingBowl(AltarRuntime.CaptureOfferingBowlSnapshot(array[0]));
				return _queenDungeonReferenceDefinition;
			}
			catch (Exception exception)
			{
				MarkQueenDungeonReferenceCaptureFailed("Dungeon room 'dvergr_new_bossroom_ENTRANCE02' failed to load. " + FormatException(exception) + " The 'Mistlands_DvergrBossEntrance1' altar reference will be retried.");
				return null;
			}
			finally
			{
				val.m_prefab.Release();
			}
		}

		private static void MarkQueenDungeonReferenceCaptureFailed(string message)
		{
			_queenDungeonReferenceCaptureFailed = true;
			WarnQueenDungeonReference(message);
		}

		private static void WarnQueenDungeonReference(string message)
		{
			if (!_queenDungeonReferenceWarningLogged)
			{
				_queenDungeonReferenceWarningLogged = true;
				BossRulesPlugin.BossRulesLogger.LogWarning((object)message);
			}
		}

		private static AltarOfferingBowlDefinition ConvertReferenceOfferingBowl(OfferingBowlSnapshot snapshot)
		{
			return new AltarOfferingBowlDefinition
			{
				BossItem = ((snapshot.BossItem.Length == 0) ? null : snapshot.BossItem),
				BossItems = ((snapshot.BossItems == 1) ? ((int?)null) : new int?(snapshot.BossItems)),
				BossPrefab = ((snapshot.BossPrefab.Length == 0) ? null : snapshot.BossPrefab),
				ItemPrefab = ((snapshot.ItemPrefab.Length == 0) ? null : snapshot.ItemPrefab),
				SetGlobalKey = (string.IsNullOrWhiteSpace(snapshot.SetGlobalKey) ? null : snapshot.SetGlobalKey),
				RenderSpawnAreaGizmos = (snapshot.RenderSpawnAreaGizmos ? new bool?(true) : ((bool?)null)),
				AlertOnSpawn = (snapshot.AlertOnSpawn ? new bool?(true) : ((bool?)null)),
				SpawnBossDelay = (IsReferenceDefault(snapshot.SpawnBossDelay, 5f) ? ((float?)null) : new float?(snapshot.SpawnBossDelay)),
				SpawnBossDistance = RangeFormatting.FromReference(snapshot.SpawnBossMinDistance, snapshot.SpawnBossMaxDistance, 0f, 40f),
				SpawnBossMaxYDistance = (IsReferenceDefault(snapshot.SpawnBossMaxYDistance, 9999f) ? ((float?)null) : new float?(snapshot.SpawnBossMaxYDistance)),
				GetSolidHeightMargin = ((snapshot.GetSolidHeightMargin == 1000) ? ((int?)null) : new int?(snapshot.GetSolidHeightMargin)),
				EnableSolidHeightCheck = (snapshot.EnableSolidHeightCheck ? ((bool?)null) : new bool?(false)),
				SpawnPointClearingRadius = (IsReferenceDefault(snapshot.SpawnPointClearingRadius, 0f) ? ((float?)null) : new float?(snapshot.SpawnPointClearingRadius)),
				SpawnYOffset = (IsReferenceDefault(snapshot.SpawnYOffset, 1f) ? ((float?)null) : new float?(snapshot.SpawnYOffset)),
				UseItemStands = (snapshot.UseItemStands ? new bool?(true) : ((bool?)null)),
				ItemStandPrefix = (string.IsNullOrWhiteSpace(snapshot.ItemStandPrefix) ? null : snapshot.ItemStandPrefix),
				ItemStandMaxRange = (IsReferenceDefault(snapshot.ItemStandMaxRange, 20f) ? ((float?)null) : new float?(snapshot.ItemStandMaxRange)),
				RespawnMinutes = null
			};
		}

		private static AltarReferenceItemStandDefinition ConvertReferenceItemStand(Transform root, ItemStand itemStand)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			ItemStandSnapshot itemStandSnapshot = AltarRuntime.CaptureItemStandSnapshot(itemStand);
			return new AltarReferenceItemStandDefinition
			{
				Path = AltarRuntime.GetRelativePath(root, ((Component)itemStand).transform),
				CanBeRemoved = (itemStandSnapshot.CanBeRemoved ? ((bool?)null) : new bool?(false)),
				AutoAttach = (itemStandSnapshot.AutoAttach ? new bool?(true) : ((bool?)null)),
				OrientationType = ((string.IsNullOrWhiteSpace(itemStandSnapshot.OrientationType) || itemStandSnapshot.OrientationType == ((object)(Orientation)1/*cast due to .constrained prefix*/).ToString()) ? null : itemStandSnapshot.OrientationType),
				SupportedTypes = ((itemStandSnapshot.SupportedTypes.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.SupportedTypes)),
				SupportedItems = ((itemStandSnapshot.SupportedItems.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.SupportedItems)),
				UnsupportedItems = ((itemStandSnapshot.UnsupportedItems.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.UnsupportedItems)),
				PowerActivationDelay = (IsReferenceDefault(itemStandSnapshot.PowerActivationDelay, 2f) ? ((float?)null) : new float?(itemStandSnapshot.PowerActivationDelay)),
				GuardianPower = (string.IsNullOrWhiteSpace(itemStandSnapshot.GuardianPower) ? null : itemStandSnapshot.GuardianPower)
			};
		}

		private static bool IsReferenceDefault(float actual, float expected)
		{
			return Math.Abs(actual - expected) < 0.0001f;
		}

		private static void WarnDuplicateComponent(string prefabName, string componentName, int count)
		{
			if (count > 1)
			{
				string item = prefabName + "@" + componentName;
				if (DuplicateComponentWarnings.Add(item))
				{
					BossRulesPlugin.BossRulesLogger.LogWarning((object)("Location prefab '" + prefabName + "' has multiple " + componentName + " components. The first one will be used for BossRules.altar.yml."));
				}
			}
		}

		private static void WriteReferenceConfigurationFile(string content)
		{
			string altarReferenceYamlFilePath = BossRulesPlugin.AltarReferenceYamlFilePath;
			if (!string.Equals(File.Exists(altarReferenceYamlFilePath) ? File.ReadAllText(altarReferenceYamlFilePath) : "", content, StringComparison.Ordinal))
			{
				File.WriteAllText(altarReferenceYamlFilePath, content, Encoding.UTF8);
				BossRulesPlugin.BossRulesLogger.LogInfo((object)("Updated altar reference configuration at " + altarReferenceYamlFilePath + "."));
			}
		}
	}
	internal readonly struct AltarPrefabOwnerSource
	{
		internal string PrefabName { get; }

		internal string PrefabAssetId { get; }

		internal string SourcePrefabName { get; }

		internal AltarPrefabOwnerSource(string prefabName, string prefabAssetId, string sourcePrefabName)
		{
			PrefabName = (prefabName ?? "").Trim();
			PrefabAssetId = NormalizeAssetId(prefabAssetId);
			SourcePrefabName = (sourcePrefabName ?? "").Trim();
		}

		private static string NormalizeAssetId(string? assetId)
		{
			return (assetId ?? "").Trim().ToLowerInvariant();
		}
	}
	internal sealed class AltarPrefabOwnerSnapshot
	{
		private readonly Dictionary<string, string> _owners;

		internal AltarPrefabOwnerSnapshot(Dictionary<string, string> owners)
		{
			_owners = owners ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		}

		internal string GetOwnerName(string? prefabName)
		{
			string text = (prefabName ?? "").Trim();
			if (text.Length <= 0 || !_owners.TryGetValue(text, out string value) || string.IsNullOrWhiteSpace(value))
			{
				return "Unknown / Untracked";
			}
			return value;
		}
	}
	internal static class AltarPrefabOwnerResolver
	{
		private enum VanillaCatalogState
		{
			Uninitialized,
			Loaded,
			Unavailable
		}

		private readonly struct OwnerMapping
		{
			internal string OwnerName { get; }

			internal int Priority { get; }

			internal OwnerMapping(string ownerName, int priority)
			{
				OwnerName = ownerName;
				Priority = priority;
			}
		}

		private sealed class PluginResources
		{
			internal string OwnerName { get; set; } = "";

			internal string[] ResourceNames { get; set; } = Array.Empty<string>();
		}

		internal const string VanillaOwnerName = "Valheim";

		internal const string UnknownOwnerName = "Unknown / Untracked";

		private const string CacheFormatVersion = "v1";

		private const string ResolverLogicVersion = "location-owner-v4";

		private const string RuntimeAssetIdPrefix = "000000010000000100000001";

		private const string JotunnZoneManagerTypeName = "Jotunn.Managers.ZoneManager";

		private const string JotunnPrefabManagerTypeName = "Jotunn.Managers.PrefabManager";

		private const string LocationManagerTemplateTypeName = "LocationManager.Location";

		private static readonly object Sync = new object();

		private static readonly HashSet<string> VanillaPrefabNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> VanillaAssetIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static string _snapshotSignature = "";

		private static AltarPrefabOwnerSnapshot _snapshot = new AltarPrefabOwnerSnapshot(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));

		private static VanillaCatalogState _vanillaCatalogState;

		private static bool _resolverWarningLogged;

		private static bool _cacheWarningLogged;

		private static bool _vanillaCatalogWarningLogged;

		private static bool _allowCacheLoad = true;

		internal static AltarPrefabOwnerSnapshot GetSnapshot(IEnumerable<AltarPrefabOwnerSource> prefabSources)
		{
			List<AltarPrefabOwnerSource> list = (from @group in (prefabSources ?? Enumerable.Empty<AltarPrefabOwnerSource>()).Where((AltarPrefabOwnerSource source) => source.PrefabName.Length > 0).GroupBy<AltarPrefabOwnerSource, string>((AltarPrefabOwnerSource source) => source.PrefabName, StringComparer.OrdinalIgnoreCase)
				select @group.First()).OrderBy<AltarPrefabOwnerSource, string>((AltarPrefabOwnerSource source) => source.PrefabName, StringComparer.OrdinalIgnoreCase).ToList();
			if (list.Count == 0)
			{
				return new AltarPrefabOwnerSnapshot(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
			}
			try
			{
				string text = BuildSnapshotSignature(list);
				if (string.Equals(text, _snapshotSignature, StringComparison.Ordinal))
				{
					return _snapshot;
				}
				lock (Sync)
				{
					if (string.Equals(text, _snapshotSignature, StringComparison.Ordinal))
					{
						return _snapshot;
					}
					if (!_allowCacheLoad || !TryLoadSnapshotFromCache(text, list.Select((AltarPrefabOwnerSource source) => source.PrefabName).ToList(), out Dictionary<string, string> owners))
					{
						owners = BuildOwnerMappings(list);
						SaveSnapshotToCache(text, owners);
					}
					_snapshot = new AltarPrefabOwnerSnapshot(owners);
					_snapshotSignature = text;
					_allowCacheLoad = true;
					return _snapshot;
				}
			}
			catch (Exception exception)
			{
				WarnResolverFailure(exception);
				return BuildUnknownSnapshot(list.Select((AltarPrefabOwnerSource source) => source.PrefabName));
			}
		}

		internal static void ResetRuntimeSnapshot(bool allowCacheLoad)
		{
			lock (Sync)
			{
				_snapshotSignature = "";
				_snapshot = new AltarPrefabOwnerSnapshot(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
				_allowCacheLoad = allowCacheLoad;
			}
		}

		internal static int GetOwnerSortBucket(string ownerName)
		{
			if (string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase))
			{
				return 0;
			}
			if (!string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase))
			{
				return 1;
			}
			return 2;
		}

		internal static string NormalizeOwnerName(string? ownerName)
		{
			string text = (ownerName ?? "").Replace('\r', ' ').Replace('\n', ' ').Trim();
			if (text.Length <= 0)
			{
				return "Unknown / Untracked";
			}
			return text;
		}

		private static Dictionary<string, string> BuildOwnerMappings(IReadOnlyCollection<AltarPrefabOwnerSource> prefabSources)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (AltarPrefabOwnerSource prefabSource in prefabSources)
			{
				foreach (string item in EnumerateSourceLookupCandidates(prefabSource))
				{
					hashSet.Add(item);
				}
			}
			Dictionary<string, OwnerMapping> dictionary = new Dictionary<string, OwnerMapping>(StringComparer.OrdinalIgnoreCase);
			CollectJotunnManagerMappings("Jotunn.Managers.PrefabManager", "Prefabs", 2, hashSet, dictionary);
			CollectLocationManagerTemplateMappings(hashSet, dictionary);
			CollectJotunnManagerMappings("Jotunn.Managers.ZoneManager", "Locations", 0, hashSet, dictionary);
			EnsureVanillaCatalogLoaded();
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (AltarPrefabOwnerSource prefabSource2 in prefabSources)
			{
				if (HasProvenance(prefabSource2, dictionary) || IsVanillaSource(prefabSource2))
				{
					continue;
				}
				foreach (string item2 in EnumerateSourceLookupCandidates(prefabSource2))
				{
					hashSet2.Add(item2);
				}
			}
			if (hashSet2.Count > 0)
			{
				CollectLoadedBundleMappings(hashSet2, dictionary);
			}
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			foreach (AltarPrefabOwnerSource prefabSource3 in prefabSources)
			{
				dictionary2[prefabSource3.PrefabName] = ResolveOwnerName(prefabSource3, dictionary);
			}
			return dictionary2;
		}

		private static string ResolveOwnerName(AltarPrefabOwnerSource source, IReadOnlyDictionary<string, OwnerMapping> provenance)
		{
			foreach (string item in EnumerateSourceLookupCandidates(source))
			{
				if (provenance.TryGetValue(item, out var value) && !string.IsNullOrWhiteSpace(value.OwnerName))
				{
					return NormalizeOwnerName(value.OwnerName);
				}
			}
			if (IsVanillaSource(source))
			{
				return "Valheim";
			}
			return "Unknown / Untracked";
		}

		private static bool HasProvenance(AltarPrefabOwnerSource source, IReadOnlyDictionary<string, OwnerMapping> provenance)
		{
			return EnumerateSourceLookupCandidates(source).Any(provenance.ContainsKey);
		}

		private static bool IsVanillaSource(AltarPrefabOwnerSource source)
		{
			if (_vanillaCatalogState == VanillaCatalogState.Loaded)
			{
				if (source.PrefabAssetId.Length > 0 && VanillaAssetIds.Contains(source.PrefabAssetId))
				{
					return true;
				}
				if (EnumerateSourceLookupCandidates(source).Any(VanillaPrefabNames.Contains))
				{
					return true;
				}
			}
			return QueenDungeonAltarSupport.IsSupportedLocationPrefab(source.PrefabName);
		}

		private static IEnumerable<string> EnumerateSourceLookupCandidates(AltarPrefabOwnerSource source)
		{
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in EnumerateLookupCandidates(source.PrefabName).Concat(EnumerateLookupCandidates(source.SourcePrefabName)))
			{
				if (seen.Add(item))
				{
					yield return item;
				}
			}
		}

		private static IEnumerable<string> EnumerateLookupCandidates(string prefabName)
		{
			List<string> candidates = new List<string>();
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string text = (prefabName ?? "").Trim();
			if (text.Length == 0)
			{
				yield break;
			}
			AddIfNew(text);
			string text2 = TrimCloneSuffix(text);
			AddIfNew(text2);
			int num = text2.IndexOf(':');
			if (num > 0)
			{
				AddIfNew(text2.Substring(0, num));
			}
			foreach (string item in candidates)
			{
				yield return item;
			}
			void AddIfNew(string candidate)
			{
				string text3 = (candidate ?? "").Trim();
				if (text3.Length > 0 && seen.Add(text3))
				{
					candidates.Add(text3);
				}
			}
		}

		private static void CollectLoadedBundleMappings(HashSet<string> lookupCandidates, Dictionary<string, OwnerMapping> mappings)
		{
			List<PluginResources> source = BuildPluginResources();
			IEnumerable<AssetBundle> enumerable;
			try
			{
				enumerable = (from bundle in AssetBundle.GetAllLoadedAssetBundles()
					where (Object)(object)bundle != (Object)null
					select bundle).OrderBy<AssetBundle, string>((AssetBundle bundle) => ((Object)bundle).name ?? "", StringComparer.OrdinalIgnoreCase).ToArray();
			}
			catch
			{
				return;
			}
			foreach (AssetBundle item in enumerable)
			{
				string bundleName = (((Object)item).name ?? "").Trim();
				if (bundleName.Length == 0)
				{
					continue;
				}
				List<string> list = (from candidate in source
					where candidate.ResourceNames.Any((string resourceName) => IsBundleResourceMatch(resourceName, bundleName))
					select candidate.OwnerName).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
				if (list.Count != 1)
				{
					continue;
				}
				string[] allAssetNames;
				try
				{
					allAssetNames = item.GetAllAssetNames();
				}
				catch
				{
					continue;
				}
				string[] array = allAssetNames;
				foreach (string text in array)
				{
					if (text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
					{
						string text2 = Path.GetFileNameWithoutExtension(text).Trim();
						if (lookupCandidates.Contains(text2))
						{
							TryAddOwnerMapping(mappings, text2, list[0], 3);
						}
					}
				}
			}
		}

		private static bool IsBundleResourceMatch(string? resourceName, string bundleName)
		{
			string text = (resourceName ?? "").Trim();
			string text2 = (bundleName ?? "").Trim();
			if (text.Length > 0 && text2.Length > 0)
			{
				if (!string.Equals(text, text2, StringComparison.OrdinalIgnoreCase))
				{
					return text.EndsWith("." + text2, StringComparison.OrdinalIgnoreCase);
				}
				return true;
			}
			return false;
		}

		private static List<PluginResources> BuildPluginResources()
		{
			List<PluginResources> list = new List<PluginResources>();
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				string pluginDisplayName = GetPluginDisplayName(value.Metadata.Name, value.Metadata.GUID);
				string[] resourceNames;
				try
				{
					resourceNames = ((object)value.Instance)?.GetType().Assembly.GetManifestResourceNames() ?? Array.Empty<string>();
				}
				catch
				{
					resourceNames = Array.Empty<string>();
				}
				list.Add(new PluginResources
				{
					OwnerName = pluginDisplayName,
					ResourceNames = resourceNames
				});
			}
			return list;
		}

		private static void CollectJotunnManagerMappings(string managerTypeName, string collectionMemberName, int priority, HashSet<string> lookupCandidates, Dictionary<string, OwnerMapping> mappings)
		{
			Assembly[] loadedAssemblies = GetLoadedAssemblies();
			foreach (Assembly assembly in loadedAssemblies)
			{
				Type type = SafeGetType(assembly, managerTypeName);
				if (type == null)
				{
					continue;
				}
				object obj = TryGetStaticMemberValue(type, "Instance");
				if (obj == null || !TryGetRawMemberValue(obj, collectionMemberName, out object value))
				{
					continue;
				}
				string fallbackOwnerName = ResolveAssemblyOwnerName(assembly);
				foreach (object item in EnumerateCollectionValues(value))
				{
					string text = NormalizePrefabName(GetPrefabNameFromHolder(item));
					if (lookupCandidates.Contains(text))
					{
						string ownerName = ResolveOwnerNameFromSourceModHolder(item, fallbackOwnerName);
						TryAddOwnerMapping(mappings, text, ownerName, priority);
					}
				}
			}
		}

		private static void CollectLocationManagerTemplateMappings(HashSet<string> lookupCandidates, Dictionary<string, OwnerMapping> mappings)
		{
			Assembly[] loadedAssemblies = GetLoadedAssemblies();
			foreach (Assembly assembly in loadedAssemblies)
			{
				FieldInfo fieldInfo = SafeGetType(assembly, "LocationManager.Location")?.GetField("registeredLocations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (fieldInfo == null)
				{
					continue;
				}
				object value;
				try
				{
					value = fieldInfo.GetValue(null);
				}
				catch
				{
					continue;
				}
				string ownerName = ResolveAssemblyOwnerName(assembly);
				foreach (object item in EnumerateCollectionValues(value))
				{
					string text = NormalizePrefabName(GetPrefabNameFromHolder(item));
					if (lookupCandidates.Contains(text))
					{
						TryAddOwnerMapping(mappings, text, ownerName, 1);
					}
				}
			}
		}

		private static IEnumerable<object> EnumerateCollectionValues(object? value)
		{
			object obj;
			if (!(value is IDictionary dictionary))
			{
				obj = (value as IEnumerable) ?? Array.Empty<object>();
			}
			else
			{
				IEnumerable values = dictionary.Values;
				obj = values;
			}
			IEnumerable enumerable = (IEnumerable)obj;
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					yield return item;
				}
			}
		}

		private static string? GetPrefabNameFromHolder(object? holder)
		{
			if (holder == null)
			{
				return null;
			}
			GameObject val = (GameObject)((holder is GameObject) ? holder : null);
			if (val != null)
			{
				return ((Object)val).name;
			}
			Component val2 = (Component)((holder is Component) ? holder : null);
			if (val2 != null)
			{
				if (!((Object)(object)val2.gameObject != (Object)null))
				{
					return ((Object)val2).name;
				}
				return ((Object)val2.gameObject).name;
			}
			if (TryGetRawMemberValue(holder, "Prefab", out object value))
			{
				GameObject val3 = (GameObject)((value is GameObject) ? value : null);
				if (val3 != null)
				{
					return ((Object)val3).name;
				}
				Component val4 = (Component)((value is Component) ? value : null);
				if (val4 != null)
				{
					if (!((Object)(object)val4.gameObject != (Object)null))
					{
						return ((Object)val4).name;
					}
					return ((Object)val4.gameObject).name;
				}
			}
			string[] array = new string[2] { "Location", "location" };
			foreach (string memberName in array)
			{
				if (!TryGetRawMemberValue(holder, memberName, out object value2))
				{
					continue;
				}
				GameObject val5 = (GameObject)((value2 is GameObject) ? value2 : null);
				if (val5 != null)
				{
					return ((Object)val5).name;
				}
				Component val6 = (Component)((value2 is Component) ? value2 : null);
				if (val6 != null)
				{
					if (!((Object)(object)val6.gameObject != (Object)null))
					{
						return ((Object)val6).name;
					}
					return ((Object)val6.gameObject).name;
				}
			}
			if (TryGetRawMemberValue(holder, "ZoneLocation", out object value3) && value3 != null && TryGetRawMemberValue(value3, "m_prefabName", out object value4))
			{
				return value4?.ToString();
			}
			return null;
		}

		private static string ResolveOwnerNameFromSourceModHolder(object holder, string fallbackOwnerName)
		{
			if (TryGetRawMemberValue(holder, "SourceMod", out object value) && value != null && TryResolvePluginOwnerName(value, out string ownerName))
			{
				return ownerName;
			}
			return fallbackOwnerName;
		}

		private static bool TryResolvePluginOwnerName(object sourceMod, out string ownerName)
		{
			ownerName = "";
			if (TryGetRawMemberValue(sourceMod, "GUID", out object value))
			{
				string text = (value?.ToString() ?? "").Trim();
				if (text.Length > 0 && Chainloader.PluginInfos.TryGetValue(text, out var value2))
				{
					ownerName = GetPluginDisplayName(value2.Metadata.Name, value2.Metadata.GUID);
					return true;
				}
				if (text.Length > 0)
				{
					ownerName = text;
					return true;
				}
			}
			if (TryGetRawMemberValue(sourceMod, "Name", out object value3))
			{
				ownerName = (value3?.ToString() ?? "").Trim();
				return ownerName.Length > 0;
			}
			return false;
		}

		private static string ResolveAssemblyOwnerName(Assembly assembly)
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				Assembly assembly2 = ((object)value.Instance)?.GetType().Assembly;
				if (!(assembly2 == null) && (object)assembly2 == assembly)
				{
					return GetPluginDisplayName(value.Metadata.Name, value.Metadata.GUID);
				}
			}
			return NormalizeOwnerName(assembly.GetName().Name ?? assembly.FullName ?? "");
		}

		private static string GetPluginDisplayName(string? pluginName, string? pluginGuid)
		{
			string text = NormalizeOwnerName(pluginName);
			if (!string.Equals(text, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase))
			{
				return text;
			}
			return NormalizeOwnerName(pluginGuid);
		}

		private static void TryAddOwnerMapping(Dictionary<string, OwnerMapping> mappings, string prefabName, string ownerName, int priority)
		{
			string text = NormalizePrefabName(prefabName);
			string text2 = NormalizeOwnerName(ownerName);
			if (text.Length != 0 && !string.Equals(text2, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase) && (!mappings.TryGetValue(text, out var value) || value.Priority > priority))
			{
				mappings[text] = new OwnerMapping(text2, priority);
			}
		}

		private static void EnsureVanillaCatalogLoaded()
		{
			if (_vanillaCatalogState != VanillaCatalogState.Uninitialized)
			{
				return;
			}
			string[] array = GetVanillaManifestPaths().Where(File.Exists).ToArray();
			if (array.Length == 0)
			{
				_vanillaCatalogState = VanillaCatalogState.Unavailable;
				WarnVanillaCatalogFailure("Vanilla prefab manifests were not found under '" + GetVanillaManifestDirectoryPath() + "'.");
				return;
			}
			int num = 0;
			string text = "";
			Exception ex = null;
			string[] array2 = array;
			foreach (string text2 in array2)
			{
				try
				{
					ReadVanillaManifest(text2);
					num++;
				}
				catch (Exception ex2)
				{
					if (ex == null)
					{
						ex = ex2;
					}
					if (text.Length == 0)
					{
						text = text2;
					}
				}
			}
			if (num > 0)
			{
				_vanillaCatalogState = VanillaCatalogState.Loaded;
				if (ex != null)
				{
					WarnVanillaCatalogFailure("Failed to read one vanilla prefab manifest at '" + text + "'. Other manifests remain available. " + ex.GetType().Name + ": " + ex.Message);
				}
			}
			else
			{
				VanillaPrefabNames.Clear();
				VanillaAssetIds.Clear();
				_vanillaCatalogState = VanillaCatalogState.Unavailable;
				WarnVanillaCatalogFailure("Failed to read vanilla prefab manifests under '" + GetVanillaManifestDirectoryPath() + "'. " + (ex?.GetType().Name ?? "Unknown error") + ": " + (ex?.Message ?? "No manifest could be read."));
			}
		}

		private static void ReadVanillaManifest(string manifestPath)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in File.ReadLines(manifestPath))
			{
				int num = item.IndexOf("asset ID:", StringComparison.OrdinalIgnoreCase);
				if (num >= 0)
				{
					string text = item.Substring(num + "asset ID:".Length).Trim().ToLowerInvariant();
					if (text.Length > 0)
					{
						hashSet2.Add(text);
					}
				}
				int num2 = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase);
				if (num2 < 0)
				{
					continue;
				}
				string text2 = item.Substring(num2 + "path in bundle:".Length).Trim();
				if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2);
					if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension))
					{
						hashSet.Add(fileNameWithoutExtension.Trim());
					}
				}
			}
			VanillaPrefabNames.UnionWith(hashSet);
			VanillaAssetIds.UnionWith(hashSet2);
		}

		private static string BuildSnapshotSignature(IReadOnlyCollection<AltarPrefabOwnerSource> prefabSources)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("location-owner-v4");
			foreach (AltarPrefabOwnerSource prefabSource in prefabSources)
			{
				stringBuilder.Append("prefab:").Append(prefabSource.PrefabName).Append(':')
					.Append(prefabSource.SourcePrefabName)
					.Append(':')
					.Append(GetStableAssetIdSignature(prefabSource.PrefabAssetId))
					.AppendLine();
			}
			foreach (PluginInfo item in Chainloader.PluginInfos.Values.OrderBy<PluginInfo, string>((PluginInfo pluginInfo) => pluginInfo.Metadata.GUID ?? "", StringComparer.OrdinalIgnoreCase))
			{
				Assembly assembly = ((object)item.Instance)?.GetType().Assembly;
				stringBuilder.Append("plugin:").Append(item.Metadata.GUID ?? "").Append(':')
					.Append(item.Metadata.Name ?? "")
					.Append(':')
					.Append(GetAssemblyModuleVersionId(assembly))
					.AppendLine();
			}
			Assembly[] loadedAssemblies = GetLoadedAssemblies();
			foreach (Assembly assembly2 in loadedAssemblies)
			{
				stringBuilder.Append("assembly:").Append(assembly2.FullName ?? assembly2.GetName().Name ?? "").Append(':')
					.Append(GetAssemblyModuleVersionId(assembly2))
					.AppendLine();
			}
			try
			{
				foreach (string item2 in (from bundle in AssetBundle.GetAllLoadedAssetBundles()
					select ((bundle != null) ? ((Object)bundle).name : null) ?? "" into bundleName
					where bundleName.Length > 0
					select bundleName).OrderBy<string, string>((string bundleName) => bundleName, StringComparer.OrdinalIgnoreCase))
				{
					stringBuilder.Append("bundle:").AppendLine(item2);
				}
			}
			catch
			{
				stringBuilder.AppendLine("bundle:<unavailable>");
			}
			foreach (string vanillaManifestPath in GetVanillaManifestPaths())
			{
				stringBuilder.Append("vanilla:").Append(Path.GetFileName(vanillaManifestPath)).Append(':');
				if (File.Exists(vanillaManifestPath))
				{
					FileInfo fileInfo = new FileInfo(vanillaManifestPath);
					stringBuilder.Append(fileInfo.Length).Append(':').Append(fileInfo.LastWriteTimeUtc.Ticks)
						.AppendLine();
				}
				else
				{
					stringBuilder.AppendLine("<missing>");
				}
			}
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString()));
			StringBuilder stringBuilder2 = new StringBuilder(array.Length * 2);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder2.Append(b.ToString("x2"));
			}
			return stringBuilder2.ToString();
		}

		private static string GetStableAssetIdSignature(string assetId)
		{
			string text = (assetId ?? "").Trim().ToLowerInvariant();
			if (!text.StartsWith("000000010000000100000001", StringComparison.Ordinal))
			{
				return text;
			}
			return "<runtime>";
		}

		private static Assembly[] GetLoadedAssemblies()
		{
			return (from assembly in AppDomain.CurrentDomain.GetAssemblies()
				where !assembly.IsDynamic
				select assembly).OrderBy<Assembly, string>((Assembly assembly) => assembly.FullName ?? assembly.GetName().Name ?? "", StringComparer.OrdinalIgnoreCase).ToArray();
		}

		private static string GetAssemblyModuleVersionId(Assembly? assembly)
		{
			if (assembly == null)
			{
				return "";
			}
			try
			{
				return assembly.ManifestModule.ModuleVersionId.ToString("N");
			}
			catch
			{
				return "";
			}
		}

		private static bool TryLoadSnapshotFromCache(string signature, IReadOnlyCollection<string> prefabNames, out Dictionary<string, string> owners)
		{
			owners = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			string cachePath = GetCachePath();
			if (!File.Exists(cachePath))
			{
				return false;
			}
			try
			{
				string[] array = File.ReadAllLines(cachePath);
				if (array.Length < 2 || !string.Equals(array[0], "v1", StringComparison.Ordinal) || !string.Equals(array[1], signature, StringComparison.Ordinal))
				{
					return false;
				}
				for (int i = 2; i < array.Length; i++)
				{
					string[] array2 = array[i].Split(new char[1] { '\t' });
					if (array2.Length == 2)
					{
						string text = DecodeCacheField(array2[0]);
						string value = NormalizeOwnerName(DecodeCacheField(array2[1]));
						if (text.Length > 0)
						{
							owners[text] = value;
						}
					}
				}
				return prefabNames.All(owners.ContainsKey) && owners.Values.All((string ownerName) => !string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase));
			}
			catch
			{
				owners.Clear();
				return false;
			}
		}

		private static void SaveSnapshotToCache(string signature, IReadOnlyDictionary<string, string> owners)
		{
			if (owners.Values.Any((string ownerName) => string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase)))
			{
				DeleteSnapshotCache();
				return;
			}
			try
			{
				string cachePath = GetCachePath();
				Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("v1");
				stringBuilder.AppendLine(signature);
				foreach (KeyValuePair<string, string> item in owners.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> pair) => pair.Key, StringComparer.OrdinalIgnoreCase))
				{
					stringBuilder.Append(EncodeCacheField(item.Key)).Append('\t').Append(EncodeCacheField(item.Value))
						.AppendLine();
				}
				string text = stringBuilder.ToString();
				if (!string.Equals(File.Exists(cachePath) ? File.ReadAllText(cachePath) : "", text, StringComparison.Ordinal))
				{
					File.WriteAllText(cachePath, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				}
			}
			catch (Exception ex)
			{
				if (!_cacheWarningLogged)
				{
					_cacheWarningLogged = true;
					BossRulesPlugin.BossRulesLogger.LogWarning((object)("Failed to cache altar prefab owner mappings. " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		private static void DeleteSnapshotCache()
		{
			try
			{
				string cachePath = GetCachePath();
				if (File.Exists(cachePath))
				{
					File.Delete(cachePath);
				}
			}
			catch (Exception ex)
			{
				if (!_cacheWarningLogged)
				{
					_cacheWarningLogged = true;
					BossRulesPlugin.BossRulesLogger.LogWarning((object)("Failed to invalidate altar prefab owner cache. " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		private static string GetCachePath()
		{
			return Path.Combine(BossRulesPlugin.ConfigDirectoryPath, "cache", ".altar-prefab-owner-cache.txt");
		}

		private static IEnumerable<string> GetVanillaManifestPaths()
		{
			string manifestDirectoryPath = GetVanillaManifestDirectoryPath();
			yield return Path.Combine(manifestDirectoryPath, "manifest");
			yield return Path.Combine(manifestDirectoryPath, "manifest_extended");
		}

		private static string GetVanillaManifestDirectoryPath()
		{
			return Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef");
		}

		private static string EncodeCacheField(string value)
		{
			return Convert.ToBase64String(Encoding.UTF8.GetBytes(value ?? ""));
		}

		private static string DecodeCacheField(string value)
		{
			try
			{
				return Encoding.UTF8.GetString(Convert.FromBase64String(value ?? ""));
			}
			catch
			{
				return "";
			}
		}

		private static AltarPrefabOwnerSnapshot BuildUnknownSnapshot(IEnumerable<string> prefabNames)
		{
			return new AltarPrefabOwnerSnapshot(prefabNames.ToDictionary<string, string, string>((string prefabName) => prefabName, (string _) => "Unknown / Untracked", StringComparer.OrdinalIgnoreCase));
		}

		private static void WarnResolverFailure(Exception exception)
		{
			if (!_resolverWarningLogged)
			{
				_resolverWarningLogged = true;
				BossRulesPlugin.BossRulesLogger.LogWarning((object)("Failed to resolve altar prefab owners; reference entries will be grouped under 'Unknown / Untracked'. " + exception.GetType().Name + ": " + exception.Message));
			}
		}

		private static void WarnVanillaCatalogFailure(string reason)
		{
			if (!_vanillaCatalogWarningLogged)
			{
				_vanillaCatalogWarningLogged = true;
				BossRulesPlugin.BossRulesLogger.LogWarning((object)(reason + " Unresolved altar location owners will be grouped under 'Unknown / Untracked'."));
			}
		}

		private static Type? SafeGetType(Assembly assembly, string fullTypeName)
		{
			try
			{
				return assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false);
			}
			catch
			{
				return null;
			}
		}

		private static object? TryGetStaticMemberValue(Type type, string memberName)
		{
			try
			{
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property.GetValue(null, null);
				}
				return type.GetField(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
			}
			catch
			{
				return null;
			}
		}

		private static bool TryGetRawMemberValue(object instance, string memberName, out object? value)
		{
			value = null;
			Type type = instance.GetType();
			while (type != null)
			{
				try
				{
					PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						value = property.GetValue(instance, null);
						return true;
					}
					FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						value = field.GetValue(instance);
						return true;
					}
				}
				catch
				{
					return false;
				}
				type = type.BaseType;
			}
			return false;
		}

		private static string NormalizePrefabName(string? prefabName)
		{
			return TrimCloneSuffix((prefabName ?? "").Trim());
		}

		private static string TrimCloneSuffix(string prefabName)
		{
			if (!prefabName.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return prefabName;
			}
			return prefabName.Substring(0, prefabName.Length - "(Clone)".Length).TrimEnd(Array.Empty<char>());
		}
	}
	internal sealed class BossRuleConfigurationSection
	{
		[YamlMember(Order = 1)]
		public BossDespawnConfigurationDefinition? Despawn { get; set; }

		[YamlMember(Order = 2)]
		public BossTamedPressureDefinition? BossTamedPressure { get; set; }

		[YamlMember(Order = 3)]
		public BossRuleLocalizationDefinition? Localization { get; set; }
	}
	internal sealed class BossDespawnConfigurationDefinition
	{
		[YamlMember(Order = 1)]
		public string? Defaults { get; set; }

		[YamlMember(Order = 2)]
		public List<string>? Rules { get; set; }
	}
	internal sealed class BossRuleLocalizationDefinition
	{
		[YamlMember(Order = 1)]
		public string? MessageDespawnStart { get; set; }

		[YamlMember(Order = 2)]
		public string? MessageDespawnReminder { get; set; }

		[YamlMember(Order = 3)]
		public string? MessageDespawnCanceled { get; set; }

		[YamlMember(Order = 4)]
		public string? MessageBossTamedPressure { get; set; }

		[YamlMember(Order = 5)]
		public string? MessageForsakenPowerRotate { get; set; }
	}
	internal sealed class BossDespawnDefinition
	{
		internal string Prefab { get; }

		public float? DespawnRange { get; set; }

		public float? DespawnDelay { get; set; }

		public bool? Refunds { get; set; }

		internal BossDespawnDefinition(string prefab, float? despawnRange, float? despawnDelay, bool? refunds)
		{
			Prefab = prefab;
			DespawnRange = despawnRange;
			DespawnDelay = despawnDelay;
			Refunds = refunds;
		}
	}
	internal sealed class BossRuleConfigurationState
	{
		internal const float FallbackDespawnRange = 64f;

		internal const float FallbackDespawnDelaySeconds = 90f;

		internal static BossRuleConfigurationState Empty => new BossRuleConfigurationState();

		internal float DefaultDespawnRange { get; set; } = 64f;

		internal float DefaultDespawnDelaySeconds { get; set; } = 90f;

		internal List<BossDespawnDefinition> DespawnRules { get; } = new List<BossDespawnDefinition>();

		internal BossTamedPressureDefinition? BossTamedPressureRule { get; set; }
	}
	internal sealed class BossTamedPressureDefinition
	{
		[YamlMember(Order = 1)]
		public List<string>? BossPrefabs { get; set; }

		[YamlMember(Order = 2)]
		public List<string>? ExcludedBossPrefabs { get; set; }

		[YamlMember(Order = 3)]
		public BossTamedPressureTargetsDefinition? Targets { get; set; }

		[YamlMember(Order = 4)]
		public BossTamedPressurePressureDefinition? Pressure { get; set; }
	}
	internal sealed class BossTamedPressureTargetsDefinition
	{
		[YamlMember(Order = 1)]
		public float? Range { get; set; }

		[YamlMember(Order = 2)]
		public int? MaxPerBoss { get; set; }

		[YamlMember(Order = 3)]
		public List<string>? ExcludedTamedPrefabs { get; set; }

		[YamlMember(Order = 4)]
		public List<string>? ExtraPressuredPrefabs { get; set; }
	}
	internal sealed class BossTamedPressurePressureDefinition
	{
		[YamlMember(Order = 1)]
		public float? DamagePercentPerSecond { get; set; }

		[YamlMember(Order = 2)]
		public float? IncomingDamageMultiplier { get; set; }

		[YamlMember(Order = 3)]
		public float? OutgoingDamageMultiplier { get; set; }
	}
	internal static class BossRuleConfiguration
	{
		private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		private static bool _reportedLegacyLocalization;

		internal static bool TryParse(string yaml, string source, out BossRuleConfigurationState state)
		{
			state = BossRuleConfigurationState.Empty;
			try
			{
				BossRuleConfigurationSection bossRuleConfigurationSection = (string.IsNullOrWhiteSpace(yaml) ? new BossRuleConfigurationSection() : Deserializer.Deserialize<BossRuleConfigurationSection>(yaml));
				state = Normalize(bossRuleConfigurationSection ?? new BossRuleConfigurationSection());
				BossRulesPlugin.BossRulesLogger.LogInfo((object)$"Loaded boss rules YAML from {source}: {state.DespawnRules.Count} despawn entries, {((state.BossTamedPressureRule != null) ? 1 : 0)} boss tamed pressure entries.");
				return true;
			}
			catch (Exception ex)
			{
				BossRulesPlugin.BossRulesLogger.LogError((object)("Rejected boss rules YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
		}

		private static BossRuleConfigurationState Normalize(BossRuleConfigurationSection section)
		{
			BossRuleConfigurationState bossRuleConfigurationState = new BossRuleConfigurationState();
			(bossRuleConfigurationState.DefaultDespawnRange, bossRuleConfigurationState.DefaultDespawnDelaySeconds) = ParseDespawnDefaults(section.Despawn?.Defaults);
			foreach (string item in section.Despawn?.Rules ?? new List<string>())
			{
				bossRuleConfigurationState.DespawnRules.Add(ParseDespawnRule(item));
			}
			if (section.BossTamedPressure != null)
			{
				NormalizeBossTamedPressure(section.BossTamedPressure);
				bossRuleConfigurationState.BossTamedPressureRule = section.BossTamedPressure;
			}
			if (section.Localization != null && !_reportedLegacyLocalization)
			{
				_reportedLegacyLocalization = true;
				BossRulesPlugin.BossRulesLogger.LogWarning((object)"BossRules.yml 'localization' is deprecated and ignored. Use BossRules.<Language>.yml or .json localization files instead.");
			}
			return bossRuleConfigurationState;
		}

		private static (float Range, float DelaySeconds) ParseDespawnDefaults(string? rawDefaults)
		{
			if (string.IsNullOrWhiteSpace(rawDefaults))
			{
				return (Range: 64f, DelaySeconds: 90f);
			}
			string text = rawDefaults.Trim();
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length > 2)
			{
				throw new FormatException("despawn.defaults '" + text + "' has too many values. Expected 'range, delaySeconds'.");
			}
			float item = ParseDefaultFloat(array, 0, "range", text, 64f);
			float item2 = ParseDefaultFloat(array, 1, "delaySeconds", text, 90f);
			return (Range: item, DelaySeconds: item2);
		}

		private static float ParseDefaultFloat(string[] parts, int index, string fieldName, string rawDefaults, float fallback)
		{
			if (parts.Length <= index)
			{
				return fallback;
			}
			string text = parts[index].Trim();
			if (text.Length == 0)
			{
				return fallback;
			}
			if (float.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			throw new FormatException("despawn.defaults '" + rawDefaults + "' has invalid " + fieldName + " value '" + text + "'.");
		}

		private static BossDespawnDefinition ParseDespawnRule(string rawRule)
		{
			string text = (rawRule ?? "").Trim();
			if (text.Length == 0)
			{
				throw new FormatException("Empty despawn row. Expected '- prefab, despawnRange, despawnDelay, refunds'.");
			}
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length > 4)
			{
				throw new FormatException("Despawn row '" + text + "' has too many values. Expected '- prefab, despawnRange, despawnDelay, refunds'.");
			}
			string text2 = array[0].Trim();
			if (text2.Length == 0)
			{
				throw new FormatException("Despawn row '" + text + "' has an empty prefab name.");
			}
			return new BossDespawnDefinition(text2, ParseOptionalFloat(array, 1, "despawnRange", text), ParseOptionalFloat(array, 2, "despawnDelay", text), ParseOptionalBool(array, 3, "refunds", text));
		}

		private static float? ParseOptionalFloat(string[] parts, int index, string fieldName, string rawRule)
		{
			if (parts.Length <= index)
			{
				return null;
			}
			string text = parts[index].Trim();
			if (text.Length == 0)
			{
				return null;
			}
			if (float.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			throw new FormatException("Despawn row '" + rawRule + "' has invalid " + fieldName + " value '" + text + "'.");
		}

		private static bool? ParseOptionalBool(string[] parts, int index, string fieldName, string rawRule)
		{
			if (parts.Length <= index)
			{
				return null;
			}
			string text = parts[index].Trim();
			if (text.Length == 0)
			{
				return null;
			}
			if (bool.TryParse(text, out var result))
			{
				return result;
			}
			throw new FormatException("Despawn row '" + rawRule + "' has invalid " + fieldName + " value '" + text + "'. Use true or false.");
		}

		private static void NormalizeBossTamedPressure(BossTamedPressureDefinition? definition)
		{
			if (definition != null)
			{
				definition.BossPrefabs = NormalizeStringList(definition.BossPrefabs);
				definition.ExcludedBossPrefabs = NormalizeStringList(definition.ExcludedBossPrefabs);
				if (definition.Targets != null)
				{
					definition.Targets.ExcludedTamedPrefabs = NormalizeStringList(definition.Targets.ExcludedTamedPrefabs);
					definition.Targets.ExtraPressuredPrefabs = NormalizeStringList(definition.Targets.ExtraPressuredPrefabs);
				}
			}
		}

		private static List<string>? NormalizeStringList(List<string>? values)
		{
			List<string> list = (from value in values?.Select((string value) => (value ?? "").Trim())
				where value.Length > 0
				select value).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
			if (list == null || list.Count <= 0)
			{
				return null;
			}
			return list;
		}
	}
	internal static class BossRuleConfigurationFiles
	{
		internal static void EnsureDefaultFile()
		{
			EnsureTextFile(BossRulesPlugin.RulesYamlFilePath, BuildDefaultRulesYaml());
		}

		private static void EnsureTextFile(string path, string content)
		{
			if (!File.Exists(path))
			{
				File.WriteAllText(path, content);
				BossRulesPlugin.BossRulesLogger.LogInfo((object)("Created " + path + "."));
			}
		}

		private static string BuildDefaultRulesYaml()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# BossRules runtime rules");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("despawn:");
			stringBuilder.AppendLine("  defaults: 64, 90 # default despawnRange, despawnDelaySeconds.");
			stringBuilder.AppendLine("  rules:");
			stringBuilder.AppendLine("  # - prefab, despawnRange, despawnDelay, refunds");
			stringBuilder.AppendLine("  #   Empty or omitted despawnRange/despawnDelay uses despawn.defaults.");
			stringBuilder.AppendLine("  #   despawnRange: 0 disables despawn for that prefab.");
			stringBuilder.AppendLine("  #   refunds omitted or empty: true. Use false to disable altar offering refunds.");
			stringBuilder.AppendLine("  - Fader, 64, 90, true # Boss prefabs are auto-detected, but non-boss Character prefabs can also be listed here for despawn rules.");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("bossTamedPressure:");
			stringBuilder.AppendLine("  bossPrefabs: [Eikthyr] # Extra source boss prefabs added to the auto-detected boss set");
			stringBuilder.AppendLine("  excludedBossPrefabs: [] # Boss prefabs to ignore from auto-detected and bossPrefabs sources");
			stringBuilder.AppendLine("  targets:");
			stringBuilder.AppendLine("    range: 32 # Clamp: 0~128. Horizontal XZ range around each boss");
			stringBuilder.AppendLine("    maxPerBoss: 4 # Clamp: 1~128. Maximum pressured targets per boss per scan");
			stringBuilder.AppendLine("    excludedTamedPrefabs: [] # Tamed MonsterAI prefabs excluded from the default pressured target set");
			stringBuilder.AppendLine("    extraPressuredPrefabs: [] # Character prefabs pressured even when not tamed");
			stringBuilder.AppendLine("  pressure:");
			stringBuilder.AppendLine("    damagePercentPerSecond: 0.01 # Clamp: 0~1. 0.01 = 1% of max health per second");
			stringBuilder.AppendLine("    incomingDamageMultiplier: 1.25 # Clamp: 0~10. Multiplies damage received while affected");
			stringBuilder.AppendLine("    outgoingDamageMultiplier: 0.75 # Clamp: 0~10. Multiplies damage dealt while affected");
			return stringBuilder.ToString();
		}
	}
	internal static class BossRulesConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__InspectRuntimeTarget;

			public static ConsoleOptionsFetcher <1>__GetInspectTabOptions;

			public static ConsoleEvent <2>__HandleBossStoneCommand;

			public static ConsoleOptionsFetcher <3>__GetBossStoneTabOptions;
		}

		private const string InspectCommandName = "bossrules:inspect";

		private const string BossStoneCommandName = "bossrules:bossstone";

		private static readonly List<string> InspectTabOptions = new List<string> { "bossstone" };

		private static readonly List<string> BossStoneTabOptions = new List<string> { "reset" };

		private static bool _registered;

		internal static void Register()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__InspectRuntimeTarget;
				if (obj == null)
				{
					ConsoleEvent val = InspectRuntimeTarget;
					<>O.<0>__InspectRuntimeTarget = val;
					obj = (object)val;
				}
				object obj2 = <>O.<1>__GetInspectTabOptions;
				if (obj2 == null)
				{
					ConsoleOptionsFetcher val2 = GetInspectTabOptions;
					<>O.<1>__GetInspectTabOptions = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("bossrules:inspect", "Inspect the current hovered/aimed BossRules runtime target. Currently supports: bossstone.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
				object obj3 = <>O.<2>__HandleBossStoneCommand;
				if (obj3 == null)
				{
					ConsoleEvent val3 = HandleBossStoneCommand;
					<>O.<2>__HandleBossStoneCommand = val3;
					obj3 = (object)val3;
				}
				object obj4 = <>O.<3>__GetBossStoneTabOptions;
				if (obj4 == null)
				{
					ConsoleOptionsFetcher val4 = GetBossStoneTabOptions;
					<>O.<3>__GetBossStoneTabOptions = val4;
					obj4 = (object)val4;
				}
				new ConsoleCommand("bossrules:bossstone", "Reset per-player boss stone state. Syntax: bossrules:bossstone reset <exactPlayerName>", (ConsoleEvent)obj3, true, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, true);
			}
		}

		private static List<string> GetInspectTabOptions()
		{
			return InspectTabOptions;
		}

		private static List<string> GetBossStoneTabOptions()
		{
			return BossStoneTabOptions;
		}

		private static void InspectRuntimeTarget(ConsoleEventArgs args)
		{
			string[] lines;
			string error;
			if (((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "") != "bossstone")
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Syntax: bossrules:inspect bossstone");
				}
			}
			else if (BossStonePerPlayerRuntime.TryInspectCurrentTarget(out lines, out error))
			{
				string[] array = lines;
				foreach (string text in array)
				{
					Terminal context2 = args.Context;
					if (context2 != null)
					{
						context2.AddString(text);
					}
				}
			}
			else
			{
				Terminal context3 = args.Context;
				if (context3 != null)
				{
					context3.AddString(error);
				}
			}
		}

		private static void HandleBossStoneCommand(ConsoleEventArgs args)
		{
			if (((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "") != "reset")
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Syntax: bossrules:bossstone reset <exactPlayerName>");
				}
				return;
			}
			BossStonePerPlayerRuntime.TryRequestReset((args.FullLine.Length > "bossrules:bossstone reset".Length) ? args.FullLine.Substring("bossrules:bossstone reset".Length).Trim() : "", out string message);
			Terminal context2 = args.Context;
			if (context2 != null)
			{
				context2.AddString(message);
			}
		}
	}
	internal static class BossRulesLocalization
	{
		internal const string MessageDespawnStartKey = "bossrules_message_despawn_start";

		internal const string MessageDespawnReminderKey = "bossrules_message_despawn_reminder";

		internal const string MessageDespawnCanceledKey = "bossrules_message_despawn_canceled";

		internal const string MessageBossTamedPressureKey = "bossrules_message_boss_tamed_pressure";

		internal const string MessageForsakenPowerRotateKey = "bossrules_message_forsaken_power_rotate";

		private static readonly Dictionary<string, string> EnglishDefaults = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["bossrules_message_despawn_start"] = "{name} will despawn in {seconds}s unless someone returns.",
			["bossrules_message_despawn_reminder"] = "{name} will despawn in {seconds}s.",
			["bossrules_message_despawn_canceled"] = "{name} despawn canceled.",
			["bossrules_message_boss_tamed_pressure"] = "Tamed creatures near a boss are weakened.",
			["bossrules_message_forsaken_power_rotate"] = "Rotate"
		};

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

		private static readonly HashSet<string> MissingEnglishNameCache = new HashSet<string>(StringComparer.Ordinal);

		internal static string Text(string key)
		{
			string text = NormalizeKey(key);
			Localization instance = Localization.instance;
			if (instance != null && instance.m_translations.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value))
			{
				return value;
			}
			if (Localizer.TryGetEnglishText(text, out string text2))
			{
				return text2;
			}
			if (!EnglishDefaults.TryGetValue(text, out string value2))
			{
				return text;
			}
			return value2;
		}

		internal static string FormatDespawnMessage(string messageKey, string? nameLocalizationKey, string? prefabName, int remainingSeconds)
		{
			string newValue = ResolveCharacterName(nameLocalizationKey, prefabName);
			return Text(messageKey).Replace("{name}", newValue).Replace("{seconds}", Math.Max(0, remainingSeconds).ToString(CultureInfo.InvariantCulture));
		}

		internal static string ResolveCharacterName(string? nameLocalizationKey, string? prefabName)
		{
			string text = (nameLocalizationKey ?? "").Trim();
			if (text.Length == 0)
			{
				text = ResolveNameTokenFromPrefab(prefabName);
			}
			if (text.Length > 0 && !text.StartsWith("$", StringComparison.Ordinal))
			{
				Localization instance = Localization.instance;
				string text2 = ((instance != null) ? instance.Localize(text) : null) ?? text;
				if (IsUsableTranslation(text2, text))
				{
					return text2;
				}
			}
			string text3 = NormalizeKey(text);
			if (text3.Length > 0)
			{
				Localization instance2 = Localization.instance;
				if (instance2 != null && instance2.m_translations.TryGetValue(text3, out var value) && IsUsableTranslation(value, text3))
				{
					return value;
				}
				if (TryGetEnglishCharacterName(instance2, text3, out string englishText))
				{
					return englishText;
				}
			}
			string text4 = Utils.GetPrefabName(prefabName ?? "").Trim();
			if (text4.Length <= 0)
			{
				return "Target";
			}
			return text4;
		}

		private static string ResolveNameTokenFromPrefab(string? prefabName)
		{
			string text = Utils.GetPrefabName(prefabName ?? "").Trim();
			if (text.Length == 0)
			{
				return "";
			}
			ZNetScene instance = ZNetScene.instance;
			GameObject obj = ((instance != null) ? instance.GetPrefab(text) : null);
			return ((obj != null) ? obj.GetComponent<Character>() : null)?.m_name?.Trim() ?? "";
		}

		private static bool TryGetEnglishCharacterName(Localization? localization, string key, out string englishText)
		{
			if (EnglishNameCache.TryGetValue(key, out englishText))
			{
				return true;
			}
			englishText = "";
			if (localization == null || MissingEnglishNameCache.Contains(key))
			{
				return false;
			}
			if (Localizer.TryGetLoadedAssemblyEnglishText(key, out string text))
			{
				EnglishNameCache[key] = text;
				englishText = text;
				return true;
			}
			try
			{
				string text2 = localization.TranslateSingleId(key, "English") ?? "";
				if (!IsUsableTranslation(text2, key))
				{
					MissingEnglishNameCache.Add(key);
					return false;
				}
				EnglishNameCache[key] = text2;
				englishText = text2;
				return true;
			}
			catch (Exception ex)
			{
				MissingEnglishNameCache.Add(key);
				BossRulesPlugin.BossRulesLogger.LogDebug((object)("English localization lookup failed for '$" + key + "'. " + ex.GetType().Name + ": " + ex.Message));
				ret