Decompiled source of NpcSpawner v1.7.1

plugins\NpcSpawner.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using ProjectM;
using ProjectM.CastleBuilding;
using ProjectM.Network;
using ProjectM.Scripting;
using Stunlock.Core;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using VCF.Core.Basics;
using VampireCommandFramework;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("NpcSpawner")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Server-side V Rising mod: spawns configured NPC groups around a random online player at random times and gives configurable loot on kill.")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyInformationalVersion("1.7.1")]
[assembly: AssemblyProduct("NpcSpawner")]
[assembly: AssemblyTitle("NpcSpawner")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.7.1.0")]
[module: UnverifiableCode]
namespace NpcSpawner
{
	internal sealed class SpawnGroup
	{
		[JsonPropertyName("RandomAround")]
		public float RandomAround { get; set; } = 8f;

		[JsonPropertyName("Prefabs")]
		public Dictionary<string, int> Prefabs { get; set; } = new Dictionary<string, int>();
	}
	internal sealed class LootEntry
	{
		[JsonPropertyName("ItemId")]
		public int ItemId { get; set; }

		[JsonPropertyName("Amount")]
		public int Amount { get; set; } = 1;

		[JsonPropertyName("Chance")]
		public float Chance { get; set; } = 1f;
	}
	[BepInPlugin("agenthere.npcspawner", "NPC Spawner", "1.7.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		public const string PluginGuid = "agenthere.npcspawner";

		public const string PluginName = "NPC Spawner";

		public const string PluginVersion = "1.7.1";

		public const string VcfGuid = "gg.deca.VampireCommandFramework";

		internal static Plugin Instance;

		internal static Harmony Harmony;

		internal static ManualLogSource Logger;

		public override void Load()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Expected O, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Expected O, but got Unknown
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			Instance = this;
			Logger = ((BasePlugin)this).Log;
			ManualLogSource logger = Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(26, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("NPC Spawner");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.7.1");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loading...");
			}
			logger.LogInfo(val);
			try
			{
				Directory.CreateDirectory(SpawnConfig.Dir);
				Settings.Init(new ConfigFile(Path.Combine(SpawnConfig.Dir, "NpcSpawner.cfg"), true));
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(23, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] settings: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				logger2.LogError(val2);
			}
			try
			{
				SpawnConfig.LoadAll();
				ManualLogSource logger3 = Logger;
				val = new BepInExInfoLogInterpolatedStringHandler(25, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] config dir: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(SpawnConfig.Dir);
				}
				logger3.LogInfo(val);
				ManualLogSource logger4 = Logger;
				val = new BepInExInfoLogInterpolatedStringHandler(49, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] configs: npcs=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SpawnConfig.Groups.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" groups, loot=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SpawnConfig.Loot.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" entries");
				}
				logger4.LogInfo(val);
			}
			catch (Exception ex2)
			{
				ManualLogSource logger5 = Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(21, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] config: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex2);
				}
				logger5.LogError(val2);
			}
			try
			{
				Harmony = new Harmony("agenthere.npcspawner");
				Harmony.PatchAll(Assembly.GetExecutingAssembly());
			}
			catch (Exception ex3)
			{
				ManualLogSource logger6 = Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(29, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] patches failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex3);
				}
				logger6.LogError(val2);
			}
			try
			{
				CommandRegistry.Middlewares.Add((CommandMiddleware)new BasicAdminCheck());
				Logger.LogInfo((object)"[NpcSpawner] VCF admin-only middleware installed.");
			}
			catch (Exception ex4)
			{
				ManualLogSource logger7 = Logger;
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[NpcSpawner] middleware not installed: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex4.Message);
				}
				logger7.LogWarning(val3);
			}
			try
			{
				CommandRegistry.RegisterAll();
				Logger.LogInfo((object)"[NpcSpawner] chat commands registered (.npcs help)");
			}
			catch (Exception ex5)
			{
				ManualLogSource logger8 = Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(30, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] commands failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex5);
				}
				logger8.LogError(val2);
			}
			ManualLogSource logger9 = Logger;
			val = new BepInExInfoLogInterpolatedStringHandler(23, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("NPC Spawner");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.7.1");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded.");
			}
			logger9.LogInfo(val);
		}

		public override bool Unload()
		{
			try
			{
				SpawnerService.Shutdown();
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
			try
			{
				CommandRegistry.UnregisterAssembly();
			}
			catch (Exception ex2)
			{
				Logger.LogError((object)ex2);
			}
			try
			{
				Harmony harmony = Harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex3)
			{
				Logger.LogError((object)ex3);
			}
			return true;
		}
	}
	internal static class Settings
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> IntervalMin;

		public static ConfigEntry<float> IntervalMax;

		public static ConfigEntry<bool> IntervalScalesWithPlayers;

		public static ConfigEntry<float> LifetimeSeconds;

		public static ConfigEntry<bool> BlockNearCastles;

		public static ConfigEntry<float> CastleBlockRadius;

		public static ConfigEntry<float> CastleHeartRadius;

		public static ConfigEntry<bool> SkipPlayersInCombat;

		public static ConfigEntry<string> CombatBuffGuids;

		public static ConfigEntry<bool> Announce;

		public static ConfigEntry<string> AnnounceMessage;

		public static ConfigEntry<bool> GlobalAnnounce;

		public static ConfigEntry<string> GlobalSpawnMessage;

		public static ConfigEntry<bool> LootEnabled;

		public static ConfigEntry<bool> NotifyKiller;

		public static ConfigEntry<string> NotifyMessage;

		public static ConfigEntry<bool> SuccessAnnounce;

		public static ConfigEntry<string> SuccessMessage;

		public static void Init(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("General", "Enabled", true, "Включён ли автоматический случайный спавн НПС.");
			IntervalMin = cfg.Bind<float>("General", "IntervalMinSeconds", 300f, "Минимальный интервал между случайными спавнами, секунды.");
			IntervalMax = cfg.Bind<float>("General", "IntervalMaxSeconds", 600f, "Максимальный интервал между случайными спавнами, секунды.");
			IntervalScalesWithPlayers = cfg.Bind<bool>("General", "IntervalScalesWithPlayers", false, "Если true — интервал делится на число игроков онлайн (больше игроков -> чаще спавн).");
			LifetimeSeconds = cfg.Bind<float>("General", "LifetimeSeconds", 0f, "Сколько секунд живёт заспавненный НПС. 0 или меньше — бессрочно (до убийства).");
			BlockNearCastles = cfg.Bind<bool>("Spawn", "BlockNearCastles", true, "Запрещать спавн в замках игроков и под крышами (рядом со плитами замка).");
			CastleBlockRadius = cfg.Bind<float>("Spawn", "CastleBlockRadius", 4f, "Радиус в метрах вокруг плит замка, где спавн запрещён (при BlockNearCastles = true).");
			CastleHeartRadius = cfg.Bind<float>("Spawn", "CastleHeartRadius", 30f, "Радиус в метрах вокруг СЕРДЦА замка, где спавн запрещён (любой этаж). Основная защита от спавна в замках.");
			SkipPlayersInCombat = cfg.Bind<bool>("Spawn", "SkipPlayersInCombat", true, "Не давать спавнить НПС у игрока, который находится в бою.");
			CombatBuffGuids = cfg.Bind<string>("Spawn", "CombatBuffGuids", "581443919,697095869", "Список id баффов 'в бою' через запятую (обычный бой и PvP-бой). Менять только если игра обновила их.");
			Announce = cfg.Bind<bool>("Messages", "Announce", true, "Сообщать ЛИЧНО игроку, что рядом появилась группа НПС.");
			AnnounceMessage = cfg.Bind<string>("Messages", "AnnounceMessage", "<color=#ff9d00>Рядом появилась группа «{0}»: врагов {1}. Убей их ради добычи!</color>", "Шаблон личного анонса. {0} — имя группы, {1} — число врагов.");
			GlobalAnnounce = cfg.Bind<bool>("Messages", "GlobalAnnounce", true, "Сообщать ВСЕМ игрокам, что у кого-то произошёл случайный спавн группы.");
			GlobalSpawnMessage = cfg.Bind<string>("Messages", "GlobalSpawnMessage", "<color=#ff5a5a>Случайный спавн! У игрока {0} появилась группа «{1}» — врагов {2}.</color>", "Шаблон сообщения всем игрокам о случайном спавне. {0} — ник игрока, {1} — имя группы, {2} — число врагов.");
			LootEnabled = cfg.Bind<bool>("Loot", "Enabled", true, "Выдавать настраиваемый лут за убийство НПС (см. loot.json).");
			NotifyKiller = cfg.Bind<bool>("Loot", "NotifyKiller", true, "Сообщать игроку в чат, какой лут он получил.");
			NotifyMessage = cfg.Bind<string>("Loot", "NotifyMessage", "<color=#ffd700>Добыча: {0} шт. (id {1}).</color>", "Шаблон сообщения о добыче. {0} — количество, {1} — id предмета.");
			SuccessAnnounce = cfg.Bind<bool>("Messages", "SuccessAnnounce", true, "Сообщать ВСЕМ игрокам, что игрок справился с задачей (убил всю группу).");
			SuccessMessage = cfg.Bind<string>("Messages", "SuccessMessage", "<color=#3cff8a>{0} справился с задачей — группа «{1}» уничтожена!</color>", "Шаблон сообщения об успехе. {0} — ник игрока, {1} — имя группы.");
		}
	}
	internal static class SpawnConfig
	{
		internal static readonly string Dir = Path.Combine(Paths.ConfigPath, "NpcSpawner");

		internal static readonly string NpcsFile = Path.Combine(Dir, "npcs.json");

		internal static readonly string LootFile = Path.Combine(Dir, "loot.json");

		internal static Dictionary<string, SpawnGroup> Groups { get; private set; } = new Dictionary<string, SpawnGroup>();

		internal static Dictionary<string, List<LootEntry>> Loot { get; private set; } = new Dictionary<string, List<LootEntry>>();

		internal static JsonSerializerOptions Options()
		{
			return new JsonSerializerOptions
			{
				PropertyNameCaseInsensitive = true,
				ReadCommentHandling = JsonCommentHandling.Skip,
				AllowTrailingCommas = true,
				WriteIndented = true
			};
		}

		internal static void LoadAll()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			try
			{
				Directory.CreateDirectory(Dir);
				EnsureFile(NpcsFile, "NpcSpawner.npcs.default.json", "{ }");
				EnsureFile(LootFile, "NpcSpawner.loot.default.json", "{ }");
				Groups = LoadGroups();
				Loot = LoadLoot();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] ошибка загрузки конфигов: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}

		private static Dictionary<string, SpawnGroup> LoadGroups()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			try
			{
				return JsonSerializer.Deserialize<Dictionary<string, SpawnGroup>>(File.ReadAllText(NpcsFile), Options()) ?? new Dictionary<string, SpawnGroup>();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] npcs.json не прочитан: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogError(val);
				return new Dictionary<string, SpawnGroup>();
			}
		}

		private static Dictionary<string, List<LootEntry>> LoadLoot()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			try
			{
				return JsonSerializer.Deserialize<Dictionary<string, List<LootEntry>>>(File.ReadAllText(LootFile), Options()) ?? new Dictionary<string, List<LootEntry>>();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] loot.json не прочитан: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogError(val);
				return new Dictionary<string, List<LootEntry>>();
			}
		}

		internal static List<LootEntry> GetLoot(string npcGuid)
		{
			if (npcGuid != null && Loot.TryGetValue(npcGuid, out var value))
			{
				return value;
			}
			return null;
		}

		private static void EnsureFile(string path, string resourceName, string fallback)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			if (!File.Exists(path))
			{
				string contents = ReadResource(resourceName) ?? fallback;
				File.WriteAllText(path, contents);
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] создан файл по умолчанию: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(path);
				}
				logger.LogInfo(val);
			}
		}

		private static string ReadResource(string name)
		{
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
				if (stream == null)
				{
					return null;
				}
				using StreamReader streamReader = new StreamReader(stream);
				return streamReader.ReadToEnd();
			}
			catch
			{
				return null;
			}
		}
	}
	internal static class SpawnerService
	{
		internal sealed class Batch
		{
			public string GroupName;

			public string PlayerName;

			public readonly List<Entity> Alive = new List<Entity>();
		}

		private sealed class Spawned
		{
			public string GuidKey;

			public Batch Batch;
		}

		private sealed class Pickup
		{
			public int Guid;

			public string GuidKey;

			public Batch Batch;

			public float3 Center;

			public float Radius;

			public DateTime DueAt;
		}

		private struct PlayerRef
		{
			public Entity UserEntity;

			public Entity Character;

			public float3 Position;
		}

		private static readonly Random Rng = new Random();

		private static readonly Dictionary<Entity, Spawned> Tracked = new Dictionary<Entity, Spawned>();

		private static readonly List<Pickup> _pickups = new List<Pickup>();

		private static readonly List<int> _combatBuffs = new List<int>();

		private static DateTime _nextSpawnAt = DateTime.MinValue;

		private static bool _initialized;

		private static EntityQuery _userQuery;

		private static EntityQuery _prefabQuery;

		private static EntityQuery _castleFloorQuery;

		private static EntityQuery _castleHeartQuery;

		private static bool _queriesCreated;

		internal static IReadOnlyList<string> GroupNames
		{
			get
			{
				List<string> list = new List<string>();
				if (SpawnConfig.Groups != null)
				{
					foreach (KeyValuePair<string, SpawnGroup> group in SpawnConfig.Groups)
					{
						if (group.Value != null && group.Value.Prefabs != null && group.Value.Prefabs.Count > 0)
						{
							list.Add(group.Key);
						}
					}
				}
				return list;
			}
		}

		internal static void Initialize()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			if (!_initialized && Svc.Ready)
			{
				_initialized = true;
				SpawnConfig.LoadAll();
				_userQuery = ((EntityManager)(ref Svc.EM)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() });
				_prefabQuery = ((EntityManager)(ref Svc.EM)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<PrefabGUID>() });
				_castleFloorQuery = ((EntityManager)(ref Svc.EM)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2]
				{
					ComponentType.ReadOnly<CastleFloor>(),
					ComponentType.ReadOnly<LocalToWorld>()
				});
				_castleHeartQuery = ((EntityManager)(ref Svc.EM)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2]
				{
					ComponentType.ReadOnly<CastleHeart>(),
					ComponentType.ReadOnly<LocalToWorld>()
				});
				_queriesCreated = true;
				ReloadCombatBuffs();
				ScheduleNext("init");
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] ready. groups=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SpawnConfig.Groups.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", loot=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SpawnConfig.Loot.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", enabled=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(Settings.Enabled.Value);
				}
				logger.LogInfo(val);
				ManualLogSource logger2 = Plugin.Logger;
				val = new BepInExInfoLogInterpolatedStringHandler(43, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] castle floors=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((EntityQuery)(ref _castleFloorQuery)).CalculateEntityCount());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", castle hearts=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((EntityQuery)(ref _castleHeartQuery)).CalculateEntityCount());
				}
				logger2.LogInfo(val);
			}
		}

		internal static void Shutdown()
		{
			try
			{
				if (_queriesCreated && Svc.Ready)
				{
					((EntityQuery)(ref _userQuery)).Dispose();
					((EntityQuery)(ref _prefabQuery)).Dispose();
					((EntityQuery)(ref _castleFloorQuery)).Dispose();
					((EntityQuery)(ref _castleHeartQuery)).Dispose();
				}
			}
			catch
			{
			}
			_queriesCreated = false;
			Tracked.Clear();
			_pickups.Clear();
			_initialized = false;
		}

		internal static void Tick()
		{
			if (!Svc.Ready)
			{
				return;
			}
			if (!_initialized)
			{
				Initialize();
				return;
			}
			CollectPickups();
			PruneTracked();
			if (!Settings.Enabled.Value || DateTime.UtcNow < _nextSpawnAt)
			{
				return;
			}
			ScheduleNext("auto");
			try
			{
				SpawnRandomGroup();
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)ex);
			}
		}

		private static void ScheduleNext(string reason)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			float num = Math.Max(5f, Settings.IntervalMin.Value);
			float num2 = Math.Max(num, Settings.IntervalMax.Value);
			double num3 = (double)num + Rng.NextDouble() * (double)(num2 - num);
			if (Settings.IntervalScalesWithPlayers.Value)
			{
				List<PlayerRef> players = GetPlayers();
				num3 /= (double)Math.Max(1, players.Count);
			}
			_nextSpawnAt = DateTime.UtcNow.AddSeconds(num3);
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(43, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] следующий спавн через ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(num3, "0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" сек (");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(reason);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
			}
			logger.LogInfo(val);
		}

		private static void CollectPickups()
		{
			if (_pickups.Count == 0)
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			for (int num = _pickups.Count - 1; num >= 0; num--)
			{
				Pickup pickup = _pickups[num];
				if (!(utcNow < pickup.DueAt))
				{
					_pickups.RemoveAt(num);
					try
					{
						CollectOne(pickup);
					}
					catch (Exception ex)
					{
						Plugin.Logger.LogError((object)ex);
					}
				}
			}
		}

		private static void CollectOne(Pickup p)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			NativeArray<Entity> val = ((EntityQuery)(ref _prefabQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				float num = (p.Radius + 6f) * (p.Radius + 6f);
				Enumerator<Entity> enumerator = val.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current = enumerator.Current;
					if (Tracked.ContainsKey(current))
					{
						continue;
					}
					PrefabGUID componentData;
					try
					{
						componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<PrefabGUID>(current);
					}
					catch
					{
						continue;
					}
					if (((PrefabGUID)(ref componentData)).GuidHash == p.Guid && TryGetPosition(current, out var pos))
					{
						float num2 = pos.x - p.Center.x;
						float num3 = pos.z - p.Center.z;
						if (!(num2 * num2 + num3 * num3 > num))
						{
							Tracked[current] = new Spawned
							{
								GuidKey = p.GuidKey,
								Batch = p.Batch
							};
							p.Batch?.Alive.Add(current);
						}
					}
				}
			}
			finally
			{
				val.Dispose();
			}
		}

		private static void PruneTracked()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (Tracked.Count == 0)
			{
				return;
			}
			List<Entity> list = null;
			foreach (Entity key in Tracked.Keys)
			{
				if (!((EntityManager)(ref Svc.EM)).Exists(key))
				{
					if (list == null)
					{
						list = new List<Entity>();
					}
					list.Add(key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (Entity item in list)
			{
				if (Tracked.TryGetValue(item, out var value))
				{
					value.Batch?.Alive.Remove(item);
				}
				Tracked.Remove(item);
			}
		}

		private static List<PlayerRef> GetPlayers()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerRef> list = new List<PlayerRef>();
			if (!Svc.Ready || !_queriesCreated)
			{
				return list;
			}
			NativeArray<Entity> val = ((EntityQuery)(ref _userQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				Enumerator<Entity> enumerator = val.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current = enumerator.Current;
					User componentData;
					try
					{
						componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<User>(current);
					}
					catch
					{
						continue;
					}
					if (componentData.IsConnected && !componentData.IsBot)
					{
						Entity entity = componentData.LocalCharacter._Entity;
						if (!(entity == Entity.Null) && ((EntityManager)(ref Svc.EM)).Exists(entity) && TryGetPosition(entity, out var pos))
						{
							list.Add(new PlayerRef
							{
								UserEntity = current,
								Character = entity,
								Position = pos
							});
						}
					}
				}
				return list;
			}
			finally
			{
				val.Dispose();
			}
		}

		private static bool TryGetPosition(Entity e, out float3 pos)
		{
			//IL_0005: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (((EntityManager)(ref Svc.EM)).HasComponent<LocalToWorld>(e))
			{
				LocalToWorld componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<LocalToWorld>(e);
				pos = ((LocalToWorld)(ref componentData)).Position;
				return true;
			}
			if (((EntityManager)(ref Svc.EM)).HasComponent<Translation>(e))
			{
				pos = ((EntityManager)(ref Svc.EM)).GetComponentData<Translation>(e).Value;
				return true;
			}
			pos = default(float3);
			return false;
		}

		internal static bool IsInCombat(Entity character)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!Svc.Ready || character == Entity.Null)
			{
				return false;
			}
			foreach (int combatBuff in _combatBuffs)
			{
				try
				{
					if (BuffUtility.GetBuffInstanceCount<EntityManager>(Svc.EM, character, new PrefabGUID(combatBuff)) > 0)
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		internal static void ReloadCombatBuffs()
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			_combatBuffs.Clear();
			string[] array = (Settings.CombatBuffGuids.Value ?? string.Empty).Split(',');
			bool flag = default(bool);
			for (int i = 0; i < array.Length; i++)
			{
				if (!int.TryParse(array[i].Trim(), out var result))
				{
					continue;
				}
				string text = Svc.PrefabName(result);
				if (string.IsNullOrEmpty(text))
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(73, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] бафф '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(result);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' не найден в префабах — пропускаю его в проверке боя.");
					}
					logger.LogWarning(val);
					continue;
				}
				_combatBuffs.Add(result);
				ManualLogSource logger2 = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(26, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] бафф боя: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(result);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" = ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(text);
				}
				logger2.LogInfo(val2);
			}
			if (_combatBuffs.Count == 0)
			{
				Plugin.Logger.LogWarning((object)"[NpcSpawner] список баффов боя пуст — игроки в бою пропускаться НЕ будут.");
			}
		}

		private static List<PlayerRef> GetSpawnablePlayers()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerRef> players = GetPlayers();
			if (!Settings.SkipPlayersInCombat.Value)
			{
				return players;
			}
			List<PlayerRef> list = new List<PlayerRef>();
			foreach (PlayerRef item in players)
			{
				if (!IsInCombat(item.Character))
				{
					list.Add(item);
				}
			}
			return list;
		}

		private static void SpawnRandomGroup()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			List<PlayerRef> spawnablePlayers = GetSpawnablePlayers();
			if (spawnablePlayers.Count == 0)
			{
				Plugin.Logger.LogInfo((object)"[NpcSpawner] нет подходящих игроков (онлайн и не в бою) — ждём.");
				return;
			}
			IReadOnlyList<string> groupNames = GroupNames;
			if (groupNames.Count == 0)
			{
				Plugin.Logger.LogWarning((object)"[NpcSpawner] в npcs.json нет ни одной группы с Prefabs.");
				return;
			}
			string text = groupNames[Rng.Next(groupNames.Count)];
			SpawnGroup spawnGroup = SpawnConfig.Groups[text];
			int num = Math.Min(3, spawnablePlayers.Count);
			bool flag = default(bool);
			for (int i = 0; i < num; i++)
			{
				PlayerRef playerRef = spawnablePlayers[Rng.Next(spawnablePlayers.Count)];
				string text2 = SafeUserName(playerRef.UserEntity);
				int num2 = SpawnGroupFor(playerRef.UserEntity, playerRef.Character, playerRef.Position, text, spawnGroup, announce: true);
				if (num2 > 0)
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(50, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] группа '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': заспавнено ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" НПС у игрока ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					logger.LogInfo(val);
					if (Settings.GlobalAnnounce.Value)
					{
						Broadcast(string.Format(Settings.GlobalSpawnMessage.Value, text2, text, num2));
					}
					return;
				}
			}
			Plugin.Logger.LogInfo((object)"[NpcSpawner] не нашлось свободного места (замки/крыши) — пропускаем спавн.");
		}

		internal static int SpawnGroupFor(Entity userEntity, Entity character, float3 center, string groupName, SpawnGroup group, bool announce)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			float num2 = Settings.LifetimeSeconds.Value;
			if (num2 <= 0f)
			{
				num2 = -1f;
			}
			Batch batch = new Batch
			{
				GroupName = groupName,
				PlayerName = SafeUserName(userEntity)
			};
			bool flag = default(bool);
			foreach (KeyValuePair<string, int> prefab in group.Prefabs)
			{
				if (prefab.Value <= 0)
				{
					continue;
				}
				if (!int.TryParse(prefab.Key, out var result))
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(37, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] некорректный id НПС: '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(prefab.Key);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'.");
					}
					logger.LogWarning(val);
					continue;
				}
				for (int i = 0; i < prefab.Value; i++)
				{
					if (!TryFindFreePosition(center, group.RandomAround, out var flat))
					{
						break;
					}
					if (SpawnOne(character, result, prefab.Key, flat, num2))
					{
						num++;
					}
				}
				_pickups.Add(new Pickup
				{
					Guid = result,
					GuidKey = prefab.Key,
					Batch = batch,
					Center = center,
					Radius = Math.Max(1f, group.RandomAround),
					DueAt = DateTime.UtcNow.AddSeconds(1.5)
				});
			}
			if (num > 0 && announce && Settings.Announce.Value)
			{
				SendToUser(userEntity, string.Format(Settings.AnnounceMessage.Value, groupName, num));
			}
			return num;
		}

		private static bool SpawnOne(Entity character, int guid, string guidKey, float2 flat, float lifetime)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float3 pos;
				float num = (TryGetPosition(character, out pos) ? pos.y : 0f);
				float3 val = default(float3);
				((float3)(ref val))..ctor(flat.x, num, flat.y);
				Svc.UnitSpawner.SpawnUnit(Entity.Null, new PrefabGUID(guid), val, 1, 1f, 1f, lifetime);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(41, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] не удалось заспавнить НПС ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(guid);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				logger.LogError(val2);
				return false;
			}
		}

		private static float2 RandomAround(float3 center, float radius)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (radius < 1f)
			{
				radius = 1f;
			}
			double num = Rng.NextDouble() * Math.PI * 2.0;
			double num2 = Math.Sqrt(Rng.NextDouble()) * (double)radius;
			return new float2(center.x + (float)(Math.Cos(num) * num2), center.z + (float)(Math.Sin(num) * num2));
		}

		private static bool TryFindFreePosition(float3 center, float radius, out float2 flat)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 15; i++)
			{
				float2 val = RandomAround(center, radius);
				if (!IsInsideCastleOrRoof(new float3(val.x, center.y, val.y)))
				{
					flat = val;
					return true;
				}
			}
			flat = default(float2);
			return false;
		}

		private static bool IsInsideCastleOrRoof(float3 pos)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (!Settings.BlockNearCastles.Value)
			{
				return false;
			}
			if (!_queriesCreated)
			{
				return false;
			}
			float num = Math.Max(1f, Settings.CastleBlockRadius.Value);
			float num2 = num * num;
			NativeArray<Entity> val = ((EntityQuery)(ref _castleFloorQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			LocalToWorld componentData;
			try
			{
				Enumerator<Entity> enumerator = val.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current = enumerator.Current;
					float3 position;
					try
					{
						componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<LocalToWorld>(current);
						position = ((LocalToWorld)(ref componentData)).Position;
					}
					catch
					{
						continue;
					}
					float num3 = position.x - pos.x;
					float num4 = position.z - pos.z;
					if (!(num3 * num3 + num4 * num4 > num2) && !(Math.Abs(position.y - pos.y) > 12f))
					{
						return true;
					}
				}
			}
			finally
			{
				val.Dispose();
			}
			float num5 = Math.Max(4f, Settings.CastleHeartRadius.Value);
			float num6 = num5 * num5;
			NativeArray<Entity> val2 = ((EntityQuery)(ref _castleHeartQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				Enumerator<Entity> enumerator = val2.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current2 = enumerator.Current;
					float3 position2;
					try
					{
						componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<LocalToWorld>(current2);
						position2 = ((LocalToWorld)(ref componentData)).Position;
					}
					catch
					{
						continue;
					}
					float num7 = position2.x - pos.x;
					float num8 = position2.z - pos.z;
					if (num7 * num7 + num8 * num8 <= num6)
					{
						return true;
					}
				}
			}
			finally
			{
				val2.Dispose();
			}
			return false;
		}

		internal static void OnDeath(DeathEventListenerSystem system)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (Tracked.Count == 0)
			{
				return;
			}
			EntityManager entityManager = ((ComponentSystemBase)system).EntityManager;
			EntityQuery deathEventQuery = system._DeathEventQuery;
			NativeArray<DeathEvent> val = ((EntityQuery)(ref deathEventQuery)).ToComponentDataArray<DeathEvent>(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				Enumerator<DeathEvent> enumerator = val.GetEnumerator();
				while (enumerator.MoveNext())
				{
					DeathEvent current = enumerator.Current;
					Entity died = current.Died;
					if (!Tracked.TryGetValue(died, out var value))
					{
						continue;
					}
					Tracked.Remove(died);
					value.Batch?.Alive.Remove(died);
					Entity killer = current.Killer;
					if (!(killer == Entity.Null) && ((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(killer))
					{
						if (Settings.LootEnabled.Value)
						{
							DropLoot(entityManager, killer, value.GuidKey);
						}
						if (value.Batch != null && value.Batch.Alive.Count == 0 && Settings.SuccessAnnounce.Value)
						{
							Broadcast(string.Format(Settings.SuccessMessage.Value, value.Batch.PlayerName, value.Batch.GroupName));
						}
					}
				}
			}
			finally
			{
				val.Dispose();
			}
		}

		private static void DropLoot(EntityManager em, Entity killer, string npcGuid)
		{
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			List<LootEntry> loot = SpawnConfig.GetLoot(npcGuid);
			if (loot == null || loot.Count == 0)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			PrefabGUID val = default(PrefabGUID);
			foreach (LootEntry item in loot)
			{
				if (item.ItemId == 0 || (item.Chance < 1f && Rng.NextDouble() > (double)item.Chance))
				{
					continue;
				}
				int num3 = ((item.Amount <= 0) ? 1 : item.Amount);
				((PrefabGUID)(ref val))..ctor(item.ItemId);
				bool flag = false;
				try
				{
					AddItemResponse val2 = ((ServerGameManager)(ref Svc.GameManager)).TryAddInventoryItem(killer, val, num3);
					flag = ((AddItemResponse)(ref val2)).Success;
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogError((object)ex);
				}
				if (!flag)
				{
					try
					{
						((ServerGameManager)(ref Svc.GameManager)).CreateDroppedItemEntity(killer, val, num3);
					}
					catch (Exception ex2)
					{
						Plugin.Logger.LogError((object)ex2);
					}
				}
				num += num3;
				num2 = item.ItemId;
			}
			if (num <= 0 || !Settings.NotifyKiller.Value)
			{
				return;
			}
			try
			{
				SendToUser(((EntityManager)(ref em)).GetComponentData<PlayerCharacter>(killer).UserEntity, string.Format(Settings.NotifyMessage.Value, num, num2));
			}
			catch (Exception ex3)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag2 = default(bool);
				BepInExDebugLogInterpolatedStringHandler val3 = new BepInExDebugLogInterpolatedStringHandler(47, 1, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[NpcSpawner] сообщение о добыче не отправлено: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex3.Message);
				}
				logger.LogDebug(val3);
			}
		}

		internal static bool TryResolveGroup(string input, out string key)
		{
			key = null;
			if (string.IsNullOrWhiteSpace(input))
			{
				return false;
			}
			string text = input.Trim();
			string text2 = RepairMojibake(text);
			if (SpawnConfig.Groups.ContainsKey(text))
			{
				key = text;
				return true;
			}
			if (SpawnConfig.Groups.ContainsKey(text2))
			{
				key = text2;
				return true;
			}
			string text3 = null;
			foreach (string groupName in GroupNames)
			{
				string text4 = groupName.Trim();
				if (string.Equals(text4, text, StringComparison.OrdinalIgnoreCase) || string.Equals(text4, text2, StringComparison.OrdinalIgnoreCase) || text4.StartsWith(text, StringComparison.OrdinalIgnoreCase) || text4.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
				{
					if (string.Equals(text4, text, StringComparison.OrdinalIgnoreCase) || string.Equals(text4, text2, StringComparison.OrdinalIgnoreCase))
					{
						key = groupName;
						return true;
					}
					if (text3 != null)
					{
						return false;
					}
					text3 = groupName;
				}
			}
			if (text3 != null)
			{
				key = text3;
				return true;
			}
			return false;
		}

		private static string RepairMojibake(string s)
		{
			try
			{
				return Encoding.UTF8.GetString(Encoding.GetEncoding("ISO-8859-1").GetBytes(s));
			}
			catch
			{
				return s;
			}
		}

		internal static List<string> DebugSnapshot()
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			if (!Svc.Ready)
			{
				list.Add("мир ещё не готов");
				return list;
			}
			List<PlayerRef> players = GetPlayers();
			list.Add($"игроков онлайн: {players.Count}; групп: {GroupNames.Count}");
			list.Add($"плит замка видно: {(_queriesCreated ? ((EntityQuery)(ref _castleFloorQuery)).CalculateEntityCount() : 0)}; сердец замка: {(_queriesCreated ? ((EntityQuery)(ref _castleHeartQuery)).CalculateEntityCount() : 0)}");
			list.Add($"баффов боя в списке: {_combatBuffs.Count}");
			foreach (PlayerRef item in players)
			{
				bool value = IsInCombat(item.Character);
				bool value2 = IsInsideCastleOrRoof(item.Position);
				list.Add($"{SafeUserName(item.UserEntity)}: в бою={value}, в замке/под крышей={value2}");
			}
			return list;
		}

		internal static bool TrySpawnForCharacter(Entity character, string groupName, out int spawned, out string error)
		{
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			spawned = 0;
			error = null;
			if (!Svc.Ready)
			{
				error = "мод ещё не готов (мир не загружен)";
				return false;
			}
			if (string.IsNullOrWhiteSpace(groupName))
			{
				error = "не указана группа";
				return false;
			}
			if (!TryResolveGroup(groupName, out var key))
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] группа '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(groupName);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' не найдена. Есть: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", GroupNames));
				}
				logger.LogWarning(val);
				error = "группа '" + groupName + "' не найдена — смотри .npcs list";
				return false;
			}
			SpawnGroup spawnGroup = SpawnConfig.Groups[key];
			groupName = key;
			if (character == Entity.Null || !((EntityManager)(ref Svc.EM)).Exists(character))
			{
				error = "персонаж не найден";
				return false;
			}
			if (!TryGetPosition(character, out var pos))
			{
				error = "не удалось определить позицию игрока";
				return false;
			}
			if (Settings.SkipPlayersInCombat.Value && IsInCombat(character))
			{
				error = "игрок сейчас в бою — спавн запрещён";
				return false;
			}
			Entity userEntity = Entity.Null;
			try
			{
				if (((EntityManager)(ref Svc.EM)).HasComponent<PlayerCharacter>(character))
				{
					userEntity = ((EntityManager)(ref Svc.EM)).GetComponentData<PlayerCharacter>(character).UserEntity;
				}
			}
			catch
			{
			}
			spawned = SpawnGroupFor(userEntity, character, pos, groupName, spawnGroup, announce: true);
			if (spawned <= 0)
			{
				error = "рядом нет свободного места — тут замок или крыша";
				return false;
			}
			return true;
		}

		internal static bool TryFindOnlineCharacter(string name, out Entity character)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			character = Entity.Null;
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			string text = name.Trim();
			string text2 = RepairMojibake(text);
			Entity val = Entity.Null;
			int num = 0;
			foreach (PlayerRef player in GetPlayers())
			{
				string text3 = SafeUserName(player.UserEntity);
				if (string.Equals(text3, text, StringComparison.OrdinalIgnoreCase) || string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase))
				{
					character = player.Character;
					return true;
				}
				if (text3.StartsWith(text, StringComparison.OrdinalIgnoreCase) || text3.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
				{
					val = player.Character;
					num++;
				}
			}
			if (num == 1)
			{
				character = val;
				return true;
			}
			return false;
		}

		internal static bool TrySpawnRandomNow(out string info)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			info = null;
			List<PlayerRef> spawnablePlayers = GetSpawnablePlayers();
			if (spawnablePlayers.Count == 0)
			{
				info = "нет игроков онлайн (или все в бою)";
				return false;
			}
			IReadOnlyList<string> groupNames = GroupNames;
			if (groupNames.Count == 0)
			{
				info = "нет групп в npcs.json";
				return false;
			}
			PlayerRef playerRef = spawnablePlayers[Rng.Next(spawnablePlayers.Count)];
			string text = groupNames[Rng.Next(groupNames.Count)];
			int value = SpawnGroupFor(playerRef.UserEntity, playerRef.Character, playerRef.Position, text, SpawnConfig.Groups[text], announce: true);
			info = $"группа '{text}' ({value} НПС) у игрока {SafeUserName(playerRef.UserEntity)}";
			ScheduleNext("manual");
			return true;
		}

		private static void SendToUser(Entity userEntity, string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			try
			{
				if (!(userEntity == Entity.Null) && ((EntityManager)(ref Svc.EM)).Exists(userEntity) && !string.IsNullOrEmpty(message))
				{
					User componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<User>(userEntity);
					FixedString512Bytes val = FixedString512Bytes.op_Implicit(Truncate(message, 480));
					ServerChatUtils.SendSystemMessageToClient(Svc.EM, componentData, ref val);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(38, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[NpcSpawner] сообщение не отправлено: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val2);
			}
		}

		private static string Truncate(string s, int maxBytes)
		{
			try
			{
				if (Encoding.UTF8.GetByteCount(s) <= maxBytes)
				{
					return s;
				}
				int num = s.Length;
				while (num > 0 && Encoding.UTF8.GetByteCount(s.Substring(0, num)) > maxBytes)
				{
					num--;
				}
				return s.Substring(0, num);
			}
			catch
			{
				return s;
			}
		}

		private static void Broadcast(string message)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerRef player in GetPlayers())
			{
				SendToUser(player.UserEntity, message);
			}
		}

		private unsafe static string SafeUserName(Entity userEntity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (userEntity == Entity.Null || !((EntityManager)(ref Svc.EM)).Exists(userEntity))
				{
					return "?";
				}
				User componentData = ((EntityManager)(ref Svc.EM)).GetComponentData<User>(userEntity);
				return ((object)(*(FixedString64Bytes*)(&componentData.CharacterName))/*cast due to .constrained prefix*/).ToString();
			}
			catch
			{
				return "?";
			}
		}
	}
	internal static class Svc
	{
		internal static World Server;

		internal static EntityManager EM;

		internal static UnitSpawnerUpdateSystem UnitSpawner;

		internal static ServerGameManager GameManager;

		internal static PrefabCollectionSystem Prefabs;

		internal static bool Ready
		{
			get
			{
				if (Server != null)
				{
					return Server.IsCreated;
				}
				return false;
			}
		}

		internal static void Init(World world)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Server = world;
			EM = world.EntityManager;
			UnitSpawner = world.GetExistingSystemManaged<UnitSpawnerUpdateSystem>();
			GameManager = world.GetExistingSystemManaged<ServerScriptMapper>().GetServerGameManager();
			try
			{
				Prefabs = world.GetExistingSystemManaged<PrefabCollectionSystem>();
			}
			catch
			{
				Prefabs = null;
			}
		}

		internal unsafe static string PrefabName(int guid)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Prefabs == null)
				{
					return null;
				}
				FixedString128Bytes assetName = Prefabs._PrefabDataLookup[new PrefabGUID(guid)].AssetName;
				return ((object)(*(FixedString128Bytes*)(&assetName))/*cast due to .constrained prefix*/).ToString();
			}
			catch
			{
				return null;
			}
		}
	}
}
namespace NpcSpawner.Patches
{
	[HarmonyPatch(typeof(DeathEventListenerSystem), "OnUpdate")]
	internal static class DeathPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(DeathEventListenerSystem __instance)
		{
			try
			{
				SpawnerService.OnDeath(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(ServerBootstrapSystem), "OnUserConnected")]
	internal static class InitPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(ServerBootstrapSystem __instance)
		{
			try
			{
				if (!Svc.Ready)
				{
					Svc.Init(((ComponentSystemBase)__instance).World);
				}
				SpawnerService.Initialize();
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(ServerBootstrapSystem), "OnUpdate")]
	internal static class TickPatch
	{
		[HarmonyPostfix]
		internal static void Postfix()
		{
			try
			{
				SpawnerService.Tick();
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)ex);
			}
		}
	}
}
namespace NpcSpawner.Commands
{
	[CommandGroup("npcs", "npcspawner")]
	internal static class NpcCommands
	{
		private const string DenyText = "<color=#ff4d4d>Команда доступна только администраторам.</color>";

