Decompiled source of RandomEventsExtended v3.0.2

RandomEvents.dll

Decompiled 41 minutes ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RandomEvents")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("3.0.1.0")]
[assembly: AssemblyInformationalVersion("3.0.1")]
[assembly: AssemblyProduct("RandomEvents")]
[assembly: AssemblyTitle("RandomEvents")]
[assembly: AssemblyVersion("3.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 RandomEventsExtended
{
	public enum BountyType
	{
		KillEnemies,
		CollectLoot,
		SurviveAll,
		KillSpecific
	}
	public static class BountySystem
	{
		private static bool _deathOccurred;

		public static BountyType CurrentType { get; private set; }

		public static string Description { get; private set; } = "";


		public static int TargetAmount { get; private set; }

		public static int RewardCredits { get; private set; }

		public static int Progress { get; private set; }

		public static bool IsCompleted { get; private set; }

		public static bool IsFailed { get; private set; }

		public static string SpecificEnemy { get; private set; } = "";


		public static void NewRound(Random rng, SelectableLevel? level, bool lootAvailable = true, bool monstersAvailable = true)
		{
			Progress = 0;
			IsCompleted = false;
			IsFailed = false;
			_deathOccurred = false;
			List<BountyType> list = new List<BountyType> { BountyType.SurviveAll };
			if (lootAvailable)
			{
				list.Add(BountyType.CollectLoot);
			}
			if (monstersAvailable)
			{
				list.Add(BountyType.KillEnemies);
				list.Add(BountyType.KillSpecific);
			}
			CurrentType = list[rng.Next(list.Count)];
			float num = (level?.riskLevel ?? "B").Trim().ToUpper() switch
			{
				"D" => 0.6f, 
				"C" => 0.8f, 
				"B" => 1f, 
				"A" => 1.3f, 
				"S" => 1.6f, 
				"S+" => 2f, 
				_ => 1f, 
			};
			switch (CurrentType)
			{
			case BountyType.KillEnemies:
				TargetAmount = Math.Min(6, Math.Max(2, (int)((float)rng.Next(3, 7) * num)));
				RewardCredits = TargetAmount * 35;
				Description = $"☠  Уничтожьте {TargetAmount} монстров";
				break;
			case BountyType.CollectLoot:
				TargetAmount = Math.Max(100, (int)((float)rng.Next(200, 450) * num));
				RewardCredits = (int)((float)TargetAmount * 0.35f);
				Description = $"\ud83d\udcb0 Доставьте добычи на {TargetAmount}+ кред.";
				break;
			case BountyType.SurviveAll:
				TargetAmount = 1;
				RewardCredits = (int)(220f * num);
				Description = "\ud83d\udee1  Все члены экипажа должны выжить";
				break;
			case BountyType.KillSpecific:
				SpecificEnemy = PickRandomEnemyName(rng, level);
				if (SpecificEnemy == "")
				{
					CurrentType = BountyType.KillEnemies;
					TargetAmount = Math.Min(6, Math.Max(2, (int)((float)rng.Next(3, 7) * num)));
					RewardCredits = TargetAmount * 35;
					Description = $"☠  Уничтожьте {TargetAmount} монстров";
				}
				else
				{
					TargetAmount = Math.Min(3, Math.Max(1, (int)((float)rng.Next(1, 4) * num)));
					RewardCredits = (int)((float)(TargetAmount * 90) * num);
					Description = $"\ud83c\udfaf Уничтожьте {TargetAmount}× {SpecificEnemy}";
				}
				break;
			}
			Plugin.Log.LogInfo((object)$"[Bounty] Новое задание: {Description} → +{RewardCredits} кред.");
		}

		private static string PickRandomEnemyName(Random rng, SelectableLevel? level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return "";
			}
			List<string> list = new List<string>();
			if (level.Enemies != null)
			{
				foreach (SpawnableEnemyWithRarity enemy in level.Enemies)
				{
					if (enemy != null && enemy.enemyType?.enemyName != null && enemy.rarity > 0)
					{
						list.Add(enemy.enemyType.enemyName);
					}
				}
			}
			if (level.OutsideEnemies != null)
			{
				foreach (SpawnableEnemyWithRarity outsideEnemy in level.OutsideEnemies)
				{
					if (outsideEnemy != null && outsideEnemy.enemyType?.enemyName != null && outsideEnemy.rarity > 0 && !list.Contains(outsideEnemy.enemyType.enemyName))
					{
						list.Add(outsideEnemy.enemyType.enemyName);
					}
				}
			}
			if (list.Count <= 0)
			{
				return "";
			}
			return list[rng.Next(list.Count)];
		}

		public static string GetStatusLine()
		{
			if (IsCompleted)
			{
				return $"<color=#4DFF6E>✓ ВЫПОЛНЕНО! +{RewardCredits} кред. зачислено.</color>";
			}
			if (IsFailed)
			{
				return "<color=#FF4444>✗ ПРОВАЛЕНО: " + Description + "</color>";
			}
			string arg = CurrentType switch
			{
				BountyType.KillEnemies => $" [{Progress}/{TargetAmount}]", 
				BountyType.KillSpecific => $" [{Progress}/{TargetAmount}]", 
				BountyType.CollectLoot => $" [{Progress}/{TargetAmount} кред.]", 
				BountyType.SurviveAll => _deathOccurred ? " [ПОТЕРЯ!]" : " [ВСЕ ЖИВЫ]", 
				_ => "", 
			};
			return $"{Description}{arg}  →  <color=#FFD700>+{RewardCredits} кред.</color>";
		}

		public static void OnEnemyKilled(string enemyName)
		{
			if (!IsCompleted && !IsFailed && (CurrentType == BountyType.KillEnemies || (CurrentType == BountyType.KillSpecific && (enemyName.IndexOf(SpecificEnemy, StringComparison.OrdinalIgnoreCase) >= 0 || SpecificEnemy.IndexOf(enemyName, StringComparison.OrdinalIgnoreCase) >= 0))))
			{
				Progress++;
				Plugin.Log.LogInfo((object)$"[Bounty] Kill progress: {Progress}/{TargetAmount} ({enemyName})");
				if (Progress >= TargetAmount)
				{
					CompleteAndAward();
				}
			}
		}

		public static void OnPlayerDied()
		{
			_deathOccurred = true;
			if (CurrentType == BountyType.SurviveAll && !IsCompleted)
			{
				IsFailed = true;
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.AddTextToChatOnServer(ChatStyle.Soft("<color=#FF4444>✗ ЗАДАНИЕ ПРОВАЛЕНО: потеря члена экипажа.</color>"), -1);
				}
				Plugin.Log.LogInfo((object)"[Bounty] SurviveAll failed.");
			}
		}

		public static void OnShipLeaving()
		{
			if (IsCompleted || IsFailed)
			{
				return;
			}
			if (CurrentType == BountyType.CollectLoot)
			{
				int num = 0;
				GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
				foreach (GrabbableObject val in array)
				{
					if (!((Object)(object)val?.itemProperties == (Object)null) && val.itemProperties.isScrap && (val.isInShipRoom || val.isInElevator))
					{
						num += val.scrapValue;
					}
				}
				Progress = num;
				if (num >= TargetAmount)
				{
					CompleteAndAward();
					return;
				}
				IsFailed = true;
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.AddTextToChatOnServer(ChatStyle.Soft($"<color=#FF7A35>✗ ЗАДАНИЕ: доставлено {num}/{TargetAmount} кред. — не выполнено.</color>"), -1);
				}
			}
			else if (CurrentType == BountyType.SurviveAll && !_deathOccurred)
			{
				CompleteAndAward();
			}
		}

		private static void CompleteAndAward()
		{
			if (!IsCompleted)
			{
				IsCompleted = true;
				Terminal val = Object.FindObjectOfType<Terminal>();
				if ((Object)(object)val != (Object)null)
				{
					val.groupCredits += RewardCredits;
				}
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.AddTextToChatOnServer(ChatStyle.Soft("<color=#FFD700>★ ЗАДАНИЕ ВЫПОЛНЕНО!</color>\n" + $"<color=#4DFF6E>{Description}\n+{RewardCredits} кредитов зачислено!</color>"), -1);
				}
				Plugin.Log.LogInfo((object)$"[Bounty] Completed! +{RewardCredits} credits.");
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "KillEnemy")]
	public static class KillEnemyBountyPatch
	{
		[HarmonyPostfix]
		public static void Postfix(EnemyAI __instance)
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton != null && singleton.IsServer)
				{
					BountySystem.OnEnemyKilled(__instance.enemyType?.enemyName ?? "");
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
	public static class PlayerDeathBountyPatch
	{
		[HarmonyPostfix]
		public static void Postfix(PlayerControllerB __instance, bool spawnBody = true)
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton != null && singleton.IsServer)
				{
					BountySystem.OnPlayerDied();
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
	public static class ShipLeaveBountyPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton != null && singleton.IsServer)
				{
					BountySystem.OnShipLeaving();
				}
			}
			catch
			{
			}
		}
	}
	internal static class ChatStyle
	{
		private const string Alpha = "CC";

		private static readonly Regex _colorTag = new Regex("<color=#([0-9A-Fa-f]{6})>", RegexOptions.Compiled);

		internal static string Soft(string text)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return _colorTag.Replace(text, "<color=#$1CC>");
			}
			return text;
		}
	}
	public static class EventBalancer
	{
		private static readonly List<string> _history = new List<string>(64);

		private static int MaxHistorySize => ModConfig.MaxHistorySize?.Value ?? 14;

		private static Dictionary<GameEventRarity, int> BaseWeights => new Dictionary<GameEventRarity, int>
		{
			{
				GameEventRarity.Good,
				ModConfig.WeightGood?.Value ?? 15
			},
			{
				GameEventRarity.Neutral,
				ModConfig.WeightNeutral?.Value ?? 23
			},
			{
				GameEventRarity.VeryBad,
				ModConfig.WeightVeryBad?.Value ?? 52
			},
			{
				GameEventRarity.Insane,
				ModConfig.WeightInsane?.Value ?? 10
			}
		};

		public static GameEventData SelectEvent(IReadOnlyList<GameEventData> pool, Random random, ManualLogSource? log = null, List<GameEventData>? exclude = null)
		{
			List<GameEventData> list = SelectMultipleEvents(pool, 1, random, log, exclude);
			if (list.Count > 0)
			{
				return list[0];
			}
			if (log != null)
			{
				log.LogWarning((object)"[EventBalancer] Пул пуст после тег-исключений — выбираю без них (анти-дедлок).");
			}
			list = SelectMultipleEvents(pool, 1, random, log);
			if (list.Count > 0)
			{
				return list[0];
			}
			return pool[0];
		}

		public static List<GameEventData> SelectMultipleEvents(IReadOnlyList<GameEventData> pool, int count, Random random, ManualLogSource? log = null, List<GameEventData>? exclude = null)
		{
			List<GameEventData> list = new List<GameEventData>();
			for (int i = 0; i < count; i++)
			{
				List<GameEventData> list2 = new List<GameEventData>();
				if (exclude != null)
				{
					list2.AddRange(exclude);
				}
				list2.AddRange(list);
				List<(GameEventData, int)> list3 = BuildCandidatesForMultiple(pool, list2, log);
				if (list3.Count == 0)
				{
					if (log != null)
					{
						log.LogWarning((object)"[EventBalancer] Все доступные события на кулдауне — сбрасываю историю.");
					}
					_history.Clear();
					list2.Clear();
					if (exclude != null)
					{
						list2.AddRange(exclude);
					}
					list2.AddRange(list);
					list3 = BuildCandidatesForMultiple(pool, list2, log);
				}
				if (list3.Count == 0)
				{
					break;
				}
				GameEventData gameEventData = WeightedPick(list3, random);
				list.Add(gameEventData);
				RecordEvent(gameEventData.Id);
				if (log != null)
				{
					log.LogInfo((object)$"[EventBalancer] Выбрано событие ({i + 1}/{count}): [{gameEventData.Rarity}] {gameEventData.Name}");
				}
			}
			return list;
		}

		public static void ResetHistory()
		{
			_history.Clear();
		}

		public static IReadOnlyList<string> GetHistory()
		{
			return _history;
		}

		private static List<(GameEventData ev, int weight)> BuildCandidatesForMultiple(IReadOnlyList<GameEventData> pool, List<GameEventData> exclude, ManualLogSource? log)
		{
			IReadOnlyList<GameEventData> pool2 = pool;
			List<(GameEventData, int)> list = new List<(GameEventData, int)>(pool2.Count);
			int num = _history.TakeLast(12).Count((string id) => pool2.Any((GameEventData e) => e.Id == id && e.Rarity == GameEventRarity.Insane));
			foreach (GameEventData ev in pool2)
			{
				if (exclude.Any((GameEventData e) => e.Id == ev.Id) || !ModConfig.IsEventEnabled(ev.Id))
				{
					continue;
				}
				int num2 = BaseWeights[ev.Rarity];
				if (ev.Id.StartsWith("meteor_", StringComparison.Ordinal))
				{
					num2 = Math.Max(1, num2 / 3);
				}
				if (ev.Rarity == GameEventRarity.Insane && num >= 1)
				{
					continue;
				}
				int num3 = LastIndex(ev.Id);
				if (num3 >= 0)
				{
					int num4 = _history.Count - num3;
					if (num4 <= ev.CooldownRounds)
					{
						continue;
					}
					if (num4 <= ev.CooldownRounds * 2)
					{
						num2 = Math.Max(1, num2 / 2);
					}
				}
				list.Add((ev, num2));
			}
			return list;
		}

		private static GameEventData WeightedPick(List<(GameEventData ev, int weight)> candidates, Random random)
		{
			int maxValue = candidates.Sum<(GameEventData, int)>(((GameEventData ev, int weight) c) => c.weight);
			int num = random.Next(0, maxValue);
			int num2 = 0;
			foreach (var candidate in candidates)
			{
				GameEventData item = candidate.ev;
				int item2 = candidate.weight;
				num2 += item2;
				if (num < num2)
				{
					return item;
				}
			}
			return candidates[candidates.Count - 1].ev;
		}

		private static int LastIndex(string id)
		{
			for (int num = _history.Count - 1; num >= 0; num--)
			{
				if (_history[num] == id)
				{
					return num;
				}
			}
			return -1;
		}

		private static void RecordEvent(string id)
		{
			_history.Add(id);
			while (_history.Count > MaxHistorySize)
			{
				_history.RemoveAt(0);
			}
		}
	}
	public enum GameEventRarity
	{
		Good,
		Neutral,
		VeryBad,
		Insane
	}
	public sealed class GameEventData
	{
		public string Id { get; }

		public string Name { get; }

		public string Description { get; }

		public GameEventRarity Rarity { get; }

		public Action Effect { get; }

		public int CooldownRounds { get; }

		public LevelWeatherType? WeatherOverride { get; }

		public GameEventData(string id, string name, string description, GameEventRarity rarity, Action effect, int cooldownRounds = -1, LevelWeatherType? weatherOverride = null)
		{
			Id = id;
			Name = name;
			Description = description;
			Rarity = rarity;
			Effect = effect;
			CooldownRounds = ((cooldownRounds >= 0) ? cooldownRounds : DefaultCooldown(rarity));
			WeatherOverride = weatherOverride;
		}

		private static int DefaultCooldown(GameEventRarity rarity)
		{
			return rarity switch
			{
				GameEventRarity.Good => 4, 
				GameEventRarity.Neutral => 5, 
				GameEventRarity.VeryBad => 6, 
				GameEventRarity.Insane => 12, 
				_ => 5, 
			};
		}
	}
	internal static class EventHelpers
	{
		private class LevelSnapshot
		{
			public int minScrap;

			public int maxScrap;

			public int minTotalScrapValue;

			public int maxTotalScrapValue;

			public int maxEnemyPowerCount;

			public int maxOutsideEnemyPowerCount;

			public int maxDaytimeEnemyPowerCount;

			public LevelWeatherType currentWeather;

			public List<SpawnableEnemyWithRarity> Enemies;

			public List<SpawnableEnemyWithRarity> OutsideEnemies;

			public List<SpawnableEnemyWithRarity> DaytimeEnemies;

			public List<SpawnableItemWithRarity> spawnableScrap;

			public SpawnableMapObject[] spawnableMapObjects;

			public Dictionary<EnemyType, int> insideRarities = new Dictionary<EnemyType, int>();

			public Dictionary<EnemyType, int> outsideRarities = new Dictionary<EnemyType, int>();

			public Dictionary<EnemyType, int> daytimeRarities = new Dictionary<EnemyType, int>();

			public Dictionary<Item, int> scrapRarities = new Dictionary<Item, int>();

			public Dictionary<SpawnableMapObject, AnimationCurve> mapObjectCurves = new Dictionary<SpawnableMapObject, AnimationCurve>();
		}

		private class ItemValueBackup
		{
			public int minValue;

			public int maxValue;
		}

		private static readonly Dictionary<string, LevelSnapshot> _levelSnapshots = new Dictionary<string, LevelSnapshot>();

		private static readonly Dictionary<Item, ItemValueBackup> _itemBackups = new Dictionary<Item, ItemValueBackup>();

		internal static RoundManager? RM => RoundManager.Instance;

		internal static SelectableLevel? Level => StartOfRound.Instance?.currentLevel;

		internal static void BackupAndModifyItemValue(Item item, int newMin, int newMax)
		{
			if (!((Object)(object)item == (Object)null))
			{
				if (!_itemBackups.ContainsKey(item))
				{
					_itemBackups[item] = new ItemValueBackup
					{
						minValue = item.minValue,
						maxValue = item.maxValue
					};
				}
				item.minValue = newMin;
				item.maxValue = newMax;
			}
		}

		internal static void RestoreOriginalLevelSettings()
		{
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_046d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			foreach (KeyValuePair<Item, ItemValueBackup> itemBackup in _itemBackups)
			{
				if ((Object)(object)itemBackup.Key != (Object)null && itemBackup.Value != null)
				{
					itemBackup.Key.minValue = itemBackup.Value.minValue;
					itemBackup.Key.maxValue = itemBackup.Value.maxValue;
				}
			}
			_itemBackups.Clear();
			string name = ((Object)level).name;
			SpawnableMapObject[] spawnableMapObjects;
			if (!_levelSnapshots.TryGetValue(name, out LevelSnapshot value))
			{
				value = new LevelSnapshot
				{
					minScrap = level.minScrap,
					maxScrap = level.maxScrap,
					minTotalScrapValue = level.minTotalScrapValue,
					maxTotalScrapValue = level.maxTotalScrapValue,
					maxEnemyPowerCount = level.maxEnemyPowerCount,
					maxOutsideEnemyPowerCount = level.maxOutsideEnemyPowerCount,
					maxDaytimeEnemyPowerCount = level.maxDaytimeEnemyPowerCount,
					currentWeather = level.currentWeather
				};
				if (level.Enemies != null)
				{
					value.Enemies = new List<SpawnableEnemyWithRarity>(level.Enemies);
					foreach (SpawnableEnemyWithRarity enemy in level.Enemies)
					{
						if ((Object)(object)enemy?.enemyType != (Object)null && !value.insideRarities.ContainsKey(enemy.enemyType))
						{
							value.insideRarities[enemy.enemyType] = enemy.rarity;
						}
					}
				}
				else
				{
					value.Enemies = new List<SpawnableEnemyWithRarity>();
				}
				if (level.OutsideEnemies != null)
				{
					value.OutsideEnemies = new List<SpawnableEnemyWithRarity>(level.OutsideEnemies);
					foreach (SpawnableEnemyWithRarity outsideEnemy in level.OutsideEnemies)
					{
						if ((Object)(object)outsideEnemy?.enemyType != (Object)null && !value.outsideRarities.ContainsKey(outsideEnemy.enemyType))
						{
							value.outsideRarities[outsideEnemy.enemyType] = outsideEnemy.rarity;
						}
					}
				}
				else
				{
					value.OutsideEnemies = new List<SpawnableEnemyWithRarity>();
				}
				if (level.DaytimeEnemies != null)
				{
					value.DaytimeEnemies = new List<SpawnableEnemyWithRarity>(level.DaytimeEnemies);
					foreach (SpawnableEnemyWithRarity daytimeEnemy in level.DaytimeEnemies)
					{
						if ((Object)(object)daytimeEnemy?.enemyType != (Object)null && !value.daytimeRarities.ContainsKey(daytimeEnemy.enemyType))
						{
							value.daytimeRarities[daytimeEnemy.enemyType] = daytimeEnemy.rarity;
						}
					}
				}
				else
				{
					value.DaytimeEnemies = new List<SpawnableEnemyWithRarity>();
				}
				if (level.spawnableScrap != null)
				{
					value.spawnableScrap = new List<SpawnableItemWithRarity>(level.spawnableScrap);
					foreach (SpawnableItemWithRarity item in level.spawnableScrap)
					{
						if ((Object)(object)item?.spawnableItem != (Object)null && !value.scrapRarities.ContainsKey(item.spawnableItem))
						{
							value.scrapRarities[item.spawnableItem] = item.rarity;
						}
					}
				}
				else
				{
					value.spawnableScrap = new List<SpawnableItemWithRarity>();
				}
				if (level.spawnableMapObjects != null)
				{
					value.spawnableMapObjects = (SpawnableMapObject[])level.spawnableMapObjects.Clone();
					spawnableMapObjects = level.spawnableMapObjects;
					foreach (SpawnableMapObject val in spawnableMapObjects)
					{
						if (val != null && !value.mapObjectCurves.ContainsKey(val))
						{
							value.mapObjectCurves[val] = val.numberToSpawn;
						}
					}
				}
				else
				{
					value.spawnableMapObjects = Array.Empty<SpawnableMapObject>();
				}
				_levelSnapshots[name] = value;
				Plugin.Log.LogInfo((object)("[LevelBaseline] Created settings snapshot for moon: " + name));
				return;
			}
			level.minScrap = value.minScrap;
			level.maxScrap = value.maxScrap;
			level.minTotalScrapValue = value.minTotalScrapValue;
			level.maxTotalScrapValue = value.maxTotalScrapValue;
			level.maxEnemyPowerCount = value.maxEnemyPowerCount;
			level.maxOutsideEnemyPowerCount = value.maxOutsideEnemyPowerCount;
			level.maxDaytimeEnemyPowerCount = value.maxDaytimeEnemyPowerCount;
			level.currentWeather = value.currentWeather;
			level.Enemies = new List<SpawnableEnemyWithRarity>(value.Enemies);
			level.OutsideEnemies = new List<SpawnableEnemyWithRarity>(value.OutsideEnemies);
			level.DaytimeEnemies = new List<SpawnableEnemyWithRarity>(value.DaytimeEnemies);
			level.spawnableScrap = new List<SpawnableItemWithRarity>(value.spawnableScrap);
			level.spawnableMapObjects = (SpawnableMapObject[])value.spawnableMapObjects.Clone();
			foreach (SpawnableEnemyWithRarity enemy2 in level.Enemies)
			{
				if ((Object)(object)enemy2?.enemyType != (Object)null && value.insideRarities.TryGetValue(enemy2.enemyType, out var value2))
				{
					enemy2.rarity = value2;
				}
			}
			foreach (SpawnableEnemyWithRarity outsideEnemy2 in level.OutsideEnemies)
			{
				if ((Object)(object)outsideEnemy2?.enemyType != (Object)null && value.outsideRarities.TryGetValue(outsideEnemy2.enemyType, out var value3))
				{
					outsideEnemy2.rarity = value3;
				}
			}
			foreach (SpawnableEnemyWithRarity daytimeEnemy2 in level.DaytimeEnemies)
			{
				if ((Object)(object)daytimeEnemy2?.enemyType != (Object)null && value.daytimeRarities.TryGetValue(daytimeEnemy2.enemyType, out var value4))
				{
					daytimeEnemy2.rarity = value4;
				}
			}
			foreach (SpawnableItemWithRarity item2 in level.spawnableScrap)
			{
				if ((Object)(object)item2?.spawnableItem != (Object)null && value.scrapRarities.TryGetValue(item2.spawnableItem, out var value5))
				{
					item2.rarity = value5;
				}
			}
			spawnableMapObjects = level.spawnableMapObjects;
			foreach (SpawnableMapObject val2 in spawnableMapObjects)
			{
				if (val2 != null && value.mapObjectCurves.TryGetValue(val2, out AnimationCurve value6))
				{
					val2.numberToSpawn = value6;
				}
			}
			Plugin.Log.LogInfo((object)("[LevelBaseline] Restored original settings for moon: " + name));
		}

		internal static void MultiplyMapObjects(float mineMult, float turretMult)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			SelectableLevel level = Level;
			if (level?.spawnableMapObjects == null)
			{
				return;
			}
			SpawnableMapObject[] spawnableMapObjects = level.spawnableMapObjects;
			foreach (SpawnableMapObject val in spawnableMapObjects)
			{
				if ((Object)(object)val?.prefabToSpawn == (Object)null)
				{
					continue;
				}
				string text = ((Object)val.prefabToSpawn).name.ToLower();
				if (text.Contains("landmine") || text.Contains("mine"))
				{
					AnimationCurve numberToSpawn = val.numberToSpawn;
					if (numberToSpawn != null)
					{
						Keyframe[] keys = numberToSpawn.keys;
						for (int j = 0; j < keys.Length; j++)
						{
							ref Keyframe reference = ref keys[j];
							((Keyframe)(ref reference)).value = ((Keyframe)(ref reference)).value * mineMult;
						}
						val.numberToSpawn = new AnimationCurve(keys);
					}
				}
				else
				{
					if (!text.Contains("turret"))
					{
						continue;
					}
					AnimationCurve numberToSpawn2 = val.numberToSpawn;
					if (numberToSpawn2 != null)
					{
						Keyframe[] keys2 = numberToSpawn2.keys;
						for (int k = 0; k < keys2.Length; k++)
						{
							ref Keyframe reference2 = ref keys2[k];
							((Keyframe)(ref reference2)).value = ((Keyframe)(ref reference2)).value * turretMult;
						}
						val.numberToSpawn = new AnimationCurve(keys2);
					}
				}
			}
		}

		internal static float GetMaxScrapValue(SelectableLevel? level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return 3.8f;
			}
			string text = ((Object)level).name.ToLower();
			string text2 = (level.PlanetName ?? "").ToLower();
			if (text.Contains("experimentation") || text2.Contains("experimentation") || text.Contains("assurance") || text2.Contains("assurance") || text.Contains("vow") || text2.Contains("vow") || level.riskLevel.Contains("D") || level.riskLevel.Contains("C"))
			{
				return 1.8f;
			}
			if (text.Contains("march") || text2.Contains("march") || text.Contains("offense") || text2.Contains("offense") || text.Contains("adamance") || text2.Contains("adamance") || text.Contains("embry") || text2.Contains("embry") || level.riskLevel.Contains("B") || level.riskLevel.Contains("A"))
			{
				return 3f;
			}
			if (text.Contains("artifice") || text2.Contains("artifice"))
			{
				return 5f;
			}
			return 4f;
		}

		internal static float GetScaledScrapMultiplier(float targetMult)
		{
			if (targetMult <= 1f)
			{
				return targetMult;
			}
			float maxScrapValue = GetMaxScrapValue(Level);
			float num = targetMult - 1f;
			float num2 = maxScrapValue / 5f;
			return 1f + num * num2;
		}

		internal static void MultiplyScrapValue(float mult)
		{
			if (!((Object)(object)RM == (Object)null))
			{
				float scaledScrapMultiplier = GetScaledScrapMultiplier(mult);
				float num = EventBaseline.ScrapValue * (scaledScrapMultiplier - 1f);
				RM.scrapValueMultiplier = Mathf.Max(0.05f, RM.scrapValueMultiplier + num);
			}
		}

		internal static void MultiplyScrapAmount(float mult)
		{
			if (!((Object)(object)RM == (Object)null))
			{
				float num = EventBaseline.ScrapAmount * (mult - 1f);
				RM.scrapAmountMultiplier = Mathf.Max(0.05f, RM.scrapAmountMultiplier + num);
			}
		}

		internal static void SetScrap(float valueMult, float amountMult)
		{
			if (valueMult >= 0f)
			{
				MultiplyScrapValue(valueMult);
			}
			if (amountMult >= 0f)
			{
				MultiplyScrapAmount(amountMult);
			}
		}

		internal static void FilterScrapToSpecificItems(int minVal, int maxVal, params string[] nameFragments)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			List<Item> list = StartOfRound.Instance?.allItemsList?.itemsList;
			if (list == null)
			{
				list = Resources.FindObjectsOfTypeAll<Item>().ToList();
			}
			List<Item> list2 = new List<Item>();
			foreach (Item item in list)
			{
				if (!((Object)(object)item == (Object)null) && item.itemName != null && nameFragments.Any((string frag) => item.itemName.IndexOf(frag, StringComparison.OrdinalIgnoreCase) >= 0))
				{
					list2.Add(item);
				}
			}
			if (list2.Count == 0)
			{
				return;
			}
			foreach (Item item2 in list2)
			{
				BackupAndModifyItemValue(item2, minVal, maxVal);
			}
			List<SpawnableItemWithRarity> list3 = new List<SpawnableItemWithRarity>();
			foreach (Item item3 in list2)
			{
				list3.Add(new SpawnableItemWithRarity(item3, 100));
			}
			level.spawnableScrap = list3;
		}

		internal static void FilterScrapToOneRandomType(Random rng)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			SelectableLevel level = Level;
			if (level?.spawnableScrap != null && level.spawnableScrap.Count != 0)
			{
				List<SpawnableItemWithRarity> list = level.spawnableScrap.Where((SpawnableItemWithRarity s) => (Object)(object)s?.spawnableItem != (Object)null && s.rarity > 0).ToList();
				if (list.Count != 0)
				{
					SpawnableItemWithRarity val = list[rng.Next(list.Count)];
					level.spawnableScrap = new List<SpawnableItemWithRarity>
					{
						new SpawnableItemWithRarity(val.spawnableItem, 100)
					};
					Plugin.Log.LogInfo((object)("[EventHelpers] FilterScrapToOneRandomType: only '" + val.spawnableItem.itemName + "' will spawn."));
				}
			}
		}

		internal static void FilterScrapToSingleRandomItem(int minVal, int maxVal)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			SelectableLevel level = Level;
			if (level?.spawnableScrap != null && level.spawnableScrap.Count != 0)
			{
				Random random = new Random(Plugin.RoundSeed + 123);
				SpawnableItemWithRarity val = level.spawnableScrap[random.Next(level.spawnableScrap.Count)];
				if (!((Object)(object)val?.spawnableItem == (Object)null))
				{
					BackupAndModifyItemValue(val.spawnableItem, minVal, maxVal);
					level.spawnableScrap = new List<SpawnableItemWithRarity>
					{
						new SpawnableItemWithRarity(val.spawnableItem, 100)
					};
				}
			}
		}

		internal static void SetAllScrapValuesRange(int minVal, int maxVal)
		{
			SelectableLevel level = Level;
			if (level?.spawnableScrap == null)
			{
				return;
			}
			foreach (SpawnableItemWithRarity item in level.spawnableScrap)
			{
				if ((Object)(object)item?.spawnableItem != (Object)null)
				{
					BackupAndModifyItemValue(item.spawnableItem, minVal, maxVal);
				}
			}
		}

		internal static int GetMonsterDangerWeight(string name)
		{
			string text = name.ToLower();
			if (text.Contains("hoard") || text.Contains("bug") || text.Contains("loot") || text.Contains("puffer") || text.Contains("lizard") || text.Contains("manti") || text.Contains("locust") || text.Contains("bee"))
			{
				return 2;
			}
			if (text.Contains("flower") || text.Contains("bracken") || text.Contains("spring") || text.Contains("coil") || text.Contains("jester") || text.Contains("nutcracker") || text.Contains("girl") || text.Contains("dress") || text.Contains("crawler") || text.Contains("thumper") || text.Contains("giant") || text.Contains("keeper") || text.Contains("dog") || text.Contains("mouth"))
			{
				return 25;
			}
			return 73;
		}

		internal static void FilterInsideToWeightedRandomOne(Random rng)
		{
			SelectableLevel level = Level;
			if (level?.Enemies == null || level.Enemies.Count == 0)
			{
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.Enemies.Where((SpawnableEnemyWithRarity e) => e.rarity > 0 && (Object)(object)e.enemyType != (Object)null).ToList();
			if (list.Count == 0)
			{
				return;
			}
			List<(SpawnableEnemyWithRarity, int)> list2 = new List<(SpawnableEnemyWithRarity, int)>();
			foreach (SpawnableEnemyWithRarity item in list)
			{
				int monsterDangerWeight = GetMonsterDangerWeight(item.enemyType?.enemyName ?? "");
				list2.Add((item, monsterDangerWeight));
			}
			int maxValue = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight);
			int num = rng.Next(0, maxValue);
			int num2 = 0;
			SpawnableEnemyWithRarity val = list[0];
			foreach (var item2 in list2)
			{
				num2 += item2.Item2;
				if (num < num2)
				{
					(val, _) = item2;
					break;
				}
			}
			if ((Object)(object)val?.enemyType != (Object)null)
			{
				MakeEnemyTypesSpawnEverywhere(new List<EnemyType> { val.enemyType });
			}
		}

		internal static void FilterOutsideToWeightedRandomOne(Random rng)
		{
			SelectableLevel level = Level;
			if (level?.OutsideEnemies == null || level.OutsideEnemies.Count == 0)
			{
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.OutsideEnemies.Where((SpawnableEnemyWithRarity e) => e.rarity > 0 && (Object)(object)e.enemyType != (Object)null).ToList();
			if (list.Count == 0)
			{
				return;
			}
			List<(SpawnableEnemyWithRarity, int)> list2 = new List<(SpawnableEnemyWithRarity, int)>();
			foreach (SpawnableEnemyWithRarity item in list)
			{
				int monsterDangerWeight = GetMonsterDangerWeight(item.enemyType?.enemyName ?? "");
				list2.Add((item, monsterDangerWeight));
			}
			int maxValue = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight);
			int num = rng.Next(0, maxValue);
			int num2 = 0;
			SpawnableEnemyWithRarity val = list[0];
			foreach (var item2 in list2)
			{
				num2 += item2.Item2;
				if (num < num2)
				{
					(val, _) = item2;
					break;
				}
			}
			if ((Object)(object)val?.enemyType != (Object)null)
			{
				MakeEnemyTypesSpawnEverywhere(new List<EnemyType> { val.enemyType });
			}
		}

		private static float ScaleEventMult(float mult)
		{
			return 1f + (mult - 1f) * ModConfig.EventPowerScale.Value;
		}

		internal static void MultiplyInsidePower(float mult)
		{
			SelectableLevel level = Level;
			if (!((Object)(object)level == (Object)null))
			{
				int num = Mathf.RoundToInt((float)((EventBaseline.InsidePower > 0) ? EventBaseline.InsidePower : level.maxEnemyPowerCount) * (ScaleEventMult(mult) - 1f));
				level.maxEnemyPowerCount = Mathf.Max(0, level.maxEnemyPowerCount + num);
			}
		}

		internal static void MultiplyOutsidePower(float mult)
		{
			SelectableLevel level = Level;
			if (!((Object)(object)level == (Object)null))
			{
				int num = Mathf.RoundToInt((float)((EventBaseline.OutsidePower > 0) ? EventBaseline.OutsidePower : level.maxOutsideEnemyPowerCount) * (ScaleEventMult(mult) - 1f));
				level.maxOutsideEnemyPowerCount = Mathf.Max(0, level.maxOutsideEnemyPowerCount + num);
			}
		}

		internal static void MultiplyDaytimePower(float mult)
		{
			SelectableLevel level = Level;
			if (!((Object)(object)level == (Object)null))
			{
				int num = Mathf.RoundToInt((float)((EventBaseline.DaytimePower > 0) ? EventBaseline.DaytimePower : level.maxDaytimeEnemyPowerCount) * (ScaleEventMult(mult) - 1f));
				level.maxDaytimeEnemyPowerCount = Mathf.Max(0, level.maxDaytimeEnemyPowerCount + num);
			}
		}

		internal static void SetAllPower(float insideMult, float outsideMult, float daytimeMult)
		{
			MultiplyInsidePower(insideMult);
			MultiplyOutsidePower(outsideMult);
			MultiplyDaytimePower(daytimeMult);
		}

		internal static void ZeroAllPower()
		{
			SelectableLevel level = Level;
			if (!((Object)(object)level == (Object)null))
			{
				level.maxEnemyPowerCount = 0;
				level.maxOutsideEnemyPowerCount = 0;
				level.maxDaytimeEnemyPowerCount = 0;
			}
		}

		internal static void SetWeather(LevelWeatherType type)
		{
			//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)
			if (!((Object)(object)Level == (Object)null))
			{
				Level.currentWeather = type;
			}
		}

		internal static void TriggerMeteorShower()
		{
			RoundManager rM = RM;
			if ((Object)(object)rM == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[Meteor] RoundManager не найден — запасной вариант: погода Stormy.");
				SetWeather((LevelWeatherType)2);
			}
			else
			{
				((MonoBehaviour)rM).StartCoroutine(TriggerMeteorShowerCoroutine(Plugin.RoundSeed));
			}
		}

		private static IEnumerator TriggerMeteorShowerCoroutine(int roundSeed)
		{
			float waited = 0f;
			while (waited < 120f)
			{
				if (Plugin.RoundSeed != roundSeed)
				{
					yield break;
				}
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance != (Object)null && instance.timeHasStarted)
				{
					break;
				}
				waited += Time.deltaTime;
				yield return null;
			}
			if (Plugin.RoundSeed == roundSeed)
			{
				TimeOfDay instance2 = TimeOfDay.Instance;
				if ((Object)(object)instance2 == (Object)null || !instance2.timeHasStarted)
				{
					Plugin.Log.LogWarning((object)"[Meteor] День так и не начался за отведённое время — ставлю Stormy вместо ливня.");
					SetWeather((LevelWeatherType)2);
				}
				else
				{
					float num = (instance2.meteorShowerAtTime = Mathf.Clamp(instance2.normalizedTimeOfDay + Random.Range(0.03f, 0.12f), 0.02f, 0.78f));
					Plugin.Log.LogInfo((object)($"[Meteor] Ливень запланирован на normalizedTimeOfDay={num:0.00} " + $"(сейчас {instance2.normalizedTimeOfDay:0.00}); запуск выполнит сама игра " + "(TimeOfDay.TimeOfDayEvents → MeteorWeather.SetStartMeteorShower → BeginDay) — тем же путём, что и обычные случайные ливни."));
				}
			}
		}

		internal static void BoostSpawnCurve(float multiplier = 3f)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			try
			{
				if (level.enemySpawnChanceThroughoutDay != null)
				{
					Keyframe[] keys = level.enemySpawnChanceThroughoutDay.keys;
					for (int i = 0; i < keys.Length; i++)
					{
						ref Keyframe reference = ref keys[i];
						((Keyframe)(ref reference)).value = ((Keyframe)(ref reference)).value * multiplier;
					}
					level.enemySpawnChanceThroughoutDay = new AnimationCurve(keys);
				}
				if (level.outsideEnemySpawnChanceThroughDay != null)
				{
					Keyframe[] keys2 = level.outsideEnemySpawnChanceThroughDay.keys;
					for (int j = 0; j < keys2.Length; j++)
					{
						ref Keyframe reference2 = ref keys2[j];
						((Keyframe)(ref reference2)).value = ((Keyframe)(ref reference2)).value * multiplier;
					}
					level.outsideEnemySpawnChanceThroughDay = new AnimationCurve(keys2);
				}
				Plugin.Log.LogInfo((object)$"[EventHelpers] BoostSpawnCurve x{multiplier} applied.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[EventHelpers] BoostSpawnCurve error: " + ex.Message));
			}
		}

		internal static void RemoveThumper(SelectableLevel? level)
		{
			if (!((Object)(object)level == (Object)null))
			{
				int num = 0;
				num += StripEnemy(ref level.Enemies, "CrawlerAI");
				num += StripEnemy(ref level.OutsideEnemies, "CrawlerAI");
				num += StripEnemy(ref level.DaytimeEnemies, "CrawlerAI");
				if (num > 0)
				{
					Plugin.Log.LogInfo((object)$"[RandomEvents] Removed {num} Thumper entries from spawn pool.");
				}
			}
		}

		private static int StripEnemy(ref List<SpawnableEnemyWithRarity> list, string componentName)
		{
			if (list == null)
			{
				return 0;
			}
			List<SpawnableEnemyWithRarity> list2 = new List<SpawnableEnemyWithRarity>();
			int num = 0;
			foreach (SpawnableEnemyWithRarity item in list)
			{
				if ((Object)(object)item?.enemyType?.enemyPrefab != (Object)null && (Object)(object)item.enemyType.enemyPrefab.GetComponent(componentName) != (Object)null)
				{
					num++;
				}
				else if (item != null)
				{
					list2.Add(item);
				}
			}
			list = list2;
			return num;
		}

		internal static void MakeEnemyTypesSpawnEverywhere(List<EnemyType> enemyTypes)
		{
			SelectableLevel level = Level;
			if (!((Object)(object)level == (Object)null) && enemyTypes != null && enemyTypes.Count != 0)
			{
				FilterEnemyListToTargetTypes(ref level.Enemies, enemyTypes);
				FilterEnemyListToTargetTypes(ref level.OutsideEnemies, enemyTypes);
				int num = Mathf.Max(level.maxEnemyPowerCount, level.maxOutsideEnemyPowerCount);
				if (num > 0 && num < 6)
				{
					num = 6;
				}
				if (num > 0)
				{
					level.maxEnemyPowerCount = num;
					level.maxOutsideEnemyPowerCount = num;
				}
			}
		}

		private static void FilterEnemyListToTargetTypes(ref List<SpawnableEnemyWithRarity> list, List<EnemyType> targetTypes)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			if (list == null)
			{
				list = new List<SpawnableEnemyWithRarity>();
			}
			foreach (SpawnableEnemyWithRarity item in list)
			{
				if ((Object)(object)item?.enemyType != (Object)null)
				{
					item.rarity = (targetTypes.Contains(item.enemyType) ? 100 : 0);
				}
			}
			foreach (EnemyType type in targetTypes)
			{
				if (!list.Any((SpawnableEnemyWithRarity e) => (Object)(object)e.enemyType == (Object)(object)type))
				{
					list.Add(new SpawnableEnemyWithRarity(type, 100));
				}
			}
		}

		internal static void FilterInsideEnemies(params string[] nameFragments)
		{
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			List<EnemyType> list = new List<EnemyType>();
			if (level.Enemies != null)
			{
				foreach (SpawnableEnemyWithRarity enemy in level.Enemies)
				{
					if ((Object)(object)enemy?.enemyType != (Object)null && MatchesName(enemy.enemyType.enemyName, nameFragments) && !list.Contains(enemy.enemyType))
					{
						list.Add(enemy.enemyType);
					}
				}
			}
			if (list.Count == 0 && level.OutsideEnemies != null)
			{
				foreach (SpawnableEnemyWithRarity outsideEnemy in level.OutsideEnemies)
				{
					if ((Object)(object)outsideEnemy?.enemyType != (Object)null && MatchesName(outsideEnemy.enemyType.enemyName, nameFragments) && !list.Contains(outsideEnemy.enemyType))
					{
						list.Add(outsideEnemy.enemyType);
					}
				}
			}
			if (list.Count == 0)
			{
				SelectableLevel[] array = Object.FindObjectsOfType<SelectableLevel>();
				foreach (SelectableLevel val in array)
				{
					List<SpawnableEnemyWithRarity> list2 = new List<SpawnableEnemyWithRarity>();
					if (val.Enemies != null)
					{
						list2.AddRange(val.Enemies);
					}
					if (val.OutsideEnemies != null)
					{
						list2.AddRange(val.OutsideEnemies);
					}
					foreach (SpawnableEnemyWithRarity item in list2)
					{
						if ((Object)(object)item?.enemyType != (Object)null && MatchesName(item.enemyType.enemyName, nameFragments) && !list.Contains(item.enemyType))
						{
							list.Add(item.enemyType);
						}
					}
					if (list.Count > 0)
					{
						break;
					}
				}
				if (list.Count <= 0)
				{
					Plugin.Log.LogWarning((object)("[EventHelpers] FilterInsideEnemies: '" + string.Join(",", nameFragments) + "' not found anywhere — skipping filter."));
					return;
				}
				Plugin.Log.LogInfo((object)"[EventHelpers] FilterInsideEnemies: enemy not on this moon, force-adding from another level.");
			}
			if (list.Count > 0)
			{
				MakeEnemyTypesSpawnEverywhere(list);
			}
		}

		internal static void FilterInsideToOutside(params string[] nameFragments)
		{
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			EnemyType target = null;
			if (level.Enemies != null)
			{
				foreach (SpawnableEnemyWithRarity enemy in level.Enemies)
				{
					if ((Object)(object)enemy?.enemyType != (Object)null && MatchesName(enemy.enemyType.enemyName, nameFragments))
					{
						target = enemy.enemyType;
						break;
					}
				}
			}
			if ((Object)(object)target == (Object)null)
			{
				SelectableLevel[] array = Object.FindObjectsOfType<SelectableLevel>();
				foreach (SelectableLevel val in array)
				{
					if (val?.Enemies == null)
					{
						continue;
					}
					foreach (SpawnableEnemyWithRarity enemy2 in val.Enemies)
					{
						if ((Object)(object)enemy2?.enemyType != (Object)null && MatchesName(enemy2.enemyType.enemyName, nameFragments))
						{
							target = enemy2.enemyType;
							break;
						}
					}
					if ((Object)(object)target != (Object)null)
					{
						break;
					}
				}
			}
			if ((Object)(object)target == (Object)null)
			{
				Plugin.Log.LogWarning((object)("[EventHelpers] FilterInsideToOutside: '" + string.Join(",", nameFragments) + "' not found anywhere — skipping."));
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.OutsideEnemies;
			if (list == null)
			{
				list = (level.OutsideEnemies = new List<SpawnableEnemyWithRarity>());
			}
			else
			{
				foreach (SpawnableEnemyWithRarity item in list)
				{
					if (item != null)
					{
						item.rarity = 0;
					}
				}
			}
			SpawnableEnemyWithRarity val2 = ((IEnumerable<SpawnableEnemyWithRarity>)list).FirstOrDefault((Func<SpawnableEnemyWithRarity, bool>)((SpawnableEnemyWithRarity e) => (Object)(object)e?.enemyType == (Object)(object)target));
			if (val2 != null)
			{
				val2.rarity = 100;
			}
			else
			{
				list.Add(new SpawnableEnemyWithRarity(target, 100));
			}
			Plugin.Log.LogInfo((object)("[EventHelpers] FilterInsideToOutside: '" + target.enemyName + "' → OutsideEnemies rarity=100."));
		}

		internal static void FilterOutsideToInside(params string[] nameFragments)
		{
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			EnemyType target = null;
			if (level.OutsideEnemies != null)
			{
				foreach (SpawnableEnemyWithRarity outsideEnemy in level.OutsideEnemies)
				{
					if ((Object)(object)outsideEnemy?.enemyType != (Object)null && MatchesName(outsideEnemy.enemyType.enemyName, nameFragments))
					{
						target = outsideEnemy.enemyType;
						break;
					}
				}
			}
			if ((Object)(object)target == (Object)null)
			{
				SelectableLevel[] array = Object.FindObjectsOfType<SelectableLevel>();
				foreach (SelectableLevel val in array)
				{
					if (val?.OutsideEnemies != null)
					{
						foreach (SpawnableEnemyWithRarity outsideEnemy2 in val.OutsideEnemies)
						{
							if ((Object)(object)outsideEnemy2?.enemyType != (Object)null && MatchesName(outsideEnemy2.enemyType.enemyName, nameFragments))
							{
								target = outsideEnemy2.enemyType;
								break;
							}
						}
					}
					if ((Object)(object)target != (Object)null)
					{
						break;
					}
				}
			}
			if ((Object)(object)target == (Object)null)
			{
				Plugin.Log.LogWarning((object)("[EventHelpers] FilterOutsideToInside: '" + string.Join(",", nameFragments) + "' not found anywhere — skipping."));
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.Enemies;
			if (list == null)
			{
				list = (level.Enemies = new List<SpawnableEnemyWithRarity>());
			}
			else
			{
				foreach (SpawnableEnemyWithRarity item in list)
				{
					if (item != null)
					{
						item.rarity = 0;
					}
				}
			}
			SpawnableEnemyWithRarity val2 = ((IEnumerable<SpawnableEnemyWithRarity>)list).FirstOrDefault((Func<SpawnableEnemyWithRarity, bool>)((SpawnableEnemyWithRarity e) => (Object)(object)e?.enemyType == (Object)(object)target));
			if (val2 != null)
			{
				val2.rarity = 100;
			}
			else
			{
				list.Add(new SpawnableEnemyWithRarity(target, 100));
			}
			Plugin.Log.LogInfo((object)("[EventHelpers] FilterOutsideToInside: '" + target.enemyName + "' → Enemies rarity=100."));
		}

		internal static void FilterOutsideEnemies(params string[] nameFragments)
		{
			SelectableLevel level = Level;
			if ((Object)(object)level == (Object)null)
			{
				return;
			}
			List<EnemyType> list = new List<EnemyType>();
			if (level.OutsideEnemies != null)
			{
				foreach (SpawnableEnemyWithRarity outsideEnemy in level.OutsideEnemies)
				{
					if ((Object)(object)outsideEnemy?.enemyType != (Object)null && MatchesName(outsideEnemy.enemyType.enemyName, nameFragments) && !list.Contains(outsideEnemy.enemyType))
					{
						list.Add(outsideEnemy.enemyType);
					}
				}
			}
			if (list.Count == 0 && level.Enemies != null)
			{
				foreach (SpawnableEnemyWithRarity enemy in level.Enemies)
				{
					if ((Object)(object)enemy?.enemyType != (Object)null && MatchesName(enemy.enemyType.enemyName, nameFragments) && !list.Contains(enemy.enemyType))
					{
						list.Add(enemy.enemyType);
					}
				}
			}
			if (list.Count > 0)
			{
				MakeEnemyTypesSpawnEverywhere(list);
			}
		}

		internal static void FilterDaytimeEnemies(params string[] nameFragments)
		{
			string[] nameFragments2 = nameFragments;
			SelectableLevel level = Level;
			if (level?.DaytimeEnemies == null || level.DaytimeEnemies.Count == 0 || !level.DaytimeEnemies.Any((SpawnableEnemyWithRarity e) => MatchesName(e.enemyType?.enemyName, nameFragments2)))
			{
				return;
			}
			foreach (SpawnableEnemyWithRarity daytimeEnemy in level.DaytimeEnemies)
			{
				daytimeEnemy.rarity = (MatchesName(daytimeEnemy.enemyType?.enemyName, nameFragments2) ? 100 : 0);
			}
		}

		internal static void FilterInsideToRandomOne(Random rng)
		{
			SelectableLevel level = Level;
			if (level?.Enemies == null || level.Enemies.Count == 0)
			{
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.Enemies.Where((SpawnableEnemyWithRarity e) => e.rarity > 0).ToList();
			if (list.Count != 0)
			{
				SpawnableEnemyWithRarity val = list[rng.Next(list.Count)];
				if ((Object)(object)val?.enemyType != (Object)null)
				{
					MakeEnemyTypesSpawnEverywhere(new List<EnemyType> { val.enemyType });
				}
			}
		}

		internal static void FilterOutsideToRandomOne(Random rng)
		{
			SelectableLevel level = Level;
			if (level?.OutsideEnemies == null || level.OutsideEnemies.Count == 0)
			{
				return;
			}
			List<SpawnableEnemyWithRarity> list = level.OutsideEnemies.Where((SpawnableEnemyWithRarity e) => e.rarity > 0).ToList();
			if (list.Count != 0)
			{
				SpawnableEnemyWithRarity val = list[rng.Next(list.Count)];
				if ((Object)(object)val?.enemyType != (Object)null)
				{
					MakeEnemyTypesSpawnEverywhere(new List<EnemyType> { val.enemyType });
				}
			}
		}

		private static bool MatchesName(string? name, string[] fragments)
		{
			string name2 = name;
			if (name2 == null)
			{
				return false;
			}
			return fragments.Any((string f) => name2.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0);
		}

		internal static void ShowTip(string header, string body, bool isWarning = false)
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.DisplayTip(header, body, isWarning, false, "LC_Tip1");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[HUD] DisplayTip error: " + ex.Message));
			}
		}
	}
	public static class EventPool
	{
		private static readonly GameEventData[] _meteor = new GameEventData[10]
		{
			E("meteor_fog_invasion", "⚠ Метеоритный туман", "Метеоритный дождь начался! Густой туман скрывает угрозы. Монстры рыщут снаружи в 3× больше, но добыча стоит на 30% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.SetWeather((LevelWeatherType)3);
				EventHelpers.MultiplyOutsidePower(3f);
				EventHelpers.MultiplyDaytimePower(2f);
				EventHelpers.SetScrap(1.3f, -1f);
			}, -1, (LevelWeatherType)3),
			E("meteor_golden", "☄ Золотой метеорит", "Метеорит принёс сокровище! На карте ровно ОДИН предмет высокой ценности (Золотой слиток или Касса стоимостью 1500-2200 кредитов). Монстров нет.", GameEventRarity.Insane, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.ZeroAllPower();
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.minScrap = 1;
					EventHelpers.Level.maxScrap = 1;
					EventHelpers.FilterScrapToSpecificItems(1500, 2200, "gold", "register");
				}
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.scrapAmountMultiplier = 1f;
					EventHelpers.RM.scrapValueMultiplier = 1f;
				}
			}, 14),
			E("meteor_celestial_shower", "☄ Небесный душ", "Метеориты рассыпали добычу по всей карте! В 3× больше предметов, но каждый стоит дешевле.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.SetScrap(0.5f, 3f);
			}),
			E("meteor_rain_of_riches", "☄ Золотой дождь", "Метеориты несут богатства! Стоимость добычи на 25% выше, но её меньше. Монстры чуть активнее.", GameEventRarity.Good, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.SetAllPower(1.05f, 1.05f, 1.05f);
				EventHelpers.SetScrap(1.25f, 0.5f);
			}),
			E("meteor_swarm", "⚠ Метеоритный рой", "Рой метеоритов сопровождается ордой снаружи! В 4× больше внешних монстров. Добыча на 8% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.MultiplyOutsidePower(4f);
				EventHelpers.MultiplyDaytimePower(2f);
				EventHelpers.SetScrap(1.08f, -1f);
			}),
			E("meteor_lockdown", "⚠ Метеоритная блокада", "Метеоритный дождь загнал монстров внутрь! Снаружи безопасно, но в здании тройная угроза. Добыча на 15% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.TriggerMeteorShower();
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 0;
					EventHelpers.Level.maxDaytimeEnemyPowerCount = 0;
				}
				EventHelpers.MultiplyInsidePower(3f);
				EventHelpers.SetScrap(1.15f, -1f);
			}),
			E("meteor_calm", "☄ Мирный метеорит", "Метеоритный дождь прошёл без особых угроз. Небольшой бонус к добыче.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.SetScrap(1.07f, 1.04f);
			}),
			E("meteor_impact_zone", "‼ Зона удара", "ЭКСТРЕННОЕ ПРЕДУПРЕЖДЕНИЕ! Сильнейший метеоритный шторм! Лимит монстров на улице и в здании увеличен в 3 раза. Добыча на 25% дороже, её немного больше.", GameEventRarity.Insane, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.SetWeather((LevelWeatherType)2);
				EventHelpers.SetAllPower(3f, 3f, 2f);
				EventHelpers.SetScrap(1.25f, 1.2f);
			}, 12, (LevelWeatherType)2),
			E("meteor_cosmic_treasure", "☄ Космическое сокровище", "Метеоритный дождь принёс дары, а монстры в панике разбежались! Нет никого на улице и внутри. Добыча на 20% дороже.", GameEventRarity.Good, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.ZeroAllPower();
				EventHelpers.SetScrap(1.2f, 0.7f);
			}),
			E("meteor_star_gift", "☄ Дар звёзд", "Метеориты принесли дары! Добыча немного дороже, внешняя угроза снижена вдвое.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.TriggerMeteorShower();
				EventHelpers.MultiplyOutsidePower(0.5f);
				EventHelpers.MultiplyDaytimePower(0.5f);
				EventHelpers.SetScrap(1.1f, -1f);
			})
		};

		private static readonly GameEventData[] _monsters = new GameEventData[10]
		{
			E("monster_bracken_party", "⚠ Вечеринка Лозы", "Внутри только Лозы и их в 2.5× больше обычного! Тихо, тихо... Не смотри им в глаза. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Bracken", "Flowerman");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyInsidePower(2.5f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("monster_spider_nest", "⚠ Паучье гнездо", "Всё здание в паутине! Только пауки внутри в двойном количестве. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Spider", "Bunker");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyInsidePower(2f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("monster_hoarder_heaven", "⚠ Рай скупердяев", "Жуки-накопители захватили ВСЮ добычу в 2.5× количестве! Победи их, чтобы вернуть своё. Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Hoarding", "Hoarder", "Loot Bug");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyInsidePower(2.5f);
				EventHelpers.SetScrap(1.3f, -1f);
			}),
			E("monster_bird_migration", "✓ Перелёт птиц", "Снаружи только безвредные птицы! Собирай добычу спокойно. Стоимость на 10% выше, нет дневных угроз.", GameEventRarity.Good, delegate
			{
				EventHelpers.FilterOutsideEnemies("Manticoil", "Docile Locust");
				EventHelpers.FilterDaytimeEnemies("Manticoil", "Docile Locust", "Circuit Bee");
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 2;
					EventHelpers.Level.maxDaytimeEnemyPowerCount = 2;
				}
				EventHelpers.MultiplyInsidePower(0.7f);
				EventHelpers.SetScrap(1.1f, -1f);
			}),
			E("monster_giant_walk", "‼ Прогулка гигантов", "Лесные Великаны заполонили поверхность в 3.5× количестве! Внутри здания монстров в 3 раза меньше. Добыча на 30% дороже, если выживешь.", GameEventRarity.Insane, delegate
			{
				EventHelpers.FilterOutsideEnemies("Forest Keeper", "Giant");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyOutsidePower(3.5f);
				EventHelpers.MultiplyInsidePower(0.33f);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxDaytimeEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.3f, -1f);
			}, 12),
			E("monster_nutcracker_drill", "⚠ Учения щелкунчиков", "Военные щелкунчики контролируют всё здание! Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Nutcracker");
				EventHelpers.BoostSpawnCurve(1.5f);
				SelectableLevel level3 = EventHelpers.Level;
				if ((Object)(object)level3 != (Object)null)
				{
					level3.maxEnemyPowerCount = Math.Min(level3.maxEnemyPowerCount, 8);
				}
				EventHelpers.SetScrap(1.3f, -1f);
			}),
			E("monster_snare_flea_farm", "⚠ Ферма членистоногих", "Прыгающие блохи везде и в тройном количестве! Добыча на 7% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Snare Flea", "Centipede");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyInsidePower(3f);
				EventHelpers.SetScrap(1.07f, -1f);
			}),
			E("monster_thumper_rally", "⚠ Ралли стукачей", "Стукачи носятся по коридорам в 3× количестве! Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideEnemies("Crawler", "Thumper");
				EventHelpers.BoostSpawnCurve();
				EventHelpers.MultiplyInsidePower(3f);
				EventHelpers.SetScrap(1.25f, -1f);
			}),
			E("monster_outside_only", "⚠ Снаружи все", "Все монстры вышли на улицу в 4× количестве! Внутри безопасно, снаружи — орда. Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate
			{
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxEnemyPowerCount = 0;
				}
				EventHelpers.MultiplyOutsidePower(4f);
				EventHelpers.MultiplyDaytimePower(3f);
				EventHelpers.SetScrap(1.25f, -1f);
			}),
			E("monster_peaceful_planet", "✓ Мирная планета", "Сегодня нет монстров! Наслаждайся спокойным сбором добычи. Добыча на 4% дороже.", GameEventRarity.Good, delegate
			{
				EventHelpers.ZeroAllPower();
				EventHelpers.SetScrap(1.04f, -1f);
			})
		};

		private static readonly GameEventData[] _oneType = new GameEventData[8]
		{
			E("one_precious_item", "‼ Единственный артефакт", "На карте ОДИН предмет высокой ценности (1200-2000 кредитов). Найди его — квота закрыта. Монстров нет.", GameEventRarity.Insane, delegate
			{
				EventHelpers.ZeroAllPower();
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.minScrap = 1;
					EventHelpers.Level.maxScrap = 1;
					EventHelpers.FilterScrapToSingleRandomItem(1200, 2000);
				}
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.scrapAmountMultiplier = 1f;
					EventHelpers.RM.scrapValueMultiplier = 1f;
				}
			}, 14),
			E("one_type_inside", "≡ Монотонный бестиарий", "Внутри только один вид монстров (с повышенным шансом средних и опасных)! Можно адаптироваться. Добыча на 5% дороже.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.FilterInsideToWeightedRandomOne(new Random(Plugin.RoundSeed + 7));
				EventHelpers.BoostSpawnCurve();
				EventHelpers.SetScrap(1.05f, -1f);
			}),
			E("one_type_outside", "≡ Один снаружи", "Снаружи только один вид монстров (с повышенным шансом средних и опасных). Предсказуемая угроза. Добыча на 5% дороже.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.FilterOutsideToWeightedRandomOne(new Random(Plugin.RoundSeed + 13));
				EventHelpers.BoostSpawnCurve();
				EventHelpers.SetScrap(1.05f, -1f);
			}),
			E("one_uniform_pricing", "≡ Единая цена", "Все предметы стоят одинаково (от 67 до 228 кредитов)! Без сюрпризов — гарантированный доход. В 1.3× больше предметов.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetAllScrapValuesRange(67, 228);
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.scrapValueMultiplier = 1f;
					RoundManager? rM = EventHelpers.RM;
					rM.scrapAmountMultiplier *= 1.3f;
				}
			}),
			E("one_gold_rush", "✓ Золотая лихорадка", "Только ценные предметы сегодня! Стоимость увеличена на 25%, но предметов мало.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetScrap(1.25f, 0.5f);
			}),
			E("one_junk_avalanche", "≡ Горы хлама", "Дешёвого хлама завались! В 3× больше, но стоит дешевле.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetScrap(0.5f, 3f);
			}),
			E("one_artifact_discovery", "✓ Открытие артефактов", "Сегодня только один тип предметов, зато каждый на 25% дороже обычного! Предметов вдвое меньше.", GameEventRarity.Good, delegate
			{
				EventHelpers.FilterScrapToOneRandomType(new Random(Plugin.RoundSeed + 55));
				EventHelpers.SetScrap(1.25f, 0.5f);
			}),
			E("one_same_kind_bonanza", "≡ Монотонность", "Сегодня только один вид предметов — зато их на 40% больше.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.FilterScrapToOneRandomType(new Random(Plugin.RoundSeed + 77));
				EventHelpers.SetScrap(1f, 1.4f);
			})
		};

		private static readonly GameEventData[] _weather = new GameEventData[8]
		{
			E("weather_fog_raid", "⚠ Туманный рейд", "Густой туман скрыл орду снаружи! Видимость нулевая, монстров в 3×. Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)3);
				EventHelpers.MultiplyOutsidePower(3f);
				EventHelpers.SetScrap(1.3f, -1f);
			}, -1, (LevelWeatherType)3),
			E("weather_thunder_bonus", "⚡ Гроза богатств", "Штормовая погода, но добыча на 12% дороже. Берегись молний!", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)2);
				EventHelpers.SetScrap(1.12f, -1f);
			}, -1, (LevelWeatherType)2),
			E("weather_flood_gold", "≡ Золотой потоп", "Наводнение! Трудно ходить, но добыча на 10% дороже.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)4);
				EventHelpers.SetScrap(1.1f, -1f);
			}, -1, (LevelWeatherType)4),
			E("weather_eclipse_bonus", "⚠ Затмение", "Полное затмение! Монстры снаружи ночью вдвое активнее, зато днём — необычно тихо. Добыча на 40% дороже — если выживешь.", GameEventRarity.Insane, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)5);
				EventHelpers.SetAllPower(1.2f, 2f, 0.5f);
				EventHelpers.SetScrap(1.4f, -1f);
			}, 10, (LevelWeatherType)5),
			E("weather_clear_harvest", "✓ Ясная жатва", "Отличная погода! Добычи на 8% больше и на 5% дороже.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)(-1));
				EventHelpers.SetScrap(1.05f, 1.08f);
			}, -1, (LevelWeatherType)(-1)),
			E("weather_rainy_surplus", "✓ Дождливый урожай", "Дождь принёс удачу! В 1.12× больше предметов.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)1);
				EventHelpers.SetScrap(1.05f, 1.12f);
			}, -1, (LevelWeatherType)1),
			E("weather_dust_treasure", "≡ Пыльная буря", "Пыльные облака скрывают ценности! Добыча на 10% дороже.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)0);
				EventHelpers.SetScrap(1.1f, -1f);
			}, -1, (LevelWeatherType)0),
			E("weather_calm_storm", "✓ Спокойная гроза", "Буря отпугнула монстров с улицы! Снаружи никого, добыча на 25% дороже.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetWeather((LevelWeatherType)2);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 0;
					EventHelpers.Level.maxDaytimeEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.25f, -1f);
			}, -1, (LevelWeatherType)2)
		};

		private static readonly GameEventData[] _scrap = new GameEventData[12]
		{
			E("scrap_rich_day", "✓ Богатый день", "Все предметы стоят на 25% дороже, но их немного меньше.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetScrap(1.25f, 0.7f);
			}),
			E("scrap_poor_day", "≡ Скудный день", "Дешёвые предметы (полцены), но их вдвое больше. Объём берёт своё.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetScrap(0.5f, 2f);
			}),
			E("scrap_jackpot", "‼ Джекпот", "На 25% дороже, но предметов почти нет — лишь 20% от нормы. Ищи каждый угол.", GameEventRarity.Insane, delegate
			{
				EventHelpers.SetScrap(1.25f, 0.2f);
			}, 10),
			E("scrap_warehouse", "≡ Планета-склад", "Добычи завались! В 3× больше, но всё стоит дешевле.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetScrap(0.5f, 3f);
			}),
			E("scrap_balanced_harvest", "≡ Сбалансированный урожай", "Обычный день с небольшим плюсом: добыча чуть дороже и чуть больше.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetScrap(1.08f, 1.08f);
			}),
			E("scrap_diamond_field", "‼ Алмазное поле", "Несколько предметов колоссальной цены (~800-1100 кредитов). Найди их.", GameEventRarity.Insane, delegate
			{
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.scrapAmountMultiplier = 0.08f;
					EventHelpers.RM.scrapValueMultiplier = 10f;
				}
			}, 12),
			E("scrap_feast", "≡ Пир добычи", "Море добычи! В 1.25× больше, но каждый предмет стоит дешевле.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.SetScrap(0.6f, 1.25f);
			}),
			E("scrap_cursed", "⚠ Проклятая добыча", "Всё проклято! Предметы стоят гроши (50% от обычной цены). Монстров нет — но и смысла мало.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.ZeroAllPower();
				EventHelpers.SetScrap(0.5f, 1.2f);
			}),
			E("scrap_rush_hour", "‼ Час пик", "ВСЁ максимально! Монстры везде в 3× активнее. Добыча на 35% дороже и в 1.25× больше.", GameEventRarity.Insane, delegate
			{
				EventHelpers.SetScrap(1.35f, 1.25f);
				EventHelpers.SetAllPower(3f, 3f, 2f);
			}, 10),
			E("scrap_empty_moon", "⚠ Пустая луна", "Нет монстров, но и нет добычи. Бесполезный визит.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.ZeroAllPower();
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.scrapAmountMultiplier = 0.03f;
				}
			}, 14),
			E("scrap_double_or_nothing", "‼ Удвоение или ноль", "50 на 50: либо добыча на 30% дороже и в 1.25× больше, либо 50% от обычной цены. Фортуна решает.", GameEventRarity.Insane, delegate
			{
				if (new Random(Plugin.RoundSeed + 99).Next(2) == 0)
				{
					EventHelpers.SetScrap(1.3f, 1.25f);
					Plugin.Log.LogInfo((object)"[Event] Удвоение — вам повезло!");
				}
				else
				{
					EventHelpers.SetScrap(0.5f, -1f);
					Plugin.Log.LogInfo((object)"[Event] Ноль — вам не повезло!");
				}
			}, 10),
			E("scrap_high_stakes", "⚠ Высокие ставки", "Тройные монстры везде, добыча на 25% дороже и в 1.25× больше. Высокий риск — высокая награда.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.SetAllPower(3f, 3f, 2.5f);
				EventHelpers.SetScrap(1.25f, 1.25f);
			})
		};

		private static readonly GameEventData[] _special = new GameEventData[17]
		{
			E("special_easy_day", "✓ Лёгкий день", "Монстры слабее сегодня! В 3× меньше мощи. Нормальная добыча.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetAllPower(0.35f, 0.35f, 0.35f);
			}),
			E("special_hard_day", "⚠ Тяжёлый день", "Монстры втройне опасны везде! Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.SetAllPower(3f, 3f, 2.5f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("special_extreme_day", "‼ Экстремальный день", "ОПАСНОСТЬ! В 4× больше монстров везде! Добыча на 30% дороже. Удачи.", GameEventRarity.Insane, delegate
			{
				EventHelpers.SetAllPower(4f, 4f, 3f);
				EventHelpers.SetScrap(1.3f, -1f);
			}, 10),
			E("special_swarm_outside", "⚠ Рой снаружи", "Снаружи орда в 4×! Внутри пусто. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxEnemyPowerCount = 0;
				}
				EventHelpers.MultiplyOutsidePower(4f);
				EventHelpers.MultiplyDaytimePower(2f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("special_fortress_inside", "⚠ Крепость внутри", "Внутри настоящая крепость монстров в 4×! Снаружи пусто. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 0;
					EventHelpers.Level.maxDaytimeEnemyPowerCount = 0;
				}
				EventHelpers.MultiplyInsidePower(4f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("special_daytime_rush", "≡ Дневной час пик", "Дневные монстры в 3× активнее! Ночью снаружи спокойно. Добыча на 12% дороже.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.MultiplyDaytimePower(3f);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.12f, -1f);
			}),
			E("special_ghost_town", "≡ Город призраков", "Тихо как на кладбище. Нет монстров, но добыча на 30% дешевле, зато её на 40% больше.", GameEventRarity.Neutral, delegate
			{
				EventHelpers.ZeroAllPower();
				EventHelpers.SetScrap(0.7f, 1.4f);
			}),
			E("special_lucky_break", "✓ Счастливый перерыв", "Сегодня вам повезло! Половина монстров, добыча на 12% дороже.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetAllPower(0.5f, 0.5f, 0.5f);
				EventHelpers.SetScrap(1.12f, -1f);
			}),
			E("special_company_audit", "✓ Аудит Компании", "Компания проводит аудит на этой луне! Стоимость добычи увеличена на 12%.", GameEventRarity.Good, delegate
			{
				EventHelpers.SetScrap(1.12f, -1f);
			}),
			E("special_minefield", "⚠ Минное поле", "Внутри здания зафиксировано огромное количество активных мин! Стоимость добычи увеличена на 12%.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.MultiplyMapObjects(6f, 1f);
				EventHelpers.SetScrap(1.12f, -1f);
			}),
			E("special_turret_hell", "⚠ Турельный ад", "Охранные турели заполонили коридоры! Стоимость добычи увеличена на 20%.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.MultiplyMapObjects(1f, 6f);
				EventHelpers.SetScrap(1.2f, -1f);
			}),
			E("special_lights_out", "‼ Отключение питания", "Абсолютное отключение питания в комплексе! Внутри полная темнота, а Coil-head активнее.", GameEventRarity.Insane, delegate
			{
				if ((Object)(object)EventHelpers.RM != (Object)null)
				{
					EventHelpers.RM.powerOffPermanently = true;
				}
				EventHelpers.FilterInsideEnemies("Coil", "Spring");
				EventHelpers.MultiplyInsidePower(3f);
			}, 12),
			E("monster_coilhead_patrol", "⚠ Стальной дозор", "Катушки вырвались на поверхность! Они патрулируют территорию снаружи. Внутри — спокойно. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideToOutside("Coil", "Spring");
				EventHelpers.BoostSpawnCurve(1.5f);
				EventHelpers.MultiplyOutsidePower(2.5f);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.2f, -1f);
			}, 10),
			E("monster_spider_outdoors", "⚠ Внешняя паутина", "Пауки оплели всю территорию снаружи! Внутри комплекса безопасно. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterInsideToOutside("Spider", "Bunker");
				EventHelpers.BoostSpawnCurve(1.5f);
				EventHelpers.MultiplyOutsidePower(2f);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.2f, -1f);
			}, 10),
			E("monster_baboon_siege", "⚠ Осада бабуинов", "Стая бабуинов ворвалась в комплекс! Они охотятся стаями в коридорах. Снаружи пусто. Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate
			{
				EventHelpers.FilterOutsideToInside("Baboon");
				EventHelpers.BoostSpawnCurve(1.5f);
				EventHelpers.MultiplyInsidePower(2.5f);
				if ((Object)(object)EventHelpers.Level != (Object)null)
				{
					EventHelpers.Level.maxOutsideEnemyPowerCount = 0;
				}
				EventHelpers.SetScrap(1.25f, -1f);
			}, 12),
			E("monster_mech_incursion", "‼ Вторжение меха", "Механический страж взломал комплекс! Ракеты и огнемёт в коридорах. Добыча на 40% дороже — если выживешь.", GameEventRarity.Insane, delegate
			{
				EventHelpers.FilterOutsideToInside("Mech", "Old");
				EventHelpers.BoostSpawnCurve(1.5f);
				SelectableLevel level2 = EventHelpers.Level;
				if ((Object)(object)level2 != (Object)null)
				{
					level2.maxEnemyPowerCount = Math.Min(level2.maxEnemyPowerCount, 8);
				}
				EventHelpers.SetScrap(1.4f, -1f);
			}, 14),
			E("monster_giant_breach", "‼ Гигант в здании", "Лесной Гигант ломится внутрь! Комплекс содрогается от его шагов. Он хватает всё что движется. Добыча на 40% дороже.", GameEventRarity.Insane, delegate
			{
				EventHelpers.FilterOutsideToInside("Giant", "Forest", "Keeper");
				EventHelpers.BoostSpawnCurve(1.5f);
				SelectableLevel level = EventHelpers.Level;
				if ((Object)(object)level != (Object)null)
				{
					level.maxEnemyPowerCount = Math.Min(level.maxEnemyPowerCount, 6);
				}
				EventHelpers.SetScrap(1.4f, -1f);
			}, 14)
		};

		private static IReadOnlyList<GameEventData>? _allEvents;

		private static IReadOnlyList<GameEventData>? _lootEvents;

		private static IReadOnlyList<GameEventData>? _weatherEvents;

		private static IReadOnlyList<GameEventData>? _monsterEvents;

		public static IReadOnlyList<GameEventData> AllEvents
		{
			get
			{
				if (_allEvents == null)
				{
					List<GameEventData> list = new List<GameEventData>();
					list.AddRange(_meteor);
					list.AddRange(_monsters);
					list.AddRange(_oneType);
					list.AddRange(_weather);
					list.AddRange(_scrap);
					list.AddRange(_special);
					_allEvents = list.AsReadOnly();
				}
				return _allEvents;
			}
		}

		public static IReadOnlyList<GameEventData> LootEvents
		{
			get
			{
				if (_lootEvents == null)
				{
					List<GameEventData> list = new List<GameEventData>();
					list.AddRange(_scrap);
					list.Add(FindEvent("one_precious_item"));
					list.Add(FindEvent("one_uniform_pricing"));
					list.Add(FindEvent("one_gold_rush"));
					list.Add(FindEvent("one_junk_avalanche"));
					list.Add(FindEvent("one_artifact_discovery"));
					list.Add(FindEvent("one_same_kind_bonanza"));
					list.Add(FindEvent("special_company_audit"));
					list.Add(FindEvent("special_ghost_town"));
					_lootEvents = list.AsReadOnly();
				}
				return _lootEvents;
			}
		}

		public static IReadOnlyList<GameEventData> WeatherEvents
		{
			get
			{
				if (_weatherEvents == null)
				{
					List<GameEventData> list = new List<GameEventData>();
					list.AddRange(_weather);
					list.AddRange(_meteor);
					_weatherEvents = list.AsReadOnly();
				}
				return _weatherEvents;
			}
		}

		public static IReadOnlyList<GameEventData> MonsterEvents
		{
			get
			{
				if (_monsterEvents == null)
				{
					List<GameEventData> list = new List<GameEventData>();
					list.AddRange(_monsters);
					list.Add(FindEvent("one_type_inside"));
					list.Add(FindEvent("one_type_outside"));
					list.Add(FindEvent("scrap_high_stakes"));
					list.Add(FindEvent("scrap_rush_hour"));
					list.Add(FindEvent("special_easy_day"));
					list.Add(FindEvent("special_hard_day"));
					list.Add(FindEvent("special_extreme_day"));
					list.Add(FindEvent("special_swarm_outside"));
					list.Add(FindEvent("special_fortress_inside"));
					list.Add(FindEvent("special_daytime_rush"));
					list.Add(FindEvent("special_lucky_break"));
					list.Add(FindEvent("special_minefield"));
					list.Add(FindEvent("special_turret_hell"));
					list.Add(FindEvent("special_lights_out"));
					list.Add(FindEvent("monster_coilhead_patrol"));
					list.Add(FindEvent("monster_spider_outdoors"));
					list.Add(FindEvent("monster_baboon_siege"));
					list.Add(FindEvent("monster_mech_incursion"));
					list.Add(FindEvent("monster_giant_breach"));
					_monsterEvents = list.AsReadOnly();
				}
				return _monsterEvents;
			}
		}

		private static GameEventData E(string id, string name, string desc, GameEventRarity rarity, Action effect, int cd = -1, LevelWeatherType? weather = null)
		{
			return new GameEventData(id, name, desc, rarity, effect, cd, weather);
		}

		private static GameEventData FindEvent(string id)
		{
			foreach (GameEventData allEvent in AllEvents)
			{
				if (allEvent.Id == id)
				{
					return allEvent;
				}
			}
			throw new Exception("[EventPool] Event " + id + " not found in AllEvents!");
		}
	}
	public class EventPopup : MonoBehaviour
	{
		public float life = 9f;

		private float _total;

		private TextMeshProUGUI _tmp;

		private void Awake()
		{
			_total = life;
			_tmp = ((Component)this).GetComponent<TextMeshProUGUI>();
		}

		private void Update()
		{
			//IL_0033: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			life -= Time.deltaTime;
			if ((Object)(object)_tmp != (Object)null && life < 1.5f)
			{
				Color color = ((Graphic)_tmp).color;
				color.a = Mathf.Clamp01(life / 1.5f);
				((Graphic)_tmp).color = color;
			}
			if (life <= 0f)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}
	}
	public class GhostlyWorldController : MonoBehaviour
	{
		private bool _hasLanded;

		private readonly Dictionary<int, float> _playerTimers = new Dictionary<int, float>();

		private static readonly BindingFlags _bfPub = BindingFlags.Instance | BindingFlags.Public;

		private static readonly string[] _dangerKeywords = new string[13]
		{
			"kill", "death", "die", "explod", "damage", "hurt", "attack", "grab", "finish", "sink",
			"fall", "ragdoll", "drown"
		};

		private static readonly FieldInfo DoorOpenedField = typeof(DoorLock).GetField("isDoorOpened", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo TerminalDoorOpenField = typeof(TerminalAccessibleObject).GetField("isDoorOpen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly Dictionary<string, List<AudioClip>> _clipCache = new Dictionary<string, List<AudioClip>>();

		private static readonly BindingFlags _bfAll = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private void Start()
		{
			Plugin.Log.LogInfo((object)"[GhostlyWorld] Controller created. Waiting for ship to land...");
		}

		private void Update()
		{
			if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer || (Object)(object)StartOfRound.Instance == (Object)null)
			{
				return;
			}
			if (!_hasLanded)
			{
				if (!StartOfRound.Instance.inShipPhase)
				{
					_hasLanded = true;
					Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship landed. Paranoia active — per-player distance-based timers started.");
				}
				return;
			}
			if (StartOfRound.Instance.inShipPhase)
			{
				Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship returned to orbit. Destroying GhostlyWorldController.");
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			if (allPlayerScripts == null)
			{
				return;
			}
			List<PlayerControllerB> active = allPlayerScripts.Where((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled && !p.isPlayerDead).ToList();
			for (int i = 0; i < allPlayerScripts.Length; i++)
			{
				PlayerControllerB val = allPlayerScripts[i];
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead)
				{
					if (!_playerTimers.ContainsKey(i))
					{
						_playerTimers[i] = GetInterval(val, active);
					}
					_playerTimers[i] -= Time.deltaTime;
					if (_playerTimers[i] <= 0f)
					{
						float interval = GetInterval(val, active);
						_playerTimers[i] = interval;
						Plugin.Log.LogInfo((object)$"[GhostlyWorld] Player {i} paranoia triggered. Next in {interval:F1}s (dist to team: {GetNearestTeammateDist(val, active):F0}m)");
						((MonoBehaviour)this).StartCoroutine(GhostlyEventForPlayer(val));
					}
				}
			}
		}

		private static float GetInterval(PlayerControllerB player, List<PlayerControllerB> active)
		{
			int num = Mathf.FloorToInt(GetNearestTeammateDist(player, active) / 20f);
			float num2 = Mathf.Min(1f + (float)num * 0.5f, 4f);
			return Random.Range(30f, 60f) / num2;
		}

		private static float GetNearestTeammateDist(PlayerControllerB player, List<PlayerControllerB> active)
		{
			//IL_0026: 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)
			float num = float.MaxValue;
			foreach (PlayerControllerB item in active)
			{
				if (!((Object)(object)item == (Object)(object)player))
				{
					float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)item).transform.position);
					if (num2 < num)
					{
						num = num2;
					}
				}
			}
			if (num != float.MaxValue)
			{
				return num;
			}
			return 999f;
		}

		private IEnumerator GhostlyEventForPlayer(PlayerControllerB player)
		{
			if (!((Object)(object)StartOfRound.Instance == (Object)null) && !TryTriggerViaSpawnedEnemy(player) && !EventConflictSystem.AnyHasTag(RoundStartPatch.CurrentRoundEvents ?? new List<GameEventData>(), "monsters_zero"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(GhostlyFallback(player));
			}
		}

		private bool TryTriggerViaSpawnedEnemy(PlayerControllerB player)
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			List<EnemyAI> list = RoundManager.Instance?.SpawnedEnemies;
			if (list == null || list.Count == 0)
			{
				return false;
			}
			bool isInside = player.isInsideFactory;
			List<EnemyAI> list2 = (from e in list
				where (Object)(object)e != (Object)null && !e.isEnemyDead && e.isOutside == !isInside
				select e into _
				orderby Random.value
				select _).ToList();
			if (list2.Count == 0)
			{
				list2 = (from e in list
					where (Object)(object)e != (Object)null && !e.isEnemyDead
					select e into _
					orderby Random.value
					select _).ToList();
			}
			if (list2.Count == 0)
			{
				return false;
			}
			EnemyAI val = list2[0];
			Vector3 position = ((Component)val).transform.position;
			bool flag = TryInvokeAudioClientRpc(val);
			OpenNearbyDoors(position, 15f, Random.Range(1, 2));
			SendPlaySoundMessage(position, val.enemyType?.enemyName ?? "", isInside, isHuntSFX: false);
			Plugin.Log.LogInfo((object)$"[GhostlyWorld] Real-enemy paranoia: {val.enemyType?.enemyName} at {position}, soundRpc={flag}");
			return true;
		}

		private static bool TryInvokeAudioClientRpc(EnemyAI enemy)
		{
			try
			{
				Type type = ((object)enemy).GetType();
				List<MethodInfo> source = (from m in type.GetMethods(_bfPub)
					where m.Name.EndsWith("ClientRpc") && m.GetParameters().Length == 0
					select m).ToList();
				List<MethodInfo> list = source.Where(delegate(MethodInfo m)
				{
					string i = m.Name.ToLower();
					return (i.Contains("sound") || i.Contains("sfx") || i.Contains("noise") || i.Contains("idle") || i.Contains("stomp") || i.Contains("footstep") || i.Contains("growl") || i.Contains("moan") || i.Contains("roar") || i.Contains("chitter") || i.Contains("chuckle") || i.Contains("hiss") || i.Contains("breathe") || i.Contains("grunt") || i.Contains("step")) && !_dangerKeywords.Any((string bad) => i.Contains(bad));
				}).ToList();
				if (list.Count == 0)
				{
					list = source.Where((MethodInfo m) => !_dangerKeywords.Any((string bad) => m.Name.ToLower().Contains(bad))).ToList();
				}
				if (list.Count == 0)
				{
					return false;
				}
				MethodInfo methodInfo = list[Random.Range(0, list.Count)];
				methodInfo.Invoke(enemy, null);
				Plugin.Log.LogInfo((object)("[GhostlyWorld] Invoked " + type.Name + "." + methodInfo.Name + " → all clients"));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[GhostlyWorld] ClientRpc invoke failed: " + ex.Message));
				return false;
			}
		}

		private IEnumerator GhostlyFallback(PlayerControllerB player)
		{
			bool isInside = player.isInsideFactory;
			SelectableLevel val = StartOfRound.Instance?.currentLevel;
			if ((Object)(object)val == (Object)null)
			{
				yield break;
			}
			List<SpawnableEnemyWithRarity> list = (isInside ? val.Enemies : val.OutsideEnemies);
			if (list == null || list.Count == 0)
			{
				yield break;
			}
			List<EnemyType> list2 = (from e in list
				where (Object)(object)e.enemyType != (Object)null && !e.enemyType.enemyName.ToLower().Contains("manticoil") && !e.enemyType.enemyName.ToLower().Contains("locust")
				select e.enemyType).ToList();
			if (list2.Count != 0)
			{
				EnemyType chosen = list2[Random.Range(0, list2.Count)];
				Vector3 val2 = Random.onUnitSphere * Random.Range(8f, 18f);
				val2.y = 0f;
				Vector3 playPos = ((Component)player).transform.position + val2;
				NavMeshHit val3 = default(NavMeshHit);
				if (NavMesh.SamplePosition(playPos, ref val3, 15f, -1))
				{
					playPos = ((NavMeshHit)(ref val3)).position;
				}
				SendPlaySoundMessage(playPos, chosen.enemyName, isInside, isHuntSFX: false);
				yield return (object)new WaitForSeconds(Random.Range(1.5f, 2.5f));
				SendPlaySoundMessage(playPos, chosen.enemyName, isInside, isHuntSFX: true);
				OpenNearbyDoors(playPos, 15f, Random.Range(1, 3));
			}
		}

		private void SendPlaySoundMessage(Vector3 position, string enemyName, bool isInside, bool isHuntSFX)
		{
			//IL_003b: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(100, (Allocator)2, -1);
			((FastBufferWriter)(ref val)).WriteValueSafe(ref position);
			((FastBufferWriter)(ref val)).WriteValueSafe(enemyName, false);
			((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref isInside, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref isHuntSFX, default(ForPrimitives));
			foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
			{
				singleton.CustomMessagingManager.SendNamedMessage("RandomEvents_PlayGhostlySound", connectedClientsId, val, (NetworkDelivery)3);
			}
		}

		private bool GetIsDoorOpened(DoorLock door)
		{
			if ((Object)(object)door != (Object)null && DoorOpenedField != null)
			{
				return (bool)DoorOpenedField.GetValue(door);
			}
			return false;
		}

		private bool GetIsTerminalDoorOpen(TerminalAccessibleObject bDoor)
		{
			if ((Object)(object)bDoor != (Object)null && TerminalDoorOpenField != null)
			{
				return (bool)TerminalDoorOpenField.GetValue(bDoor);
			}
			return false;
		}

		private void OpenNearbyDoors(Vector3 center, float radius, int count)
		{
			//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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(center, radius);
			int num = 0;
			List<DoorLock> list = new List<DoorLock>();
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					DoorLock componentInParent = ((Component)val).GetComponentInParent<DoorLock>();
					if ((Object)(object)componentInParent != (Object)null && !componentInParent.isLocked && !GetIsDoorOpened(componentInParent) && !list.Contains(componentInParent))
					{
						list.Add(componentInParent);
					}
				}
			}
			list = list.OrderBy((DoorLock d) => Vector3.Distance(((Component)d).transform.position, center)).ToList();
			foreach (DoorLock item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					if (num >= count)
					{
						break;
					}
					Plugin.Log.LogInfo((object)$"[GhostlyWorld] Opening normal door: {((Object)item).name} near {center}");
					item.OpenDoorAsEnemyServerRpc();
					num++;
				}
			}
			if (num >= count)
			{
				return;
			}
			List<TerminalAccessibleObject> list2 = new List<TerminalAccessibleObject>();
			array2 = array;
			foreach (Collider val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					TerminalAccessibleObject componentInParent2 = ((Component)val2).GetComponentInParent<TerminalAccessibleObject>();
					if ((Object)(object)componentInParent2 != (Object)null && componentInParent2.isBigDoor && !GetIsTerminalDoorOpen(componentInParent2) && !list2.Contains(componentInParent2))
					{
						list2.Add(componentInParent2);
					}
				}
			}
			list2 = list2.OrderBy((TerminalAccessibleObject b) => Vector3.Distance(((Component)b).transform.position, center)).ToList();
			foreach (TerminalAccessibleObject item2 in list2)
			{
				if (!((Object)(object)item2 == (Object)null))
				{
					if (num >= count)
					{
						break;
					}
					Plugin.Log.LogInfo((object)$"[GhostlyWorld] Opening big security door: {((Object)item2).name} near {center}");
					item2.SetDoorOpenServerRpc(true);
					num++;
				}
			}
		}

		public static void PlaySoundLocally(Vector3 playPos, string enemyName, bool isInside, bool isHuntSFX)
		{
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Expected O, but got Unknown
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			EnemyType val = FindEnemyTypeByName(enemyName);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)("[GhostlyWorld] Could not find EnemyType for name: " + enemyName));
				return;
			}
			List<AudioClip> list = GetEnemyAudioClips(val);
			if (list.Count == 0)
			{
				SelectableLevel val2 = StartOfRound.Instance?.currentLevel;
				List<SpawnableEnemyWithRarity> list2 = new List<SpawnableEnemyWithRarity>();
				if (val2?.Enemies != null)
				{
					list2.AddRange(val2.Enemies);
				}
				if (val2?.OutsideEnemies != null)
				{
					list2.AddRange(val2.OutsideEnemies);
				}
				Random rng = new Random();
				list2 = list2.OrderBy((SpawnableEnemyWithRarity _) => rng.Next()).ToList();
				foreach (SpawnableEnemyWithRarity item in list2)
				{
					if (!((Object)(object)item?.enemyType == (Object)null) && !((Object)(object)item.enemyType == (Object)(object)val))
					{
						List<AudioClip> enemyAudioClips = GetEnemyAudioClips(item.enemyType);
						if (enemyAudioClips.Count > 0)
						{
							list = enemyAudioClips;
							val = item.enemyType;
							Plugin.Log.LogInfo((object)("[GhostlyWorld] Clip fallback: using " + val.enemyName + " instead."));
							break;
						}
					}
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			AudioClip val3 = null;
			AudioClip val4 = null;
			foreach (AudioClip item2 in list)
			{
				if (!((Object)(object)item2 == (Object)null))
				{
					string text = ((Object)item2).name.ToLower();
					if (text.Contains("chase") || text.Contains("hunt") || text.Contains("scream") || text.Contains("growl") || text.Contains("lunge") || text.Contains("charge"))
					{
						val4 = item2;
					}
					else if (text.Contains("foot") || text.Contains("step") || text.Contains("walk") || text.Contains("run") || text.Contains("move") || text.Contains("crawl") || text.Contains("skitter"))
					{
						val3 = item2;
					}
				}
			}
			if ((Object)(object)val3 == (Object)null && list.Count > 0)
			{
				val3 = list[0];
			}
			if ((Object)(object)val4 == (Object)null && list.Count > 1)
			{
				val4 = list[1];
			}
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = val3;
			}
			AudioClip val5 = (isHuntSFX ? val4 : val3);
			if (!((Object)(object)val5 == (Object)null))
			{
				Plugin.Log.LogInfo((object)$"[GhostlyWorld] Playing clip '{((Object)val5).name}' for {enemyName} at {playPos}");
				GameObject val6 = new GameObject("TempGhostlyAudio");
				val6.transform.position = playPos;
				AudioSource val7 = val6.AddComponent<AudioSource>();
				val7.clip = val5;
				val7.spatialBlend = 1f;
				val7.minDistance = 6f;
				val7.maxDistance = 50f;
				val7.volume = 1f;
				val7.Play();
				GhostlyAudioTrigger.Attach(val6, val7, val5.length + 0.5f, playPos);
				if (!isHuntSFX)
				{
					GhostlyVisual.Spawn(playPos, val);