Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RandomEventsExtended v3.0.2
RandomEvents.dll
Decompiled 41 minutes ago
The result has been truncated due to the large size, download it to view full contents!
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);