		private const int MaxMessageBytes = 300;

		private static bool Deny(ChatCommandContext ctx)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				flag = ctx.User.IsAdmin;
			}
			catch
			{
			}
			if (flag)
			{
				return false;
			}
			ctx.Reply("<color=#ff4d4d>Команда доступна только администраторам.</color>");
			return true;
		}

		private static void Reply(ChatCommandContext ctx, string text)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			try
			{
				ctx.Reply(TruncateBytes(text, 450));
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(27, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] reply failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}

		private static void Run(ChatCommandContext ctx, string command, Action body)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			try
			{
				body();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(25, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] команда '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(command);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				Reply(ctx, "<color=#ff8080>Ошибка команды: " + ex.Message + " (подробности в логе)</color>");
			}
		}

		private static string TruncateBytes(string s, int maxBytes)
		{
			try
			{
				if (string.IsNullOrEmpty(s))
				{
					return s;
				}
				if (Encoding.UTF8.GetByteCount(s) <= maxBytes)
				{
					return s;
				}
				int num = s.Length;
				while (num > 0 && Encoding.UTF8.GetByteCount(s.Substring(0, num)) > maxBytes)
				{
					num--;
				}
				return s.Substring(0, num);
			}
			catch
			{
				return s;
			}
		}

		private static List<string> BuildChunks(string header, IReadOnlyList<string> items, int maxBytes)
		{
			List<string> list = new List<string>();
			StringBuilder stringBuilder = new StringBuilder(header);
			bool flag = false;
			foreach (string item in items)
			{
				string s = (flag ? (stringBuilder.ToString() + ", " + item) : (stringBuilder.ToString() + item));
				if (Encoding.UTF8.GetByteCount(s) > maxBytes && flag)
				{
					list.Add(stringBuilder.ToString());
					stringBuilder.Clear();
					stringBuilder.Append(item);
					flag = true;
					continue;
				}
				if (flag)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(item);
				flag = true;
			}
			if (flag || stringBuilder.Length > 0)
			{
				list.Add(stringBuilder.ToString());
			}
			if (list.Count == 0)
			{
				list.Add(header);
			}
			return list;
		}

		private static void ReplyList(ChatCommandContext ctx, string header, IReadOnlyList<string> items)
		{
			List<string> list = BuildChunks(header, items, 300);
			for (int i = 0; i < list.Count; i++)
			{
				string text = ((list.Count > 1) ? $"({i + 1}/{list.Count}) " : string.Empty);
				Reply(ctx, text + list[i]);
			}
		}

		private static Entity ResolveCharacter(ChatCommandContext ctx)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Entity senderUserEntity = ctx.Event.SenderUserEntity;
				if (senderUserEntity != Entity.Null && Svc.Ready && ((EntityManager)(ref Svc.EM)).Exists(senderUserEntity) && ((EntityManager)(ref Svc.EM)).HasComponent<User>(senderUserEntity))
				{
					Entity entity = ((EntityManager)(ref Svc.EM)).GetComponentData<User>(senderUserEntity).LocalCharacter._Entity;
					if (entity != Entity.Null && ((EntityManager)(ref Svc.EM)).Exists(entity))
					{
						return entity;
					}
				}
			}
			catch
			{
			}
			return ctx.Event.SenderCharacterEntity;
		}

		[Command("help", null, null, "Справка по командам NPC Spawner", null, true)]
		public static void Help(ChatCommandContext ctx)
		{
			if (!Deny(ctx))
			{
				Run(ctx, "help", delegate
				{
					Reply(ctx, "--- NPC Spawner (только для админов) ---");
					Reply(ctx, ".npcs list — список групп из npcs.json");
					Reply(ctx, ".npcs spawn <группа> — заспавнить у себя");
					Reply(ctx, ".npcs spawnme <группа> — то же самое");
					Reply(ctx, ".npcs spawnfor <ник> <группа> — заспавнить у игрока");
					Reply(ctx, ".npcs random — случайный спавн сейчас");
					Reply(ctx, ".npcs loot <id НПС> — лут для НПС");
					Reply(ctx, ".npcs reload — перечитать конфиги");
					Reply(ctx, ".npcs debug — диагностика");
				});
			}
		}

		[Command("list", null, null, "Список групп НПС", null, true)]
		public static void List(ChatCommandContext ctx)
		{
			if (Deny(ctx))
			{
				return;
			}
			Run(ctx, "list", delegate
			{
				IReadOnlyList<string> groupNames = SpawnerService.GroupNames;
				if (groupNames.Count == 0)
				{
					Reply(ctx, "Групп нет. Проверь BepInEx/config/NpcSpawner/npcs.json");
				}
				else
				{
					ReplyList(ctx, $"Группы ({groupNames.Count}): ", groupNames);
				}
			});
		}

		[Command("spawn", null, "<группа>", "Заспавнить группу НПС рядом с собой", null, true)]
		public static void Spawn(ChatCommandContext ctx, string group)
		{
			if (!Deny(ctx))
			{
				SpawnSelf(ctx, group);
			}
		}

		[Command("spawnme", null, "<группа>", "Заспавнить группу НПС рядом с собой", null, true)]
		public static void SpawnMe(ChatCommandContext ctx, string group)
		{
			if (!Deny(ctx))
			{
				SpawnSelf(ctx, group);
			}
		}

		private static void SpawnSelf(ChatCommandContext ctx, string group)
		{
			Run(ctx, "spawn", delegate
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Expected O, but got Unknown
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] команда spawn '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(group);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' от ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(SafeName(ctx));
				}
				logger.LogInfo(val);
				if (SpawnerService.TrySpawnForCharacter(ResolveCharacter(ctx), group, out var spawned, out var error))
				{
					Reply(ctx, $"<color=#3cff8a>Заспавнено {spawned} НПС из группы '{group}'.</color>");
				}
				else
				{
					Reply(ctx, "<color=#ff8080>Не вышло: " + error + "</color>");
				}
			});
		}

		[Command("spawnfor", null, "<ник> <группа>", "Заспавнить группу НПС у другого игрока", null, true)]
		public static void SpawnFor(ChatCommandContext ctx, string playerName, string group)
		{
			if (Deny(ctx))
			{
				return;
			}
			Run(ctx, "spawnfor", delegate
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Expected O, but got Unknown
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NpcSpawner] команда spawnfor '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(playerName);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(group);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' от ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(SafeName(ctx));
				}
				logger.LogInfo(val);
				int spawned;
				string error;
				if (!SpawnerService.TryFindOnlineCharacter(playerName, out var character))
				{
					Reply(ctx, "<color=#ff8080>Игрок '" + playerName + "' не в сети.</color>");
				}
				else if (SpawnerService.TrySpawnForCharacter(character, group, out spawned, out error))
				{
					Reply(ctx, $"<color=#3cff8a>Заспавнено {spawned} НПС из группы '{group}' у {playerName}.</color>");
				}
				else
				{
					Reply(ctx, "<color=#ff8080>Не вышло: " + error + "</color>");
				}
			});
		}

		[Command("random", null, null, "Случайный спавн как по таймеру (сейчас)", null, true)]
		public static void Random(ChatCommandContext ctx)
		{
			if (Deny(ctx))
			{
				return;
			}
			Run(ctx, "random", delegate
			{
				if (SpawnerService.TrySpawnRandomNow(out var info))
				{
					Reply(ctx, "<color=#3cff8a>Случайный спавн: " + info + "</color>");
				}
				else
				{
					Reply(ctx, "<color=#ff8080>Не вышло: " + info + "</color>");
				}
			});
		}

		[Command("loot", null, "<id НПС>", "Показать настроенный лут для НПС", null, true)]
		public static void Loot(ChatCommandContext ctx, string npcId)
		{
			if (Deny(ctx))
			{
				return;
			}
			Run(ctx, "loot", delegate
			{
				List<LootEntry> loot = SpawnConfig.GetLoot(npcId);
				if (loot == null || loot.Count == 0)
				{
					Reply(ctx, "Для '" + npcId + "' лут не настроен (см. BepInEx/config/NpcSpawner/loot.json)");
				}
				else
				{
					List<string> list = new List<string>();
					foreach (LootEntry item in loot)
					{
						list.Add($"{item.ItemId} x{item.Amount} ({item.Chance * 100f:0.#}%)");
					}
					ReplyList(ctx, npcId + ": ", list);
				}
			});
		}

		[Command("reload", null, null, "Перечитать npcs.json и loot.json без рестарта", null, true)]
		public static void Reload(ChatCommandContext ctx)
		{
			if (!Deny(ctx))
			{
				Run(ctx, "reload", delegate
				{
					SpawnConfig.LoadAll();
					SpawnerService.ReloadCombatBuffs();
					Reply(ctx, $"<color=#3cff8a>Перечитано: групп {SpawnConfig.Groups.Count}, записей лута {SpawnConfig.Loot.Count}.</color>");
				});
			}
		}

		[Command("debug", null, null, "Диагностика: кто в бою, где найдены замки", null, true)]
		public static void Debug(ChatCommandContext ctx)
		{
			if (Deny(ctx))
			{
				return;
			}
			Run(ctx, "debug", delegate
			{
				foreach (string item in SpawnerService.DebugSnapshot())
				{
					Reply(ctx, item);
				}
			});
		}

		private static string SafeName(ChatCommandContext ctx)
		{
			try
			{
				return ctx.Name;
			}
			catch
			{
				return "?";
			}
		}
	}
}