using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 Unity.Netcode.Components; using UnityEngine; using UnityEngine.AI; using UnityEngine.Networking; 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.1.3.0")] [assembly: AssemblyInformationalVersion("3.1.3+47c5ecb9a7de125789ae59819230f89e554240af")] [assembly: AssemblyProduct("RandomEvents")] [assembly: AssemblyTitle("RandomEvents")] [assembly: AssemblyVersion("3.1.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace 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 list = new List { 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 list = new List(); 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 $"✓ ВЫПОЛНЕНО! +{RewardCredits} кред. зачислено."; } if (IsFailed) { return "✗ ПРОВАЛЕНО: " + Description + ""; } string arg = CurrentType switch { BountyType.KillEnemies => $" [{Progress}/{TargetAmount}]", BountyType.KillSpecific => $" [{Progress}/{TargetAmount}]", BountyType.CollectLoot => $" [{Progress}/{TargetAmount} кред.]", BountyType.SurviveAll => _deathOccurred ? " [ПОТЕРЯ!]" : " [ВСЕ ЖИВЫ]", _ => "", }; return $"{Description}{arg} → +{RewardCredits} кред."; } 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("✗ ЗАДАНИЕ ПРОВАЛЕНО: потеря члена экипажа."), -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(); 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($"✗ ЗАДАНИЕ: доставлено {num}/{TargetAmount} кред. — не выполнено."), -1); } } else if (CurrentType == BountyType.SurviveAll && !_deathOccurred) { CompleteAndAward(); } } private static void CompleteAndAward() { if (!IsCompleted) { IsCompleted = true; Terminal val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { val.groupCredits += RewardCredits; } HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer(ChatStyle.Soft("★ ЗАДАНИЕ ВЫПОЛНЕНО!\n" + $"{Description}\n+{RewardCredits} кредитов зачислено!"), -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("", RegexOptions.Compiled); internal static string Soft(string text) { if (!string.IsNullOrEmpty(text)) { return _colorTag.Replace(text, ""); } return text; } } public static class EventBalancer { private static readonly List _history = new List(64); private static int MaxHistorySize => ModConfig.MaxHistorySize?.Value ?? 14; private static Dictionary BaseWeights => new Dictionary { { 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 pool, Random random, ManualLogSource? log = null, List? exclude = null) { List 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 SelectMultipleEvents(IReadOnlyList pool, int count, Random random, ManualLogSource? log = null, List? exclude = null) { List list = new List(); for (int i = 0; i < count; i++) { List list2 = new List(); 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 GetHistory() { return _history; } private static List<(GameEventData ev, int weight)> BuildCandidatesForMultiple(IReadOnlyList pool, List exclude, ManualLogSource? log) { List<(GameEventData, int)> list = new List<(GameEventData, int)>(pool.Count); int num = _history.TakeLast(12).Count((string id) => pool.Any((GameEventData e) => e.Id == id && e.Rarity == GameEventRarity.Insane)); foreach (GameEventData ev in pool) { 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 Enemies; public List OutsideEnemies; public List DaytimeEnemies; public List spawnableScrap; public SpawnableMapObject[] spawnableMapObjects; public Dictionary insideRarities = new Dictionary(); public Dictionary outsideRarities = new Dictionary(); public Dictionary daytimeRarities = new Dictionary(); public Dictionary scrapRarities = new Dictionary(); public Dictionary mapObjectCurves = new Dictionary(); } private class ItemValueBackup { public int minValue; public int maxValue; } private static readonly Dictionary _levelSnapshots = new Dictionary(); private static readonly Dictionary _itemBackups = new Dictionary(); 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 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(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(); } if (level.OutsideEnemies != null) { value.OutsideEnemies = new List(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(); } if (level.DaytimeEnemies != null) { value.DaytimeEnemies = new List(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(); } if (level.spawnableScrap != null) { value.spawnableScrap = new List(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(); } 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(); } _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(value.Enemies); level.OutsideEnemies = new List(value.OutsideEnemies); level.DaytimeEnemies = new List(value.DaytimeEnemies); level.spawnableScrap = new List(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, float mineFloor = 0f, float turretFloor = 0f) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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++) { ((Keyframe)(ref keys[j])).value = Mathf.Max(((Keyframe)(ref keys[j])).value * mineMult, mineFloor); } 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++) { ((Keyframe)(ref keys2[k])).value = Mathf.Max(((Keyframe)(ref keys2[k])).value * turretMult, turretFloor); } val.numberToSpawn = new AnimationCurve(keys2); } } } } internal static void DisableAllTurrets() { Turret[] array = Object.FindObjectsOfType(); int num = 0; Turret[] array2 = array; foreach (Turret val in array2) { if (!((Object)(object)val == (Object)null)) { try { val.ToggleTurretEnabled(false); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[EventHelpers] DisableTurret failed: " + ex.Message)); } } } Plugin.Log.LogInfo((object)$"[EventHelpers] DisableAllTurrets: disabled {num}/{array.Length} turrets"); } 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 void MultiplyScrapValue(float mult) { if (!((Object)(object)RM == (Object)null)) { float num = EventBaseline.ScrapValue * (mult - 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 list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null) { list = Resources.FindObjectsOfTypeAll().ToList(); } List list2 = new List(); 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 list3 = new List(); 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 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 { 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 { 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 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 num = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight); int num2 = ((num > 0) ? rng.Next(0, num) : 0); int num3 = 0; SpawnableEnemyWithRarity val = list[0]; foreach (var item2 in list2) { num3 += item2.Item2; if (num2 < num3) { (val, _) = item2; break; } } if ((Object)(object)val?.enemyType != (Object)null) { MakeEnemyTypesSpawnEverywhere(new List { val.enemyType }); } } internal static void FilterOutsideToWeightedRandomOne(Random rng) { SelectableLevel level = Level; if (level?.OutsideEnemies == null || level.OutsideEnemies.Count == 0) { return; } List 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 num = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight); int num2 = ((num > 0) ? rng.Next(0, num) : 0); int num3 = 0; SpawnableEnemyWithRarity val = list[0]; foreach (var item2 in list2) { num3 += item2.Item2; if (num2 < num3) { (val, _) = item2; break; } } if ((Object)(object)val?.enemyType != (Object)null) { MakeEnemyTypesSpawnEverywhere(new List { 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 list, string componentName) { if (list == null) { return 0; } List list2 = new List(); 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 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 list, List 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(); } 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 list = new List(); 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(); foreach (SelectableLevel val in array) { List list2 = new List(); 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(); 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 list = level.OutsideEnemies; if (list == null) { list = (level.OutsideEnemies = new List()); } else { foreach (SpawnableEnemyWithRarity item in list) { if (item != null) { item.rarity = 0; } } } SpawnableEnemyWithRarity val2 = ((IEnumerable)list).FirstOrDefault((Func)((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(); 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 list = level.Enemies; if (list == null) { list = (level.Enemies = new List()); } else { foreach (SpawnableEnemyWithRarity item in list) { if (item != null) { item.rarity = 0; } } } SpawnableEnemyWithRarity val2 = ((IEnumerable)list).FirstOrDefault((Func)((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 list = new List(); 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) { SelectableLevel level = Level; if (level?.DaytimeEnemies == null || level.DaytimeEnemies.Count == 0 || !level.DaytimeEnemies.Any((SpawnableEnemyWithRarity e) => MatchesName(e.enemyType?.enemyName, nameFragments))) { return; } foreach (SpawnableEnemyWithRarity daytimeEnemy in level.DaytimeEnemies) { daytimeEnemy.rarity = (MatchesName(daytimeEnemy.enemyType?.enemyName, nameFragments) ? 100 : 0); } } internal static void FilterInsideToRandomOne(Random rng) { SelectableLevel level = Level; if (level?.Enemies == null || level.Enemies.Count == 0) { return; } List 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 { val.enemyType }); } } } internal static void FilterOutsideToRandomOne(Random rng) { SelectableLevel level = Level; if (level?.OutsideEnemies == null || level.OutsideEnemies.Count == 0) { return; } List 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 { val.enemyType }); } } } private static bool MatchesName(string? name, string[] fragments) { if (name == null) { return false; } return fragments.Any((string f) => name.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0); } internal static void RemoveZoneRestrictions() { try { GameObject[] array = (from g in Object.FindObjectsOfType() where (Object)(object)g != (Object)null && (((Object)g).name.StartsWith("OutsideAINode") || ((Object)g).name.StartsWith("AINode")) select g).ToArray(); int num = 0; EnemyAI[] array2 = Object.FindObjectsOfType(); foreach (EnemyAI val in array2) { if ((Object)(object)val == (Object)null || val.isEnemyDead) { continue; } try { NavMeshAgent component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } if (array.Length != 0) { val.allAINodes = array; } num++; } catch { } } Plugin.Log.LogInfo((object)$"[EventHelpers] RemoveZoneRestrictions: {num} enemies unlocked."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[EventHelpers] RemoveZoneRestrictions failed: " + ex.Message)); } } 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.VeryBad, 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 level = EventHelpers.Level; if ((Object)(object)level != (Object)null) { level.maxEnemyPowerCount = Math.Min(level.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", "‼ Алмазное поле", "Несколько предметов высокой цены (~300-800 кредитов). Найди их.", 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.VeryBad, 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, 8f); EventHelpers.SetScrap(1.12f, -1f); }), E("special_turret_hell", "⚠ Турельный ад", "Охранные турели заполонили коридоры! Стоимость добычи увеличена на 20%.", GameEventRarity.VeryBad, delegate { EventHelpers.MultiplyMapObjects(1f, 6f, 0f, 4f); 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); EventHelpers.DisableAllTurrets(); }, 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 level = EventHelpers.Level; if ((Object)(object)level != (Object)null) { level.maxEnemyPowerCount = Math.Min(level.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? _allEvents; private static IReadOnlyList? _lootEvents; private static IReadOnlyList? _weatherEvents; private static IReadOnlyList? _monsterEvents; public static IReadOnlyList AllEvents { get { if (_allEvents == null) { List list = new List(); 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 LootEvents { get { if (_lootEvents == null) { List list = new List(); 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 WeatherEvents { get { if (_weatherEvents == null) { List list = new List(); list.AddRange(_weather); list.AddRange(_meteor); _weatherEvents = list.AsReadOnly(); } return _weatherEvents; } } public static IReadOnlyList MonsterEvents { get { if (_monsterEvents == null) { List list = new List(); 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(); } 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 float _landedTime; private readonly Dictionary _playerTimers = new Dictionary(); private float _updateThrottle; private const float UpdateInterval = 0.5f; private readonly List _activePlayers = new List(); 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); public static readonly List CustomHorrorClips = new List(); private static readonly Dictionary> _clipCache = new Dictionary>(); private static readonly BindingFlags _bfAll = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private void Start() { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log.LogInfo((object)"[GhostlyWorld] Controller created. Waiting for ship to land..."); } private void Update() { try { UpdateInternal(); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"[GhostlyWorld] Update exception (non-fatal):\n{arg}"); } } private void UpdateInternal() { 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; _landedTime = Time.time; Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship landed. Paranoia active — per-player distance-based timers started."); } } else if (StartOfRound.Instance.inShipPhase && Time.time - _landedTime > 30f) { Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship returned to orbit. Destroying GhostlyWorldController."); Object.Destroy((Object)(object)((Component)this).gameObject); } else { if (StartOfRound.Instance.inShipPhase) { return; } _updateThrottle += Time.deltaTime; if (_updateThrottle < 0.5f) { return; } float updateThrottle = _updateThrottle; _updateThrottle = 0f; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return; } _activePlayers.Clear(); foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.isPlayerControlled && !val.isPlayerDead) { _activePlayers.Add(val); } } for (int j = 0; j < allPlayerScripts.Length; j++) { PlayerControllerB val2 = allPlayerScripts[j]; if (!((Object)(object)val2 == (Object)null) && val2.isPlayerControlled && !val2.isPlayerDead) { if (!_playerTimers.ContainsKey(j)) { _playerTimers[j] = GetInterval(val2, _activePlayers); } _playerTimers[j] -= updateThrottle; if (_playerTimers[j] <= 0f) { float interval = GetInterval(val2, _activePlayers); _playerTimers[j] = interval; Plugin.Log.LogInfo((object)$"[GhostlyWorld] Player {j} paranoia triggered. Next in {interval:F1}s (dist to team: {GetNearestTeammateDist(val2, _activePlayers):F0}m)"); ((MonoBehaviour)this).StartCoroutine(GhostlyEventForPlayer(val2)); } } } } } private static float GetInterval(PlayerControllerB player, List active) { int num = Mathf.FloorToInt(GetNearestTeammateDist(player, active) / 20f); float num2 = Mathf.Min(1f + (float)num * 0.3f, 2f); return Random.Range(90f, 150f) / num2; } private static float GetNearestTeammateDist(PlayerControllerB player, List 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(), "monsters_zero")) { yield return ((MonoBehaviour)this).StartCoroutine(GhostlyFallback(player)); } } private static bool TryInvokeAudioClientRpc(EnemyAI enemy) { try { Type type = ((object)enemy).GetType(); List source = (from m in type.GetMethods(_bfPub) where m.Name.EndsWith("ClientRpc") && m.GetParameters().Length == 0 select m).ToList(); List list = source.Where(delegate(MethodInfo m) { string n = m.Name.ToLower(); return (n.Contains("sound") || n.Contains("sfx") || n.Contains("noise") || n.Contains("idle") || n.Contains("stomp") || n.Contains("footstep") || n.Contains("growl") || n.Contains("moan") || n.Contains("roar") || n.Contains("chitter") || n.Contains("chuckle") || n.Contains("hiss") || n.Contains("breathe") || n.Contains("grunt") || n.Contains("step")) && !_dangerKeywords.Any((string bad) => n.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] ClientRpc " + type.Name + "." + methodInfo.Name + " → all clients (incl. vanilla)")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[GhostlyWorld] ClientRpc invoke failed: " + ex.Message)); return false; } } private bool TryTriggerViaSpawnedEnemy(PlayerControllerB player) { //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) List list = RoundManager.Instance?.SpawnedEnemies; if (list == null || list.Count == 0) { return false; } bool isInside = player.isInsideFactory; SelectableLevel val = StartOfRound.Instance?.currentLevel; HashSet hostileNames = new HashSet(); if (val?.Enemies != null) { foreach (SpawnableEnemyWithRarity enemy in val.Enemies) { if ((Object)(object)enemy?.enemyType != (Object)null) { hostileNames.Add(enemy.enemyType.enemyName); } } } if (val?.OutsideEnemies != null) { foreach (SpawnableEnemyWithRarity outsideEnemy in val.OutsideEnemies) { if ((Object)(object)outsideEnemy?.enemyType != (Object)null) { hostileNames.Add(outsideEnemy.enemyType.enemyName); } } } List list2 = (from _ in list where (Object)(object)_ != (Object)null && !_.isEnemyDead && _.isOutside == !isInside && (hostileNames.Count == 0 || hostileNames.Contains(_.enemyType?.enemyName ?? "")) orderby Random.value select _).ToList(); if (list2.Count == 0) { list2 = (from _ in list where (Object)(object)_ != (Object)null && !_.isEnemyDead && (hostileNames.Count == 0 || hostileNames.Contains(_.enemyType?.enemyName ?? "")) orderby Random.value select _).ToList(); } if (list2.Count == 0) { return false; } EnemyAI val2 = list2[0]; Vector3 position = ((Component)val2).transform.position; TryInvokeAudioClientRpc(val2); SendPlaySoundMessage(position, val2.enemyType?.enemyName ?? "", isInside, isHuntSFX: false, visualOnly: true); OpenNearbyDoors(position, 15f, Random.Range(1, 2)); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Real-enemy paranoia: {val2.enemyType?.enemyName} at {position}"); return true; } private IEnumerator GhostlyFallback(PlayerControllerB player) { bool isInsideFactory = player.isInsideFactory; SelectableLevel val = StartOfRound.Instance?.currentLevel; if ((Object)(object)val == (Object)null) { yield break; } List list = (isInsideFactory ? val.Enemies : val.OutsideEnemies); if (list == null || list.Count == 0) { yield break; } List 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 val2 = list2[Random.Range(0, list2.Count)]; Vector3 val3 = Random.onUnitSphere * Random.Range(8f, 18f); val3.y = 0f; Vector3 val4 = ((Component)player).transform.position + val3; NavMeshHit val5 = default(NavMeshHit); if (NavMesh.SamplePosition(val4, ref val5, 15f, -1)) { val4 = ((NavMeshHit)(ref val5)).position; } SendPlaySoundMessage(val4, val2.enemyName, isInsideFactory, isHuntSFX: false); OpenNearbyDoors(val4, 15f, Random.Range(1, 2)); } } private unsafe void SendPlaySoundMessage(Vector3 position, string enemyName, bool isInside, bool isHuntSFX, bool visualOnly = false) { //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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(110, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((FastBufferWriter)(ref val)).WriteValueSafe(enemyName, false); ((FastBufferWriter)(ref val)).WriteValueSafe(ref isInside, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref isHuntSFX, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref visualOnly, default(ForPrimitives)); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { singleton.CustomMessagingManager.SendNamedMessage("RandomEvents_PlayGhostlySound", connectedClientsId, val, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } 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 list = new List(); Collider[] array2 = array; foreach (Collider val in array2) { if (!((Object)(object)val == (Object)null)) { DoorLock componentInParent = ((Component)val).GetComponentInParent(); 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 list2 = new List(); array2 = array; foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { TerminalAccessibleObject componentInParent2 = ((Component)val2).GetComponentInParent(); 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, bool visualOnly = false) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Expected O, but got Unknown //IL_043e: 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; } if (visualOnly) { if (!isHuntSFX) { GhostlyVisual.Spawn(playPos, val); } return; } if (CustomHorrorClips.Count > 0 && Random.value < 0.25f) { AudioClip val2 = CustomHorrorClips[Random.Range(0, CustomHorrorClips.Count)]; Plugin.Log.LogInfo((object)$"[GhostlyWorld] Custom horror sound: {((Object)val2).name} at {playPos}"); GameObject val3 = new GameObject("TempCustomHorrorAudio"); val3.transform.position = playPos; AudioSource val4 = val3.AddComponent(); val4.clip = val2; val4.spatialBlend = 1f; val4.minDistance = 6f; val4.maxDistance = 50f; val4.volume = 1f; val4.Play(); GhostlyAudioTrigger.Attach(val3, val4, val2.length + 0.5f, playPos); if (!isHuntSFX) { GhostlyVisual.Spawn(playPos, val); } return; } List list = GetEnemyAudioClips(val); if (list.Count == 0) { SelectableLevel val5 = StartOfRound.Instance?.currentLevel; List list2 = new List(); if (val5?.Enemies != null) { list2.AddRange(val5.Enemies); } if (val5?.OutsideEnemies != null) { list2.AddRange(val5.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 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 val6 = null; AudioClip val7 = 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")) { val7 = item2; } else if (text.Contains("foot") || text.Contains("step") || text.Contains("walk") || text.Contains("run") || text.Contains("move") || text.Contains("crawl") || text.Contains("skitter")) { val6 = item2; } } } if ((Object)(object)val6 == (Object)null && list.Count > 0) { val6 = list[0]; } if ((Object)(object)val7 == (Object)null && list.Count > 1) { val7 = list[1]; } if ((Object)(object)val7 == (Object)null) { val7 = val6; } AudioClip val8 = (isHuntSFX ? val7 : val6); if (!((Object)(object)val8 == (Object)null)) { Plugin.Log.LogInfo((object)$"[GhostlyWorld] Playing clip '{((Object)val8).name}' for {enemyName} at {playPos}"); GameObject val9 = new GameObject("TempGhostlyAudio"); val9.transform.position = playPos; AudioSource val10 = val9.AddComponent(); val10.clip = val8; val10.spatialBlend = 1f; val10.minDistance = 6f; val10.maxDistance = 50f; val10.volume = 1f; val10.Play(); GhostlyAudioTrigger.Attach(val9, val10, val8.length + 0.5f, playPos); if (!isHuntSFX) { GhostlyVisual.Spawn(playPos, val); } } } private static EnemyType? FindEnemyTypeByName(string name) { SelectableLevel val = StartOfRound.Instance?.currentLevel; if ((Object)(object)val != (Object)null) { List[] array = new List[3] { val.Enemies, val.OutsideEnemies, val.DaytimeEnemies }; foreach (List list in array) { if (list != null) { SpawnableEnemyWithRarity val2 = ((IEnumerable)list).FirstOrDefault((Func)((SpawnableEnemyWithRarity e) => e.enemyType?.enemyName == name)); if ((Object)(object)val2?.enemyType != (Object)null) { return val2.enemyType; } } } } return null; } private static List GetEnemyAudioClips(EnemyType enemyType) { if ((Object)(object)enemyType?.enemyPrefab == (Object)null) { return new List(); } if (_clipCache.TryGetValue(enemyType.enemyName, out List value)) { return value; } List list = new List(); AudioSource[] componentsInChildren = enemyType.enemyPrefab.GetComponentsInChildren(true); foreach (AudioSource val in componentsInChildren) { if ((Object)(object)val != (Object)null && (Object)(object)val.clip != (Object)null && !list.Contains(val.clip)) { list.Add(val.clip); } } MonoBehaviour[] componentsInChildren2 = enemyType.enemyPrefab.GetComponentsInChildren(true); foreach (MonoBehaviour val2 in componentsInChildren2) { if ((Object)(object)val2 == (Object)null) { continue; } try { FieldInfo[] fields = ((object)val2).GetType().GetFields(_bfAll); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(AudioClip)) { object? value2 = fieldInfo.GetValue(val2); AudioClip val3 = (AudioClip)((value2 is AudioClip) ? value2 : null); if ((Object)(object)val3 != (Object)null && !list.Contains(val3)) { list.Add(val3); } } else { if (!(fieldInfo.FieldType == typeof(AudioClip[])) || !(fieldInfo.GetValue(val2) is AudioClip[] array)) { continue; } AudioClip[] array2 = array; foreach (AudioClip val4 in array2) { if ((Object)(object)val4 != (Object)null && !list.Contains(val4)) { list.Add(val4); } } } } } catch { } } Plugin.Log.LogInfo((object)$"[GhostlyWorld] Cached {list.Count} clips for {enemyType.enemyName}"); _clipCache[enemyType.enemyName] = list; return list; } } public class GhostlyAudioTrigger : MonoBehaviour { private AudioSource _src; private Vector3 _soundPos; private float _maxLifetime; private float _elapsed; private bool _fading; private float _fadeElapsed; private const float FadeDuration = 1.2f; private const float LookAngle = 45f; public static void Attach(GameObject go, AudioSource src, float maxLifetime, Vector3 soundPos) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) GhostlyAudioTrigger ghostlyAudioTrigger = go.AddComponent(); ghostlyAudioTrigger._src = src; ghostlyAudioTrigger._maxLifetime = maxLifetime; ghostlyAudioTrigger._soundPos = soundPos; } private void Update() { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) _elapsed += Time.deltaTime; if ((Object)(object)_src == (Object)null || _elapsed >= _maxLifetime) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } if (_fading) { _fadeElapsed += Time.deltaTime; _src.volume = Mathf.Lerp(1f, 0f, _fadeElapsed / 1.2f); if (_fadeElapsed >= 1.2f) { _src.Stop(); Object.Destroy((Object)(object)((Component)this).gameObject); } return; } if (!_src.isPlaying) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null)) { Vector3 val2 = _soundPos - ((Component)val).transform.position; val2.y = 0f; Vector3 forward = ((Component)val).transform.forward; forward.y = 0f; if (!(((Vector3)(ref val2)).sqrMagnitude < 0.01f) && Vector3.Angle(((Vector3)(ref forward)).normalized, ((Vector3)(ref val2)).normalized) <= 45f) { Plugin.Log.LogInfo((object)"[GhostlyWorld] Player looked at sound source — fading out."); _fading = true; } } } } public class GhostlyVisual : MonoBehaviour { private Vector3 _position; private float _elapsed; private const float Lifetime = 5f; private const float NearLimit = 4f; private const float FarLimit = 22f; public void Initialize(Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _position = position; } private void Update() { //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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) _elapsed += Time.deltaTime; if (_elapsed >= 5f) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return; } float num = Vector3.Distance(((Component)val).transform.position, _position); if (num < 4f || num > 22f) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Vector3 val2 = _position - ((Component)val).transform.position; val2.y = 0f; Vector3 forward = ((Component)val).transform.forward; forward.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude > 0.01f && Vector3.Angle(((Vector3)(ref forward)).normalized, ((Vector3)(ref val2)).normalized) <= 45f) { Object.Destroy((Object)(object)((Component)this).gameObject); } } public static void Spawn(Vector3 soundPos, EnemyType? enemyType) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemyType?.enemyPrefab == (Object)null) { return; } try { Vector3 val = FindNearestDoor(soundPos); PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController; Quaternion val3 = Quaternion.identity; if ((Object)(object)val2 != (Object)null) { Vector3 val4 = ((Component)val2).transform.position - val; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude > 0.01f) { val3 = Quaternion.LookRotation(((Vector3)(ref val4)).normalized); } } GameObject val5 = Object.Instantiate(enemyType.enemyPrefab, val, val3); ((Object)val5).name = "GhostlyVisualOnly"; EnemyAI[] componentsInChildren = val5.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } NavMeshAgent[] componentsInChildren2 = val5.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ((Behaviour)componentsInChildren2[i]).enabled = false; } Collider[] componentsInChildren3 = val5.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = false; } AudioSource[] componentsInChildren4 = val5.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren4.Length; i++) { ((Behaviour)componentsInChildren4[i]).enabled = false; } val5.AddComponent().Initialize(val); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Ghost visual spawned: {enemyType.enemyName} at {val}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[GhostlyWorld] Ghost visual failed: " + ex.Message)); } } private static Vector3 FindNearestDoor(Vector3 near) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) DoorLock[] array = Object.FindObjectsOfType(); DoorLock val = null; float num = float.MaxValue; DoorLock[] array2 = array; foreach (DoorLock val2 in array2) { float num2 = Vector3.Distance(((Component)val2).transform.position, near); if (num2 < num) { num = num2; val = val2; } } if (!((Object)(object)val != (Object)null) || !(num < 10f)) { return near; } return ((Component)val).transform.position; } } [HarmonyPatch(typeof(RoundManager), "SyncScrapValuesClientRpc")] public static class GradualSpawnPatch { private const float WaveInterval = 300f; private static Coroutine? _active; [HarmonyPostfix] public static void Postfix() { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return; } if (_active != null) { try { ((MonoBehaviour)instance).StopCoroutine(_active); } catch { } } _active = ((MonoBehaviour)instance).StartCoroutine(RunGradualReveal(instance)); } private static IEnumerator RunGradualReveal(RoundManager rm) { yield return (object)new WaitForSeconds(2f); List list = (from _ in Object.FindObjectsOfType().Where(delegate(GrabbableObject g) { if ((Object)(object)g != (Object)null) { Item itemProperties = g.itemProperties; if (itemProperties != null && itemProperties.isScrap && !g.isInShipRoom && !g.isInElevator && !g.isHeld) { return !g.isHeldByEnemy; } } return false; }) orderby Random.value select _).ToList(); if (list.Count < 8) { Plugin.Log.LogInfo((object)$"[GradualSpawn] Only {list.Count} items — skipping gradual reveal."); yield break; } int num = list.Count / 4; List list2 = list.Take(num).ToList(); List wave2 = list.Skip(num).Take(num).ToList(); List wave3 = list.Skip(num * 2).Take(num).ToList(); List wave4 = list.Skip(num * 3).ToList(); Plugin.Log.LogInfo((object)($"[GradualSpawn] total={list.Count} " + $"w1={list2.Count} w2={wave2.Count} w3={wave3.Count} w4={wave4.Count}")); Dictionary hidden = new Dictionary(); Vector3 hidePos = default(Vector3); ((Vector3)(ref hidePos))..ctor(0f, -500f, 0f); foreach (GrabbableObject item in wave2.Concat(wave3).Concat(wave4)) { if ((Object)(object)item != (Object)null) { HideItem(item, hidePos, hidden); } } int count = hidden.Count; HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer($"[Лут] На объекте {list2.Count} предметов. " + $"Ещё {count} появятся позже.", -1); } yield return (object)new WaitForSeconds(300f); if (ShipLeft()) { RevealAll(hidden); yield break; } int num2 = RevealWave(wave2, hidden); if (num2 > 0) { HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.AddTextToChatOnServer($"[Лут] \ud83d\udce6 Новая партия лута появилась! (+{num2})", -1); } } yield return (object)new WaitForSeconds(300f); if (ShipLeft()) { RevealAll(hidden); yield break; } int num3 = RevealWave(wave3, hidden); if (num3 > 0) { HUDManager instance3 = HUDManager.Instance; if (instance3 != null) { instance3.AddTextToChatOnServer($"[Лут] \ud83d\udce6 Новая партия лута появилась! (+{num3})", -1); } } yield return (object)new WaitForSeconds(300f); if (ShipLeft()) { RevealAll(hidden); yield break; } int num4 = RevealWave(wave4, hidden); if (num4 > 0) { HUDManager instance4 = HUDManager.Instance; if (instance4 != null) { instance4.AddTextToChatOnServer("[Лут] \ud83d\udce6 Последняя партия лута! " + $"(+{num4}) — весь доступный лут на объекте.", -1); } } _active = null; } private static bool ShipLeft() { return StartOfRound.Instance?.inShipPhase ?? false; } private static void HideItem(GrabbableObject item, Vector3 hidePos, Dictionary hidden) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { hidden[item] = ((Component)item).transform.position; Rigidbody component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { component.velocity = Vector3.zero; component.isKinematic = true; } ((Component)item).transform.position = hidePos; NetworkTransform component2 = ((Component)item).GetComponent(); if (component2 != null) { component2.Teleport(hidePos, ((Component)item).transform.rotation, ((Component)item).transform.localScale); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[GradualSpawn] HideItem: " + ex.Message)); } } private static int RevealWave(List wave, Dictionary hidden) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (GrabbableObject item in wave) { if ((Object)(object)item == (Object)null || !hidden.ContainsKey(item)) { continue; } if (item.isHeld || item.isInShipRoom) { hidden.Remove(item); continue; } try { Vector3 val = hidden[item]; hidden.Remove(item); ((Component)item).transform.position = val; NetworkTransform component = ((Component)item).GetComponent(); if (component != null) { component.Teleport(val, ((Component)item).transform.rotation, ((Component)item).transform.localScale); } Rigidbody component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = false; } num++; } catch { } } return num; } private static void RevealAll(Dictionary hidden) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (KeyValuePair item in hidden.ToList()) { if ((Object)(object)item.Key == (Object)null || item.Key.isHeld || item.Key.isInShipRoom) { continue; } try { ((Component)item.Key).transform.position = item.Value; NetworkTransform component = ((Component)item.Key).GetComponent(); if (component != null) { component.Teleport(item.Value, ((Component)item.Key).transform.rotation, ((Component)item.Key).transform.localScale); } Rigidbody component2 = ((Component)item.Key).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = false; } num++; } catch { } } hidden.Clear(); _active = null; if (num > 0) { Plugin.Log.LogInfo((object)$"[GradualSpawn] Ship leaving — revealed {num} remaining items."); } } } internal static class ModConfig { private static readonly Dictionary> _eventEnabled = new Dictionary>(); private static readonly Dictionary _eventRuNames = new Dictionary(); public static ConfigEntry BaselineInside { get; private set; } = null; public static ConfigEntry BaselineOutside { get; private set; } = null; public static ConfigEntry BaselineDaytime { get; private set; } = null; public static ConfigEntry EventPowerScale { get; private set; } = null; public static ConfigEntry WeightGood { get; private set; } = null; public static ConfigEntry WeightNeutral { get; private set; } = null; public static ConfigEntry WeightVeryBad { get; private set; } = null; public static ConfigEntry WeightInsane { get; private set; } = null; public static ConfigEntry MaxHistorySize { get; private set; } = null; public static bool IsEventEnabled(string id) { if (_eventEnabled.TryGetValue(id, out ConfigEntry value)) { return value.Value; } return true; } internal static void Init(ConfigFile cfg) { BaselineInside = cfg.Bind("Сила монстров", "Множитель внутри", 3f, "Во сколько раз увеличить максимальное количество монстров ВНУТРИ здания каждый раунд. По умолчанию: 3.0"); BaselineOutside = cfg.Bind("Сила монстров", "Множитель снаружи", 2.5f, "Во сколько раз увеличить максимальное количество монстров СНАРУЖИ каждый раунд. По умолчанию: 2.5"); BaselineDaytime = cfg.Bind("Сила монстров", "Множитель дневных", 2f, "Во сколько раз увеличить максимальное количество ДНЕВНЫХ монстров каждый раунд. По умолчанию: 2.0"); EventPowerScale = cfg.Bind("Сила монстров", "Сила баффов событий", 1f, "Насколько сильно ОТДЕЛЬНЫЕ СОБЫТИЯ (Вечеринка Лозы ×2.5, Метеоритная блокада ×3 и т.п.) меняют число монстров относительно базового уровня. 1.0 = как задумано автором событий, 0.5 = эффект событий вдвое мягче, 0 = события вообще не меняют число монстров (только баф/нерф добычи и т.п.). Сам базовый уровень не трогает — для него ползунки выше. По умолчанию: 1.0"); WeightGood = cfg.Bind("Веса событий", "Вес хорошего", 15, "Частота выпадения хороших событий (✓). Чем выше — тем чаще. По умолчанию: 15"); WeightNeutral = cfg.Bind("Веса событий", "Вес нейтрального", 23, "Частота выпадения нейтральных событий (≡). По умолчанию: 23"); WeightVeryBad = cfg.Bind("Веса событий", "Вес плохого", 52, "Частота выпадения плохих событий (⚠). По умолчанию: 52"); WeightInsane = cfg.Bind("Веса событий", "Вес хаоса", 10, "Частота выпадения событий хаоса (‼). По умолчанию: 10"); MaxHistorySize = cfg.Bind("История событий", "Размер истории", 14, "Сколько последних событий помнит система (защита от повторов). Чем больше — тем реже повторения. По умолчанию: 14"); BindEvents(cfg, "Метеориты", new(string, string, string)[10] { ("meteor_fog_invasion", "Туманное вторжение", "Туман + орда снаружи + дорогая добыча"), ("meteor_golden", "Золотой метеорит", "Один предмет стоимостью 1500-2200 кредитов"), ("meteor_celestial_shower", "Метеоритный душ", "Много метеоритов → много добычи, но дешёвой"), ("meteor_rain_of_riches", "Дождь богатств", "Метеоритный дождь + дорогая добыча"), ("meteor_swarm", "Метеоритный рой", "Орда снаружи + много добычи"), ("meteor_lockdown", "Метеоритная блокада", "Метеориты загнали монстров внутрь: снаружи безопасно, внутри тройная угроза"), ("meteor_calm", "Спокойный метеорит", "Метеориты без особых угроз + бонус к добыче"), ("meteor_impact_zone", "Зона удара", "Хаос: шторм + метеориты + усиленные монстры везде"), ("meteor_cosmic_treasure", "Космическое сокровище", "Метеориты + нет монстров + 1.5× цена"), ("meteor_star_gift", "Дар звёзд", "Метеориты + добыча дороже + меньше внешних угроз") }); BindEvents(cfg, "Монстры", new(string, string, string)[14] { ("monster_bracken_party", "Вечеринка Лозы", "Только Bracken внутри в 2.5× количестве"), ("monster_spider_nest", "Паучье гнездо", "Только пауки внутри в 2× количестве"), ("monster_hoarder_heaven", "Рай скупердяев", "Только Hoarding Bugs внутри в 2.5× количестве"), ("monster_bird_migration", "Перелёт птиц", "Снаружи только безвредные Manticoils — абсолютно безопасно"), ("monster_giant_walk", "Прогулка гигантов", "Только Лесные Гиганты снаружи в 3.5× количестве"), ("monster_nutcracker_drill", "Учения щелкунчиков", "Только Щелкунчики внутри, максимум 8 штук одновременно"), ("monster_snare_flea_farm", "Ферма членистоногих", "Только Snare Flea внутри в 3× количестве"), ("monster_thumper_rally", "Ралли Стукачей", "Только Thumper внутри в 3× количестве"), ("monster_outside_only", "Все снаружи", "Все монстры вышли на улицу в 4× — внутри безопасно"), ("monster_coilhead_patrol", "Стальной дозор", "Катушки вырвались наружу! Снаружи опасно, внутри спокойно"), ("monster_spider_outdoors", "Внешняя паутина", "Пауки оплели территорию снаружи — внутри пусто"), ("monster_baboon_siege", "Осада бабуинов", "Стая бабуинов ворвалась в комплекс — снаружи пусто"), ("monster_mech_incursion", "Вторжение меха", "Механический страж взломал комплекс! Ракеты в коридорах (Insane)"), ("monster_giant_breach", "Гигант в здании", "Лесной Гигант ломится внутрь комплекса (Insane)") }); BindEvents(cfg, "Лут", new(string, string, string)[17] { ("one_artifact_discovery", "Открытие артефактов", "Только один тип предметов, зато каждый на 25% дороже"), ("one_same_kind_bonanza", "Бонанза одного типа", "Только один тип предметов в 1.4× количестве"), ("scrap_rich_day", "Богатый день", "Предметы на 25% дороже, на 30% меньше"), ("scrap_poor_day", "Скудный день", "Предметов вдвое больше, но каждый стоит вдвое дешевле"), ("scrap_jackpot", "Джекпот", "Предметов почти нет (20% от нормы), зато каждый на 25% дороже"), ("scrap_warehouse", "Склад", "3× количество предметов, но цена 0.5×"), ("scrap_balanced_harvest", "Сбалансированный урожай", "Небольшой баф: +8% к цене и количеству"), ("scrap_diamond_field", "Алмазное поле", "10× цена, но предметов почти нет (8% от нормы)"), ("scrap_feast", "Пир добычи", "В 1.25× больше предметов, но каждый стоит 0.6×"), ("scrap_cursed", "Проклятая добыча", "Монстров нет, но предметы стоят полцены. Тихо... подозрительно тихо"), ("scrap_high_stakes", "Высокие ставки", "Монстры в 3× везде, добыча дороже — классический хаос"), ("scrap_rush_hour", "Час пик", "Монстры в 3× везде! Добыча на 35% дороже и на 25% больше"), ("scrap_empty_moon", "Пустая луна", "Почти нет предметов — тихий раунд"), ("one_uniform_pricing", "Единая цена", "Все предметы стоят одинаково (67-228 кредитов)"), ("one_gold_rush", "Золотая лихорадка", "Только дорогие предметы (+25%), но их мало (50% от нормы)"), ("one_junk_avalanche", "Горы хлама", "В 3× больше предметов, но каждый стоит вдвое дешевле"), ("one_precious_item", "Драгоценный предмет", "Один случайный тип предметов с джекпотом — всё остальное убрано") }); BindEvents(cfg, "Погода", new(string, string, string)[8] { ("weather_fog_raid", "Туманный налёт", "Туман + 3× монстры снаружи + дорогая добыча"), ("weather_thunder_bonus", "Грозовой бонус", "Шторм + добыча дороже на 12%"), ("weather_flood_gold", "Золотое наводнение", "Наводнение + добыча дороже на 10%"), ("weather_eclipse_bonus", "Затмение-бонус", "Затмение + монстры снаружи опаснее + добыча дороже на 40%"), ("weather_clear_harvest", "Ясный урожай", "Ясная погода + больше предметов"), ("weather_rainy_surplus", "Дождливый излишек", "Дождь + предметов больше на 12%"), ("weather_dust_treasure", "Пыльное сокровище", "Пыльные облака + добыча дороже на 10%"), ("weather_calm_storm", "Шторм разогнал монстров", "Шторм отпугнул всех снаружи + добыча дороже на 25%") }); BindEvents(cfg, "Специальные", new(string, string, string)[16] { ("monster_peaceful_planet", "Нет монстров", "Монстры не появляются весь раунд — полная тишина"), ("one_type_inside", "Один тип внутри", "Случайный один тип монстров внутри здания"), ("one_type_outside", "Один тип снаружи", "Случайный один тип монстров снаружи"), ("scrap_double_or_nothing", "Удвоение или ноль", "50/50: либо ×2 добыча, либо совсем ничего"), ("special_easy_day", "Лёгкий день", "Вдвое меньше монстров, нормальная добыча"), ("special_hard_day", "Тяжёлый день", "Монстры ×3 везде, добыча +20%"), ("special_extreme_day", "Экстремальный день", "Монстры ×4 везде — настоящий хаос"), ("special_swarm_outside", "Рой снаружи", "Орда ×4 снаружи, внутри пусто"), ("special_fortress_inside", "Крепость внутри", "Орда ×4 внутри, снаружи пусто"), ("special_daytime_rush", "Дневной натиск", "Дневные монстры в 3×, ночью снаружи пусто"), ("special_lucky_break", "Счастливый перерыв", "Монстров вдвое меньше, добыча чуть дороже"), ("special_company_audit", "Аудит Компании", "Стоимость добычи +12%, монстры в норме"), ("special_minefield", "Минное поле", "Внутри здания в 6× больше мин! Добыча +12%"), ("special_turret_hell", "Турельный ад", "Внутри здания орда турелей + добыча +20%"), ("special_lights_out", "Отключение питания", "Полная темнота внутри + только Coil-Head (Insane)"), ("special_ghost_town", "Город-призрак", "Нет монстров, много добычи — слишком тихо") }); if (Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig")) { RegisterLethalConfig(); } cfg.SettingChanged += delegate { cfg.Save(); }; } private static void BindEvents(ConfigFile cfg, string section, (string id, string ruName, string desc)[] events) { for (int i = 0; i < events.Length; i++) { var (text, text2, text3) = events[i]; _eventEnabled[text] = cfg.Bind(section, text, true, "[" + text2 + "] " + text3); _eventRuNames[text] = text2; } } private static void RegisterLethalConfig() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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) //IL_0087: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00d9: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //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) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Expected O, but got Unknown //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected O, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Expected O, but got Unknown //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Expected O, but got Unknown //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Expected O, but got Unknown //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown LethalConfigManager.SetModDescription("Настройки RandomEventsExtended\nВсе изменения применяются со следующего раунда — рестарт не нужен."); ConfigEntry baselineInside = BaselineInside; FloatStepSliderOptions val = new FloatStepSliderOptions { Name = "Множитель монстров внутри", Description = "Во сколько раз увеличить лимит монстров ВНУТРИ здания каждый раунд.\n1.0 = без изменений, 2.0 = вдвое больше." }; ((BaseRangeOptions)val).Min = 0.5f; ((BaseRangeOptions)val).Max = 5f; val.Step = 0.1f; ((BaseOptions)val).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatStepSliderConfigItem(baselineInside, val)); ConfigEntry baselineOutside = BaselineOutside; FloatStepSliderOptions val2 = new FloatStepSliderOptions { Name = "Множитель монстров снаружи", Description = "Во сколько раз увеличить лимит монстров СНАРУЖИ каждый раунд.\n1.0 = без изменений, 2.0 = вдвое больше." }; ((BaseRangeOptions)val2).Min = 0.5f; ((BaseRangeOptions)val2).Max = 5f; val2.Step = 0.1f; ((BaseOptions)val2).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatStepSliderConfigItem(baselineOutside, val2)); ConfigEntry baselineDaytime = BaselineDaytime; FloatStepSliderOptions val3 = new FloatStepSliderOptions { Name = "Множитель дневных монстров", Description = "Во сколько раз увеличить лимит ДНЕВНЫХ монстров каждый раунд.\n1.0 = без изменений." }; ((BaseRangeOptions)val3).Min = 0.5f; ((BaseRangeOptions)val3).Max = 5f; val3.Step = 0.1f; ((BaseOptions)val3).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatStepSliderConfigItem(baselineDaytime, val3)); ConfigEntry eventPowerScale = EventPowerScale; FloatStepSliderOptions val4 = new FloatStepSliderOptions { Name = "Сила баффов событий на монстров", Description = "Насколько сильно КОНКРЕТНЫЕ СОБЫТИЯ (Вечеринка Лозы ×2.5, Метеоритная блокада ×3,\nЛёгкий день ×0.35 и т.д.) меняют число монстров относительно базового уровня.\n1.0 = как задумано автором, 0.5 = эффект вдвое мягче, 0 = события вовсе не\nтрогают число монстров (но их прочие эффекты — добыча, погода — остаются).\nСам базовый уровень не меняет — для него три ползунка выше." }; ((BaseRangeOptions)val4).Min = 0f; ((BaseRangeOptions)val4).Max = 3f; val4.Step = 0.1f; ((BaseOptions)val4).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatStepSliderConfigItem(eventPowerScale, val4)); ConfigEntry weightGood = WeightGood; IntSliderOptions val5 = new IntSliderOptions { Name = "Вес хорошего события (✓)", Description = "Частота выпадения хороших событий.\nЧем выше относительно других — тем чаще выпадают позитивные события." }; ((BaseRangeOptions)val5).Min = 0; ((BaseRangeOptions)val5).Max = 100; ((BaseOptions)val5).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(weightGood, val5)); ConfigEntry weightNeutral = WeightNeutral; IntSliderOptions val6 = new IntSliderOptions { Name = "Вес нейтрального события (≡)", Description = "Частота выпадения нейтральных событий." }; ((BaseRangeOptions)val6).Min = 0; ((BaseRangeOptions)val6).Max = 100; ((BaseOptions)val6).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(weightNeutral, val6)); ConfigEntry weightVeryBad = WeightVeryBad; IntSliderOptions val7 = new IntSliderOptions { Name = "Вес плохого события (⚠)", Description = "Частота выпадения плохих событий.\nПо умолчанию самый высокий — игра должна быть сложной." }; ((BaseRangeOptions)val7).Min = 0; ((BaseRangeOptions)val7).Max = 100; ((BaseOptions)val7).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(weightVeryBad, val7)); ConfigEntry weightInsane = WeightInsane; IntSliderOptions val8 = new IntSliderOptions { Name = "Вес события хаоса (‼)", Description = "Частота выпадения экстремальных событий (Insane).\nСлишком высокое значение сделает игру невыносимой." }; ((BaseRangeOptions)val8).Min = 0; ((BaseRangeOptions)val8).Max = 100; ((BaseOptions)val8).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(weightInsane, val8)); ConfigEntry maxHistorySize = MaxHistorySize; IntSliderOptions val9 = new IntSliderOptions { Name = "Размер истории событий", Description = "Сколько прошлых событий помнит система.\nСобытие не выпадет повторно пока находится в истории.\nБольше = меньше повторений." }; ((BaseRangeOptions)val9).Min = 3; ((BaseRangeOptions)val9).Max = 40; ((BaseOptions)val9).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(maxHistorySize, val9)); foreach (KeyValuePair> item in _eventEnabled) { _eventRuNames.TryGetValue(item.Key, out string value); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(item.Value, new BoolCheckBoxOptions { Name = (value ?? item.Key), Description = ((ConfigEntryBase)item.Value).Description.Description, RequiresRestart = false })); } } } [HarmonyPatch(typeof(Terminal), "LoadNewNode")] public static class TerminalDangerRatingPatch { [HarmonyPostfix] public static void Postfix(Terminal __instance, TerminalNode node) { try { FieldInfo fieldInfo = AccessTools.Field(typeof(TerminalNode), "displayPlanetInfo"); if (fieldInfo == null) { return; } int num = (int)(fieldInfo.GetValue(node) ?? ((object)(-1))); if (num < 0) { return; } StartOfRound instance = StartOfRound.Instance; if (instance?.levels == null || num >= instance.levels.Length) { return; } SelectableLevel val = instance.levels[num]; if ((Object)(object)val == (Object)null) { return; } string text = ((Object)val).name?.ToLower() ?? ""; if (text.Contains("gordion") || text.Contains("company")) { return; } TMP_InputField screenText = __instance.screenText; if ((Object)(object)screenText == (Object)null) { return; } string text2 = screenText.text; if (!string.IsNullOrEmpty(text2)) { Regex regex = new Regex("(?i)(risk\\s*level\\s*[:\\-]?\\s*\\S*[^\\n]*)", RegexOptions.IgnoreCase); if (regex.IsMatch(text2)) { string replacement = BuildDangerLine(val); screenText.text = regex.Replace(text2, replacement, 1); Plugin.Log.LogInfo((object)("[DangerRating] Terminal updated for: " + val.PlanetName)); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DangerRating] Terminal patch: " + ex.Message)); } } internal static string BuildDangerLine(SelectableLevel level) { int num = (level.riskLevel ?? "?").Trim().ToUpper() switch { "D" => 1, "C" => 2, "B" => 3, "A" => 4, "S" => 5, "S+" => 6, _ => 3, }; string text = new string('█', num) + new string('░', Math.Max(0, 6 - num)); string text2 = num switch { 1 => "#4DFF6E", 2 => "#AEFF6E", 3 => "#FFE44D", 4 => "#FF9933", 5 => "#FF4444", _ => "#FF2E2E", }; string text3 = num switch { 1 => "МИНИМАЛЬНАЯ", 2 => "НИЗКАЯ", 3 => "СРЕДНЯЯ", 4 => "ВЫСОКАЯ", 5 => "ОПАСНАЯ", _ => "ЭКСТРЕМАЛЬНАЯ", }; return "Уровень опасности: [" + text + "] " + text3 + ""; } } public static class DangerRating { public static void SendToChat(SelectableLevel? level, GameEventData loot, GameEventData weather, GameEventData monster) { HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)level == (Object)null)) { int num = ScoreFromRarity(loot.Rarity) + ScoreFromRarity(weather.Rarity) + ScoreFromRarity(monster.Rarity); string text = (level.riskLevel ?? "?").Trim().ToUpper(); int num2 = num + text switch { "D" => 0, "C" => 1, "B" => 2, "A" => 3, "S" => 4, "S+" => 5, _ => 2, }; int num3 = Math.Min(10, (int)Math.Round((double)num2 / 2.0)); string text2 = new string('█', num3) + new string('░', 10 - num3); string text3; string text4; if (num2 <= 2) { text3 = "#4DFF6E"; text4 = "БЕЗОПАСНО"; } else if (num2 <= 4) { text3 = "#AEFF6E"; text4 = "НИЗКАЯ"; } else if (num2 <= 6) { text3 = "#FFE44D"; text4 = "СРЕДНЯЯ"; } else if (num2 <= 9) { text3 = "#FF9933"; text4 = "ВЫСОКАЯ"; } else if (num2 <= 13) { text3 = "#FF4444"; text4 = "ОПАСНАЯ"; } else { text3 = "#FF2E2E"; text4 = "КРИТИЧЕСКАЯ"; } string text5 = "══ УГРОЗА СЕГОДНЯ ══\nЛуна: " + (level.PlanetName ?? ((Object)level).name) + " [" + text + "]\n" + text2 + " " + text4 + ""; instance.AddTextToChatOnServer(ChatStyle.Soft(text5), -1); } } private static int ScoreFromRarity(GameEventRarity r) { return r switch { GameEventRarity.Good => 0, GameEventRarity.Neutral => 1, GameEventRarity.VeryBad => 3, GameEventRarity.Insane => 5, _ => 1, }; } } [HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")] public static class ChatCommandPatch { private static readonly string[] _chatFieldNames = new string[4] { "chatTextField", "chatText", "chatTextInput", "chatInput" }; [HarmonyPrefix] public static bool Prefix(HUDManager __instance) { try { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer) { return true; } TMP_InputField val = FindChatField(__instance); if ((Object)(object)val == (Object)null) { return true; } string text = (val.text ?? "").Trim(); if (!text.StartsWith("!")) { return true; } string text2 = text.TrimStart('!').ToLower().Split(' ')[0]; if (text2 == "events") { HandleEvents(); val.text = ""; return false; } if (text2 == "bounty") { HandleBounty(); val.text = ""; return false; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ChatCmd] " + ex.Message)); } return true; } private static TMP_InputField? FindChatField(HUDManager hud) { string[] chatFieldNames = _chatFieldNames; foreach (string text in chatFieldNames) { object? obj = AccessTools.Field(typeof(HUDManager), text)?.GetValue(hud); TMP_InputField val = (TMP_InputField)((obj is TMP_InputField) ? obj : null); if (val != null) { return val; } } FieldInfo[] fields = typeof(HUDManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(TMP_InputField)) { object? value = fieldInfo.GetValue(hud); TMP_InputField val2 = (TMP_InputField)((value is TMP_InputField) ? value : null); if (val2 != null) { return val2; } } } return null; } private static void HandleEvents() { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("══ СОБЫТИЯ ══"); List currentRoundEvents = RoundStartPatch.CurrentRoundEvents; if (currentRoundEvents != null && currentRoundEvents.Count > 0) { stringBuilder.AppendLine("Сегодня:"); foreach (GameEventData item in currentRoundEvents) { string text = item.Rarity switch { GameEventRarity.Good => "#4DFF6E", GameEventRarity.Neutral => "#FFE44D", GameEventRarity.VeryBad => "#FF7A35", GameEventRarity.Insane => "#FF2E2E", _ => "#FFFFFF", }; stringBuilder.AppendLine(" • " + item.Name + ""); } } else { stringBuilder.AppendLine("Нет данных (ещё не высадились)"); } IReadOnlyList hist = EventBalancer.GetHistory(); if (hist.Count > 0) { int num = Math.Min(6, hist.Count); stringBuilder.AppendLine($"История (посл. {num}):"); int i; for (i = hist.Count - num; i < hist.Count; i++) { string text2 = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == hist[i])?.Name ?? hist[i]; stringBuilder.AppendLine(" • " + text2 + ""); } } instance.AddTextToChatOnServer(ChatStyle.Soft(stringBuilder.ToString().TrimEnd()), -1); } private static void HandleBounty() { HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer(ChatStyle.Soft("★ ТЕКУЩЕЕ ЗАДАНИЕ\n" + BountySystem.GetStatusLine()), -1); } } } public static class MidRoundSystem { private static bool _firedThisRound; public static void Reset() { _firedThisRound = false; } public static IEnumerator RunCoroutine(int roundSeed) { _firedThisRound = false; yield return (object)new WaitForSeconds(240f); if (Plugin.RoundSeed != roundSeed || _firedThisRound) { yield break; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { yield break; } FieldInfo fieldInfo = AccessTools.Field(typeof(StartOfRound), "shipIsLeaving"); if (fieldInfo != null && (bool)(fieldInfo.GetValue(instance) ?? ((object)false))) { yield break; } _firedThisRound = true; List? events = RoundStartPatch.CurrentRoundEvents ?? new List(); bool flag = !EventConflictSystem.AnyHasTag(events, "monsters_zero"); bool flag2 = !EventConflictSystem.AnyHasTag(events, "loot_zero"); (string, string, string, Action, bool, bool)[] obj = new(string, string, string, Action, bool, bool)[8] { ("Разведка Компании", "Обнаружены дополнительные запасы. Ценность добычи +15%.", "#4DFF6E", delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { RoundManager? rM = EventHelpers.RM; rM.scrapValueMultiplier *= 1.15f; } }, false, false), ("Аномальная активность", "Сенсоры фиксируют усиление активности монстров!", "#FF7A35", delegate { SelectableLevel level = EventHelpers.Level; if (!((Object)(object)level == (Object)null)) { int val = Math.Max(1, EventBaseline.InsidePower * 4); int val2 = Math.Max(1, EventBaseline.OutsidePower * 4); level.maxEnemyPowerCount = Math.Min((int)((float)level.maxEnemyPowerCount * 1.3f), val); level.maxOutsideEnemyPowerCount = Math.Min((int)((float)level.maxOutsideEnemyPowerCount * 1.3f), val2); } }, true, false), ("Затишье перед бурей", "Монстры временно отступили. Действуй быстро!", "#AEFF6E", delegate { SelectableLevel level = EventHelpers.Level; if (!((Object)(object)level == (Object)null)) { level.maxEnemyPowerCount = Math.Max(0, (int)((float)level.maxEnemyPowerCount * 0.65f)); level.maxOutsideEnemyPowerCount = Math.Max(0, (int)((float)level.maxOutsideEnemyPowerCount * 0.65f)); } }, true, false), ("Ценная находка", "Компания повысила закупочные цены. Ценность добычи +20%.", "#4DFF6E", delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { RoundManager? rM = EventHelpers.RM; rM.scrapValueMultiplier *= 1.2f; } }, false, true), ("Сигнал тревоги", "Система безопасности активирована! Монстры внутри усиливаются.", "#FF4444", delegate { SelectableLevel level = EventHelpers.Level; if (!((Object)(object)level == (Object)null)) { int val = Math.Max(1, EventBaseline.InsidePower * 4); level.maxEnemyPowerCount = Math.Min((int)((float)level.maxEnemyPowerCount * 1.45f), val); } }, true, false), ("Ионный шторм", "Электромагнитная буря вывела турели из строя.", "#FFE44D", delegate { EventHelpers.DisableAllTurrets(); }, false, false), ("Прорыв периметра", "Барьеры между зонами рухнули! Монстры игнорируют границы комплекса — внутренние вышли наружу, наружные рвутся внутрь. Будь начеку везде.", "#FF6B35", delegate { EventHelpers.RemoveZoneRestrictions(); }, true, false), ("Благоприятные условия", "Погода улучшилась. Небольшой бонус к ценности добычи +10%.", "#AEFF6E", delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { RoundManager? rM = EventHelpers.RM; rM.scrapValueMultiplier *= 1.1f; } }, false, false) }; List<(string, string, string, Action, bool, bool)> list = new List<(string, string, string, Action, bool, bool)>(); (string, string, string, Action, bool, bool)[] array = obj; for (int num = 0; num < array.Length; num++) { (string, string, string, Action, bool, bool) item = array[num]; if ((!item.Item5 || flag) && (!item.Item6 || flag2)) { list.Add(item); } } if (list.Count != 0) { Random random = new Random(roundSeed + 54321); (string, string, string, Action, bool, bool) tuple = list[random.Next(list.Count)]; try { tuple.Item4(); } catch (Exception ex) { Plugin.Log.LogError((object)("[MidRound] Effect error: " + ex.Message)); } HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.AddTextToChatOnServer(ChatStyle.Soft("[ 4 МИН ] " + tuple.Item1 + "\n" + tuple.Item2 + ""), -1); } Plugin.Log.LogInfo((object)("[MidRound] Fired: " + tuple.Item1)); } } } [BepInPlugin("daz.randomeventsextended", "RandomEventsExtended", "3.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public static readonly HashSet RegisteredModdedClients = new HashSet(); private Harmony? _harmony; public static int RoundSeed { get; set; } public static ManualLogSource Log { get; private set; } = null; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("daz.randomeventsextended"); ModConfig.Init(((BaseUnityPlugin)this).Config); SafePatchAll(_harmony); ProceduralHorrorSounds.Generate(); ((MonoBehaviour)this).StartCoroutine(LoadCustomHorrorSoundsCoroutine()); Log.LogInfo((object)"☄ RandomEventsExtended 3.1.3 loaded successfully!"); } private IEnumerator LoadCustomHorrorSoundsCoroutine() { string text = Path.Combine(Paths.PluginPath, "RandomEvents", "Sounds"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); Log.LogInfo((object)("[CustomSounds] Создана папка для звуков: " + text)); yield break; } string[] array = Directory.GetFiles(text, "*.ogg").Concat(Directory.GetFiles(text, "*.wav")).ToArray(); if (array.Length == 0) { Log.LogInfo((object)("[CustomSounds] Файлы звуков не найдены. Положите .ogg/.wav в " + text)); yield break; } Log.LogInfo((object)$"[CustomSounds] Найдено {array.Length} файлов."); string[] array2 = array; foreach (string file in array2) { AudioType val = (AudioType)(file.ToLower().EndsWith(".wav") ? 20 : 14); string text2 = "file:///" + file.Replace('\\', '/'); UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(text2, val); try { yield return req.SendWebRequest(); if ((int)req.result == 1) { AudioClip content = DownloadHandlerAudioClip.GetContent(req); if ((Object)(object)content != (Object)null) { ((Object)content).name = Path.GetFileNameWithoutExtension(file); GhostlyWorldController.CustomHorrorClips.Add(content); Log.LogInfo((object)$"[CustomSounds] Загружен: {((Object)content).name} ({content.length:F1}s)"); } } else { Log.LogWarning((object)("[CustomSounds] Не удалось загрузить " + Path.GetFileName(file) + ": " + req.error)); } } finally { ((IDisposable)req)?.Dispose(); } } Log.LogInfo((object)$"[CustomSounds] Итого загружено: {GhostlyWorldController.CustomHorrorClips.Count} клипов."); } private static void SafePatchAll(Harmony harmony) { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { try { harmony.PatchAll(type); } catch (Exception ex) { Log.LogWarning((object)("[Harmony] Patch skipped for '" + type.Name + "': " + ex.Message)); } } } } public static class EventBaseline { public static int InsidePower; public static int OutsidePower; public static int DaytimePower; public static float ScrapValue = 1f; public static float ScrapAmount = 1f; } [HarmonyPatch(typeof(EnemyAI), "Start")] public static class EnemyNavMeshAllAreasFix { [HarmonyPostfix] public static void Postfix(EnemyAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(MouthDogAI), "Start")] public static class P_MouthDog { [HarmonyPostfix] public static void Postfix(MouthDogAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(SandSpiderAI), "Start")] public static class P_SandSpider { [HarmonyPostfix] public static void Postfix(SandSpiderAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(JesterAI), "Start")] public static class P_Jester { [HarmonyPostfix] public static void Postfix(JesterAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(ForestGiantAI), "Start")] public static class P_ForestGiant { [HarmonyPostfix] public static void Postfix(ForestGiantAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(BaboonBirdAI), "Start")] public static class P_BaboonBird { [HarmonyPostfix] public static void Postfix(BaboonBirdAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(RadMechAI), "Start")] public static class P_RadMech { [HarmonyPostfix] public static void Postfix(RadMechAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(NutcrackerEnemyAI), "Start")] public static class P_Nutcracker { [HarmonyPostfix] public static void Postfix(NutcrackerEnemyAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(StingrayAI), "Start")] public static class P_Stingray { [HarmonyPostfix] public static void Postfix(StingrayAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(PumaAI), "Start")] public static class P_Puma { [HarmonyPostfix] public static void Postfix(PumaAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(HoarderBugAI), "Start")] public static class P_HoarderBug { [HarmonyPostfix] public static void Postfix(HoarderBugAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(FlowermanAI), "Start")] public static class P_Flowerman { [HarmonyPostfix] public static void Postfix(FlowermanAI __instance) { } } [HarmonyPatch(typeof(CrawlerAI), "Start")] public static class P_Crawler { [HarmonyPostfix] public static void Postfix(CrawlerAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(SpringManAI), "DoAIInterval")] public static class SpringManNavMeshTick { [HarmonyPostfix] public static void Postfix(SpringManAI __instance) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && component.areaMask != -1) { component.areaMask = -1; } } catch { } } } [HarmonyPatch(typeof(EnemyAI), "DoAIInterval")] public static class EnemyNavMeshIntervalFix { [HarmonyPostfix] public static void Postfix(EnemyAI __instance) { try { if (!(__instance is FlowermanAI)) { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && component.areaMask != -1) { component.areaMask = -1; } } } catch { } } } [HarmonyPatch(typeof(StartOfRound))] public static class StartOfRoundNetworkingPatch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static HandleNamedMessageDelegate <>9__2_0; public static HandleNamedMessageDelegate <>9__2_2; public static HandleNamedMessageDelegate <>9__2_3; public static HandleNamedMessageDelegate <>9__2_4; internal void b__2_0(ulong senderClientId, FastBufferReader reader) { Plugin.Log.LogInfo((object)$"[Network] Received mod handshake pong from client ID: {senderClientId}"); Plugin.RegisteredModdedClients.Add(senderClientId); } internal void b__2_2(ulong senderClientId, FastBufferReader reader) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); bool isInside = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInside, default(ForPrimitives)); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); bool flag2 = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag2, default(ForPrimitives)); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Client received ghost request: {text} at {val}, hunt: {flag}, visualOnly: {flag2}"); GhostlyWorldController.PlaySoundLocally(val, text, isInside, flag, flag2); } internal void b__2_3(ulong senderClientId, FastBufferReader reader) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); bool isInsideFactory = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInsideFactory, default(ForPrimitives)); bool isInElevator = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInElevator, default(ForPrimitives)); bool isInHangarShipRoom = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInHangarShipRoom, default(ForPrimitives)); PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val2 == (Object)null)) { val2.TeleportPlayer(val, false, 0f, false, true); val2.isInsideFactory = isInsideFactory; val2.isInElevator = isInElevator; val2.isInHangarShipRoom = isInHangarShipRoom; NetworkTransform component = ((Component)val2).GetComponent(); if (component != null) { component.Teleport(val, ((Component)val2).transform.rotation, ((Component)val2).transform.localScale); } Plugin.Log.LogInfo((object)$"[Network] TeleportTo received from host -> {val}"); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Network] TeleportTo handler error: " + ex.Message)); } } internal void b__2_4(ulong senderClientId, FastBufferReader reader) { try { <>c__DisplayClass2_1 CS$<>8__locals9 = new <>c__DisplayClass2_1(); ((FastBufferReader)(ref reader)).ReadValueSafe(ref CS$<>8__locals9.lootId, false); ((FastBufferReader)(ref reader)).ReadValueSafe(ref CS$<>8__locals9.weatherId, false); ((FastBufferReader)(ref reader)).ReadValueSafe(ref CS$<>8__locals9.monsterId, false); GameEventData gameEventData = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == CS$<>8__locals9.lootId); GameEventData gameEventData2 = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == CS$<>8__locals9.weatherId); GameEventData gameEventData3 = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == CS$<>8__locals9.monsterId); if (gameEventData == null || gameEventData2 == null || gameEventData3 == null) { Plugin.Log.LogWarning((object)("[EventSync] Unknown event IDs from host: " + CS$<>8__locals9.lootId + " | " + CS$<>8__locals9.weatherId + " | " + CS$<>8__locals9.monsterId)); } else { Plugin.Log.LogInfo((object)("[EventSync] Received from host: " + gameEventData.Name + " | " + gameEventData2.Name + " | " + gameEventData3.Name)); RoundStartPatch.SetCurrentRoundEvents(new List { gameEventData, gameEventData2, gameEventData3 }); } } catch (Exception ex) { Plugin.Log.LogError((object)("[EventSync] Handler error: " + ex.Message)); } } } [CompilerGenerated] private sealed class <>c__DisplayClass2_1 { public string lootId; public string weatherId; public string monsterId; internal bool b__5(GameEventData e) { return e.Id == lootId; } internal bool b__6(GameEventData e) { return e.Id == weatherId; } internal bool b__7(GameEventData e) { return e.Id == monsterId; } } private static NetworkManager? _registeredNetManager; private static void OnClientDisconnect(ulong clientId) { Plugin.RegisteredModdedClients.Remove(clientId); Plugin.Log.LogInfo((object)$"[Network] Client disconnected. Cleaned up clientId: {clientId}"); } [HarmonyPatch("Start")] [HarmonyPostfix] public unsafe static void OnStart() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown NetworkManager netManager = NetworkManager.Singleton; if ((Object)(object)netManager == (Object)null || netManager.CustomMessagingManager == null) { return; } CustomMessagingManager customMessagingManager = netManager.CustomMessagingManager; object obj = <>c.<>9__2_0; if (obj == null) { HandleNamedMessageDelegate val = delegate(ulong senderClientId, FastBufferReader reader) { Plugin.Log.LogInfo((object)$"[Network] Received mod handshake pong from client ID: {senderClientId}"); Plugin.RegisteredModdedClients.Add(senderClientId); }; <>c.<>9__2_0 = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("RandomEvents_Pong", (HandleNamedMessageDelegate)obj); netManager.CustomMessagingManager.RegisterNamedMessageHandler("RandomEvents_Ping", (HandleNamedMessageDelegate)delegate(ulong senderClientId, FastBufferReader reader) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)"[Network] Received mod handshake ping from host. Replying with pong."); FastBufferWriter val5 = default(FastBufferWriter); ((FastBufferWriter)(ref val5))..ctor(4, (Allocator)2, -1); try { netManager.CustomMessagingManager.SendNamedMessage("RandomEvents_Pong", senderClientId, val5, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val5))/*cast due to .constrained prefix*/).Dispose(); } }); CustomMessagingManager customMessagingManager2 = netManager.CustomMessagingManager; object obj2 = <>c.<>9__2_2; if (obj2 == null) { HandleNamedMessageDelegate val2 = delegate(ulong senderClientId, FastBufferReader reader) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Vector3 val5 = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val5); string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); bool isInside = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInside, default(ForPrimitives)); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); bool flag2 = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag2, default(ForPrimitives)); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Client received ghost request: {text} at {val5}, hunt: {flag}, visualOnly: {flag2}"); GhostlyWorldController.PlaySoundLocally(val5, text, isInside, flag, flag2); }; <>c.<>9__2_2 = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("RandomEvents_PlayGhostlySound", (HandleNamedMessageDelegate)obj2); CustomMessagingManager customMessagingManager3 = netManager.CustomMessagingManager; object obj3 = <>c.<>9__2_3; if (obj3 == null) { HandleNamedMessageDelegate val3 = delegate(ulong senderClientId, FastBufferReader reader) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val5 = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val5); bool isInsideFactory = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInsideFactory, default(ForPrimitives)); bool isInElevator = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInElevator, default(ForPrimitives)); bool isInHangarShipRoom = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref isInHangarShipRoom, default(ForPrimitives)); PlayerControllerB val6 = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val6 == (Object)null)) { val6.TeleportPlayer(val5, false, 0f, false, true); val6.isInsideFactory = isInsideFactory; val6.isInElevator = isInElevator; val6.isInHangarShipRoom = isInHangarShipRoom; NetworkTransform component = ((Component)val6).GetComponent(); if (component != null) { component.Teleport(val5, ((Component)val6).transform.rotation, ((Component)val6).transform.localScale); } Plugin.Log.LogInfo((object)$"[Network] TeleportTo received from host -> {val5}"); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Network] TeleportTo handler error: " + ex.Message)); } }; <>c.<>9__2_3 = val3; obj3 = (object)val3; } customMessagingManager3.RegisterNamedMessageHandler("RandomEvents_TeleportTo", (HandleNamedMessageDelegate)obj3); CustomMessagingManager customMessagingManager4 = netManager.CustomMessagingManager; object obj4 = <>c.<>9__2_4; if (obj4 == null) { HandleNamedMessageDelegate val4 = delegate(ulong senderClientId, FastBufferReader reader) { try { string lootId = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref lootId, false); string weatherId = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref weatherId, false); string monsterId = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref monsterId, false); GameEventData gameEventData = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == lootId); GameEventData gameEventData2 = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == weatherId); GameEventData gameEventData3 = EventPool.AllEvents.FirstOrDefault((GameEventData e) => e.Id == monsterId); if (gameEventData == null || gameEventData2 == null || gameEventData3 == null) { Plugin.Log.LogWarning((object)("[EventSync] Unknown event IDs from host: " + lootId + " | " + weatherId + " | " + monsterId)); } else { Plugin.Log.LogInfo((object)("[EventSync] Received from host: " + gameEventData.Name + " | " + gameEventData2.Name + " | " + gameEventData3.Name)); RoundStartPatch.SetCurrentRoundEvents(new List { gameEventData, gameEventData2, gameEventData3 }); } } catch (Exception ex) { Plugin.Log.LogError((object)("[EventSync] Handler error: " + ex.Message)); } }; <>c.<>9__2_4 = val4; obj4 = (object)val4; } customMessagingManager4.RegisterNamedMessageHandler("RandomEvents_SyncEvents", (HandleNamedMessageDelegate)obj4); if (netManager.IsServer && (Object)(object)_registeredNetManager != (Object)(object)netManager) { if ((Object)(object)_registeredNetManager != (Object)null) { _registeredNetManager.OnClientDisconnectCallback -= OnClientDisconnect; } netManager.OnClientDisconnectCallback += OnClientDisconnect; _registeredNetManager = netManager; } } } [HarmonyPatch(typeof(HUDManager))] public static class SessionWelcomePatch { private static GameObject? _welcomePopup; [HarmonyPatch("Start")] [HarmonyPostfix] public static void OnHUDStart(HUDManager __instance) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) try { TextMeshProUGUI globalNotificationText = __instance.globalNotificationText; if ((Object)(object)globalNotificationText == (Object)null) { return; } if ((Object)(object)_welcomePopup != (Object)null) { Object.Destroy((Object)(object)_welcomePopup); } string text = "RANDOM EVENTS\n\nУспешно загружен!\nПлагин RandomEventsExtended v3.1.1 активен.\nМодификаторы будут применены при посадке.\n\nМагазин в терминале: \"shells N\" — патроны (70 кред/шт), \"boomstick N\" — дробовик (1500 кред/шт). Прилетают кораблём при посадке, как обычная покупка."; Canvas canvas = ((Graphic)globalNotificationText).canvas; Transform val = ((!((Object)(object)canvas != (Object)null)) ? ((TMP_Text)globalNotificationText).transform.parent : ((Component)canvas.rootCanvas).transform); TextMeshProUGUI obj = Object.Instantiate(globalNotificationText, val); _welcomePopup = ((Component)obj).gameObject; _welcomePopup.SetActive(true); _welcomePopup.transform.SetAsLastSibling(); ((Behaviour)obj).enabled = true; RectTransform rectTransform = ((TMP_Text)obj).rectTransform; rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchoredPosition = new Vector2(0f, 20f); rectTransform.sizeDelta = new Vector2(820f, 420f); ((Transform)rectTransform).localScale = Vector3.one; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).enableWordWrapping = true; ((TMP_Text)obj).overflowMode = (TextOverflowModes)0; ((TMP_Text)obj).fontSize = 22f; ((TMP_Text)obj).enableAutoSizing = false; ((TMP_Text)obj).richText = true; ((Graphic)obj).color = Color.white; Material fontMaterial = ((TMP_Text)obj).fontMaterial; if ((Object)(object)fontMaterial != (Object)null) { if (fontMaterial.HasProperty("_FaceColor")) { fontMaterial.SetColor("_FaceColor", Color.white); } if (fontMaterial.HasProperty("_OutlineColor")) { fontMaterial.SetColor("_OutlineColor", Color.black); } if (fontMaterial.HasProperty("_OutlineWidth")) { fontMaterial.SetFloat("_OutlineWidth", 0.15f); } } ((TMP_Text)obj).text = text; ((TMP_Text)obj).ForceMeshUpdate(false, false); _welcomePopup.AddComponent().life = 10f; Plugin.Log.LogInfo((object)"[HUD] Welcome loading popup spawned for 10 seconds."); } catch (Exception arg) { Plugin.Log.LogError((object)$"[HUD] Welcome popup error: {arg}"); } } } public static class EventConflictSystem { private static readonly Dictionary Tags = new Dictionary { { "scrap_rich_day", new string[1] { "loot_buff" } }, { "scrap_balanced_harvest", new string[1] { "loot_buff" } }, { "scrap_jackpot", new string[1] { "loot_buff" } }, { "scrap_feast", new string[1] { "loot_buff" } }, { "one_gold_rush", new string[1] { "loot_buff" } }, { "one_artifact_discovery", new string[1] { "loot_buff" } }, { "one_same_kind_bonanza", new string[1] { "loot_buff" } }, { "meteor_rain_of_riches", new string[1] { "loot_buff" } }, { "meteor_cosmic_treasure", new string[2] { "loot_buff", "monsters_zero" } }, { "meteor_star_gift", new string[2] { "loot_buff", "outdoor_nerf" } }, { "meteor_calm", new string[1] { "loot_buff" } }, { "weather_clear_harvest", new string[1] { "loot_buff" } }, { "weather_rainy_surplus", new string[1] { "loot_buff" } }, { "weather_calm_storm", new string[3] { "loot_buff", "outdoor_zero", "outdoor_nerf" } }, { "scrap_rush_hour", new string[3] { "loot_buff", "indoor_buff", "outdoor_buff" } }, { "scrap_poor_day", new string[1] { "loot_nerf" } }, { "scrap_warehouse", new string[1] { "loot_nerf" } }, { "scrap_cursed", new string[2] { "loot_nerf", "monsters_zero" } }, { "one_junk_avalanche", new string[1] { "loot_nerf" } }, { "special_company_audit", new string[1] { "loot_buff" } }, { "meteor_swarm", new string[2] { "loot_nerf", "outdoor_buff" } }, { "meteor_lockdown", new string[3] { "loot_nerf", "indoor_buff", "outdoor_zero" } }, { "meteor_fog_invasion", new string[2] { "loot_nerf", "outdoor_buff" } }, { "special_ghost_town", new string[3] { "loot_nerf", "loot_zero", "monsters_zero" } }, { "scrap_empty_moon", new string[3] { "loot_nerf", "loot_zero", "monsters_zero" } }, { "meteor_golden", new string[3] { "loot_buff", "loot_jackpot", "monsters_zero" } }, { "one_precious_item", new string[3] { "loot_buff", "loot_jackpot", "monsters_zero" } }, { "scrap_diamond_field", new string[2] { "loot_buff", "loot_jackpot" } }, { "monster_outside_only", new string[2] { "outdoor_buff", "indoor_zero" } }, { "special_swarm_outside", new string[2] { "outdoor_buff", "indoor_zero" } }, { "monster_giant_walk", new string[2] { "outdoor_buff", "indoor_zero" } }, { "meteor_impact_zone", new string[2] { "outdoor_buff", "indoor_buff" } }, { "monster_bird_migration", new string[1] { "outdoor_nerf" } }, { "special_fortress_inside", new string[2] { "indoor_buff", "outdoor_zero" } }, { "one_type_outside", new string[2] { "outdoor_nerf", "outdoor_buff" } }, { "one_type_inside", new string[2] { "indoor_nerf", "indoor_buff" } }, { "monster_bracken_party", new string[1] { "indoor_buff" } }, { "monster_spider_nest", new string[1] { "indoor_buff" } }, { "monster_hoarder_heaven", new string[1] { "indoor_buff" } }, { "monster_nutcracker_drill", new string[1] { "indoor_buff" } }, { "monster_snare_flea_farm", new string[1] { "indoor_buff" } }, { "monster_thumper_rally", new string[1] { "indoor_buff" } }, { "special_lights_out", new string[1] { "indoor_buff" } }, { "special_hard_day", new string[2] { "indoor_buff", "outdoor_buff" } }, { "special_extreme_day", new string[2] { "indoor_buff", "outdoor_buff" } }, { "scrap_high_stakes", new string[2] { "indoor_buff", "outdoor_buff" } }, { "monster_peaceful_planet", new string[1] { "monsters_zero" } }, { "special_easy_day", new string[1] { "monsters_nerf" } }, { "special_lucky_break", new string[1] { "monsters_nerf" } }, { "meteor_celestial_shower", new string[1] { "loot_nerf" } }, { "weather_fog_raid", new string[1] { "outdoor_buff" } }, { "weather_eclipse_bonus", new string[1] { "outdoor_buff" } }, { "special_daytime_rush", new string[1] { "outdoor_zero" } }, { "monster_coilhead_patrol", new string[2] { "outdoor_buff", "indoor_zero" } }, { "monster_spider_outdoors", new string[2] { "outdoor_buff", "indoor_zero" } }, { "monster_baboon_siege", new string[2] { "indoor_buff", "outdoor_zero" } }, { "monster_mech_incursion", new string[1] { "indoor_buff" } }, { "monster_giant_breach", new string[1] { "indoor_buff" } }, { "special_minefield", new string[1] { "outdoor_buff" } }, { "special_turret_hell", new string[1] { "indoor_buff" } } }; private static readonly Dictionary Conflicts = new Dictionary { { "loot_buff", new string[2] { "loot_nerf", "loot_zero" } }, { "loot_nerf", new string[2] { "loot_buff", "loot_jackpot" } }, { "loot_zero", new string[4] { "loot_buff", "loot_jackpot", "outdoor_buff", "indoor_buff" } }, { "loot_jackpot", new string[2] { "loot_nerf", "loot_zero" } }, { "monsters_zero", new string[5] { "outdoor_buff", "indoor_buff", "monsters_nerf", "outdoor_zero", "indoor_zero" } }, { "monsters_nerf", new string[3] { "outdoor_buff", "indoor_buff", "monsters_zero" } }, { "outdoor_buff", new string[4] { "outdoor_nerf", "outdoor_zero", "monsters_zero", "monsters_nerf" } }, { "outdoor_nerf", new string[1] { "outdoor_buff" } }, { "outdoor_zero", new string[1] { "outdoor_buff" } }, { "indoor_buff", new string[4] { "indoor_zero", "monsters_zero", "monsters_nerf", "indoor_nerf" } }, { "indoor_zero", new string[1] { "indoor_buff" } }, { "indoor_nerf", new string[1] { "indoor_buff" } } }; public static bool AnyHasTag(IEnumerable events, string tag) { foreach (GameEventData @event in events) { if (!Tags.TryGetValue(@event.Id, out string[] value)) { continue; } string[] array = value; for (int i = 0; i < array.Length; i++) { if (array[i] == tag) { return true; } } } return false; } public static List GetConflicting(IReadOnlyList pool, IEnumerable alreadySelected) { HashSet hashSet = new HashSet(); foreach (GameEventData item3 in alreadySelected) { if (Tags.TryGetValue(item3.Id, out string[] value)) { string[] array = value; foreach (string item in array) { hashSet.Add(item); } } } HashSet forbiddenTags = new HashSet(); foreach (string item4 in hashSet) { if (Conflicts.TryGetValue(item4, out string[] value2)) { string[] array = value2; foreach (string item2 in array) { forbiddenTags.Add(item2); } } } if (forbiddenTags.Count == 0) { return new List(); } List list = new List(); foreach (GameEventData item5 in pool) { if (Tags.TryGetValue(item5.Id, out string[] value3) && value3.Any((string t) => forbiddenTags.Contains(t))) { list.Add(item5); } } return list; } } [HarmonyPatch(typeof(RoundManager))] public static class RoundStartPatch { [HarmonyPatch(typeof(RoundManager), "SyncScrapValuesClientRpc")] public static class ScrapCountChatPatch { [HarmonyPostfix] public static void Postfix(RoundManager __instance) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { ((MonoBehaviour)__instance).StartCoroutine(SendScrapCountDelayed()); } } private static IEnumerator SendScrapCountDelayed() { yield return null; try { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { yield break; } int num = 0; int num2 = 0; GrabbableObject[] array = Object.FindObjectsOfType(); foreach (GrabbableObject val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.itemProperties == (Object)null) && val.itemProperties.isScrap && !val.isInShipRoom && !val.isInElevator) { num++; num2 += val.scrapValue; } } if (num != 0) { string text = $"Лут на карте: {num} пред. (~{num2} кред.)"; instance.AddTextToChatOnServer(ChatStyle.Soft(text), -1); Plugin.Log.LogInfo((object)$"[Chat] Map loot: {num} items, ~{num2} credits"); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Chat] ScrapCount error: " + ex.Message)); } } } private static GameObject? _popup; private static int _lastProcessedSeed = -1; public static List? CurrentRoundEvents { get; private set; } private static bool IsCompanyLevel(SelectableLevel? level) { if ((Object)(object)level == (Object)null) { return false; } string text = ((Object)level).name ?? ""; string text2 = level.sceneName ?? ""; if (!text.ToLower().Contains("gordion") && !text.ToLower().Contains("company")) { return text2.Equals("CompanyBuilding", StringComparison.OrdinalIgnoreCase); } return true; } [HarmonyPatch("GenerateNewLevelClientRpc")] [HarmonyPostfix] [HarmonyPriority(200)] public static void OnLevelGenerated(RoundManager __instance, int randomSeed, int levelID) { //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) if (_lastProcessedSeed == randomSeed) { Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Already processed level generation for seed {randomSeed}. Skipping duplicate execution."); return; } _lastProcessedSeed = randomSeed; Plugin.RoundSeed = randomSeed; Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Level generated with seed: {randomSeed}, level ID: {levelID}"); EventHelpers.RestoreOriginalLevelSettings(); SelectableLevel val = null; if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.levels != null && levelID >= 0 && levelID < StartOfRound.Instance.levels.Length) { val = StartOfRound.Instance.levels[levelID]; } bool flag = false; if ((Object)(object)val != (Object)null && IsCompanyLevel(val)) { flag = true; } else if ((Object)(object)__instance.currentLevel != (Object)null && IsCompanyLevel(__instance.currentLevel)) { flag = true; } else if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.currentLevel != (Object)null && IsCompanyLevel(StartOfRound.Instance.currentLevel)) { flag = true; } if (flag) { Plugin.Log.LogInfo((object)"[RandomEventsExtended] Landed on the Company building. Skipping events and notifications."); if ((Object)(object)_popup != (Object)null) { Object.Destroy((Object)(object)_popup); _popup = null; } return; } SelectableLevel val2 = val ?? __instance.currentLevel; ApplyBaseline(val2); if ((Object)(object)val2 != (Object)null) { EventBaseline.InsidePower = val2.maxEnemyPowerCount; EventBaseline.OutsidePower = val2.maxOutsideEnemyPowerCount; EventBaseline.DaytimePower = val2.maxDaytimeEnemyPowerCount; } if ((Object)(object)EventHelpers.RM != (Object)null) { EventBaseline.ScrapValue = EventHelpers.RM.scrapValueMultiplier; EventBaseline.ScrapAmount = EventHelpers.RM.scrapAmountMultiplier; } EventHelpers.RemoveThumper(val2); try { Random random = new Random(randomSeed); GameEventData gameEventData = EventBalancer.SelectEvent(EventPool.LootEvents, random, Plugin.Log); List conflicting = EventConflictSystem.GetConflicting(EventPool.WeatherEvents, new List { gameEventData }); if (conflicting.Count > 0) { Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Conflict system excluded {conflicting.Count} weather events due to tag conflicts with loot."); } GameEventData gameEventData2 = EventBalancer.SelectEvent(EventPool.WeatherEvents, random, Plugin.Log, conflicting); List list = new List { gameEventData, gameEventData2 }; List conflicting2 = EventConflictSystem.GetConflicting(EventPool.MonsterEvents, list); if (conflicting2.Count > 0) { Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Conflict system excluded {conflicting2.Count} monster events due to tag conflicts with loot/weather."); } foreach (GameEventData ev in list) { if (!conflicting2.Any((GameEventData e) => e.Id == ev.Id)) { conflicting2.Add(ev); } } GameEventData gameEventData3 = EventBalancer.SelectEvent(EventPool.MonsterEvents, random, Plugin.Log, conflicting2); Plugin.Log.LogInfo((object)("[RandomEventsExtended] Selected: Loot=[" + gameEventData.Id + "] Weather=[" + gameEventData2.Id + "] Monster=[" + gameEventData3.Id + "]")); List list2 = (CurrentRoundEvents = new List { gameEventData, gameEventData2, gameEventData3 }); if (!((NetworkBehaviour)__instance).IsServer) { return; } try { GhostlyWorldController ghostlyWorldController = Object.FindObjectOfType(); if ((Object)(object)ghostlyWorldController != (Object)null) { Object.Destroy((Object)(object)((Component)ghostlyWorldController).gameObject); } new GameObject("GhostlyWorldController").AddComponent(); Plugin.Log.LogInfo((object)"[RandomEventsExtended] GhostlyWorldController spawned — paranoia active this round."); } catch (Exception arg) { Plugin.Log.LogError((object)$"[RandomEventsExtended] Failed to spawn GhostlyWorldController: {arg}"); } foreach (GameEventData item in list2) { Plugin.Log.LogInfo((object)("[RandomEventsExtended] Applying gameplay modifications for: " + item.Id)); item.Effect?.Invoke(); } ClampFinalMultipliers(list2); SendEventsToChat(list2); DangerRating.SendToChat(val2, gameEventData, gameEventData2, gameEventData3); bool lootAvailable = !EventConflictSystem.AnyHasTag(list2, "loot_zero"); bool monstersAvailable = !EventConflictSystem.AnyHasTag(list2, "monsters_zero"); BountySystem.NewRound(new Random(randomSeed + 777), val2, lootAvailable, monstersAvailable); HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer(ChatStyle.Soft("\ud83d\udccb ЗАДАНИЕ: " + BountySystem.Description + "\n" + $"Награда: +{BountySystem.RewardCredits} кред. (!bounty для статуса)"), -1); } MidRoundSystem.Reset(); ((MonoBehaviour)__instance).StartCoroutine(MidRoundSystem.RunCoroutine(randomSeed)); ((MonoBehaviour)__instance).StartCoroutine(SendEventNotificationsAfterDelay(list2)); if (gameEventData2.WeatherOverride.HasValue) { EventHelpers.SetWeather(gameEventData2.WeatherOverride.Value); Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Weather applied on host: '{gameEventData2.Id}' → {gameEventData2.WeatherOverride.Value}"); } ShowEventCard(list2); BroadcastSelectedEvents(gameEventData.Id, gameEventData2.Id, gameEventData3.Id); } catch (Exception arg2) { Plugin.Log.LogError((object)$"[RandomEventsExtended] Error applying events: {arg2}"); } } private static void ClampFinalMultipliers(List events) { if ((Object)(object)EventHelpers.RM == (Object)null) { return; } bool flag = false; foreach (GameEventData @event in events) { if (@event.Id == "meteor_golden" || @event.Id == "one_precious_item" || @event.Id == "scrap_diamond_field" || @event.Id == "scrap_jackpot" || @event.Id == "scrap_empty_moon") { flag = true; break; } } float num = EventHelpers.RM.scrapValueMultiplier; float num2 = EventHelpers.RM.scrapAmountMultiplier; if (!flag) { if (num > 2.5f) { num = 2.5f; } if (num2 > 2f) { num2 = 2f; } if (num < 0.5f) { num = 0.5f; } if (num2 < 0.5f) { num2 = 0.5f; } } else { if (num > 4f) { num = 4f; } if (num < 0.5f) { num = 0.5f; } } EventHelpers.RM.scrapValueMultiplier = num; EventHelpers.RM.scrapAmountMultiplier = num2; Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Final clamped scrap multipliers -> Value: {num:F2}x, Amount: {num2:F2}x"); } private static void ApplyBaseline(SelectableLevel? level) { if (!((Object)(object)level == (Object)null)) { float value = ModConfig.BaselineInside.Value; float value2 = ModConfig.BaselineOutside.Value; float value3 = ModConfig.BaselineDaytime.Value; level.maxEnemyPowerCount = Mathf.Max(1, Mathf.RoundToInt((float)level.maxEnemyPowerCount * value)); level.maxOutsideEnemyPowerCount = Mathf.Max(1, Mathf.RoundToInt((float)level.maxOutsideEnemyPowerCount * value2)); level.maxDaytimeEnemyPowerCount = Mathf.Max(1, Mathf.RoundToInt((float)level.maxDaytimeEnemyPowerCount * value3)); Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Baseline spawn boost applied (in x{value}, out x{value2}, day x{value3}). Max power - In: {level.maxEnemyPowerCount}, Out: {level.maxOutsideEnemyPowerCount}, Day: {level.maxDaytimeEnemyPowerCount}"); } } private static string RarityColor(GameEventRarity r) { return r switch { GameEventRarity.Good => "#4DFF6E", GameEventRarity.Neutral => "#FFE44D", GameEventRarity.VeryBad => "#FF7A35", GameEventRarity.Insane => "#FF2E2E", _ => "#FFFFFF", }; } private static void SendEventsToChat(List events) { try { HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null)) { IEnumerable values = events.Select((GameEventData ev) => "" + ev.Name + ""); string text = string.Join(" ", values); instance.AddTextToChatOnServer(ChatStyle.Soft(text), -1); Plugin.Log.LogInfo((object)("[Chat] Sent: " + text)); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Chat] SendEventsToChat error: " + ex.Message)); } } private static void ShowEventCard(List events) { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) try { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { return; } TextMeshProUGUI globalNotificationText = instance.globalNotificationText; if ((Object)(object)globalNotificationText == (Object)null) { Plugin.Log.LogWarning((object)"[HUD] globalNotificationText is null."); return; } if ((Object)(object)_popup != (Object)null) { Object.Destroy((Object)(object)_popup); } string text = "СОБЫТИЯ ДНЯ\n\n"; foreach (GameEventData @event in events) { string text2 = @event.Rarity switch { GameEventRarity.Good => "#5BE35B", GameEventRarity.Neutral => "#E3E35B", GameEventRarity.VeryBad => "#E39B5B", GameEventRarity.Insane => "#FF3030", _ => "#FFFFFF", }; text = text + "" + @event.Name + "\n" + @event.Description + "\n\n"; } Plugin.Log.LogInfo((object)("=== СОБЫТИЯ ДНЯ ===\n" + text)); Canvas canvas = ((Graphic)globalNotificationText).canvas; Transform val = ((!((Object)(object)canvas != (Object)null)) ? ((TMP_Text)globalNotificationText).transform.parent : ((Component)canvas.rootCanvas).transform); TextMeshProUGUI obj = Object.Instantiate(globalNotificationText, val); _popup = ((Component)obj).gameObject; _popup.SetActive(true); _popup.transform.SetAsLastSibling(); ((Behaviour)obj).enabled = true; RectTransform rectTransform = ((TMP_Text)obj).rectTransform; rectTransform.anchorMin = new Vector2(1f, 0.5f); rectTransform.anchorMax = new Vector2(1f, 0.5f); rectTransform.pivot = new Vector2(1f, 0.5f); rectTransform.anchoredPosition = new Vector2(-12f, 0f); rectTransform.sizeDelta = new Vector2(340f, 260f); ((Transform)rectTransform).localScale = Vector3.one; ((TMP_Text)obj).alignment = (TextAlignmentOptions)516; ((TMP_Text)obj).enableWordWrapping = true; ((TMP_Text)obj).overflowMode = (TextOverflowModes)0; ((TMP_Text)obj).fontSize = 15f; ((TMP_Text)obj).enableAutoSizing = false; ((TMP_Text)obj).richText = true; ((Graphic)obj).color = Color.white; Material fontMaterial = ((TMP_Text)obj).fontMaterial; if ((Object)(object)fontMaterial != (Object)null) { if (fontMaterial.HasProperty("_FaceColor")) { fontMaterial.SetColor("_FaceColor", Color.white); } if (fontMaterial.HasProperty("_OutlineColor")) { fontMaterial.SetColor("_OutlineColor", Color.black); } if (fontMaterial.HasProperty("_OutlineWidth")) { fontMaterial.SetFloat("_OutlineWidth", 0.15f); } } ((TMP_Text)obj).text = text.TrimEnd(); ((TMP_Text)obj).ForceMeshUpdate(false, false); _popup.AddComponent().life = 9f; } catch (Exception arg) { Plugin.Log.LogError((object)$"[HUD] Popup error: {arg}"); } } private unsafe static void QueryClientsForMod() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) Plugin.RegisteredModdedClients.Clear(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || !singleton.IsServer) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { Plugin.Log.LogInfo((object)$"[Network] Sending mod handshake ping to client ID: {connectedClientsId}"); singleton.CustomMessagingManager.SendNamedMessage("RandomEvents_Ping", connectedClientsId, val, (NetworkDelivery)3); } } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static IEnumerator SendEventNotificationsAfterDelay(List events) { QueryClientsForMod(); yield return (object)new WaitForSeconds(2.5f); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { yield break; } bool flag = false; foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId && !Plugin.RegisteredModdedClients.Contains(connectedClientsId)) { flag = true; break; } } Plugin.Log.LogInfo((object)$"[Network] Handshake check finished. Has vanilla clients: {flag}"); } public static void SetCurrentRoundEvents(List events) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) CurrentRoundEvents = events; GameEventData gameEventData = events.FirstOrDefault((GameEventData e) => e.WeatherOverride.HasValue); if (gameEventData != null && gameEventData.WeatherOverride.HasValue) { EventHelpers.SetWeather(gameEventData.WeatherOverride.Value); Plugin.Log.LogInfo((object)$"[EventSync] Weather synced from host: {gameEventData.WeatherOverride.Value} ({gameEventData.Id})"); } ShowEventCard(events); } private unsafe static void BroadcastSelectedEvents(string lootId, string weatherId, string monsterId) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return; } try { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(lootId, false); ((FastBufferWriter)(ref val)).WriteValueSafe(weatherId, false); ((FastBufferWriter)(ref val)).WriteValueSafe(monsterId, false); int num = 0; foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { singleton.CustomMessagingManager.SendNamedMessage("RandomEvents_SyncEvents", connectedClientsId, val, (NetworkDelivery)3); num++; } } Plugin.Log.LogInfo((object)$"[EventSync] Broadcast events → {num} client(s): {lootId} | {weatherId} | {monsterId}"); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[EventSync] BroadcastSelectedEvents failed: " + ex.Message)); } } private static string GetShortCode(string id, string name) { string text = id.ToLower(); if (text.Contains("ghostly")) { return "GHOST WORLD"; } if (text.Contains("audit")) { return "AUDIT"; } if (text.Contains("minefield")) { return "MINEFIELD"; } if (text.Contains("turret")) { return "TURRETS"; } if (text.Contains("lights")) { return "NO POWER"; } if (text.Contains("bracken") || text.Contains("party")) { return "BRACKENS"; } if (text.Contains("spider")) { return "SPIDERS"; } if (text.Contains("coilhead") || text.Contains("spring")) { return "COILHEADS"; } if (text.Contains("jester")) { return "JESTERS"; } if (text.Contains("dog")) { return "DOGS"; } if (text.Contains("giant")) { return "GIANTS"; } if (text.Contains("meteor")) { return "METEORS"; } if (text.Contains("fog")) { return "FOGGY"; } if (text.Contains("storm")) { return "STORM"; } if (text.Contains("flood")) { return "FLOOD"; } if (text.Contains("loot") || text.Contains("scrap") || text.Contains("riches") || text.Contains("treasure")) { return "MORE LOOT"; } if (text.Contains("kiwi")) { return "GIANT KIWI"; } if (text.Contains("puma")) { return "PUMA"; } string text2 = name.Replace("☄", "").Replace("⚠", "").Replace("‼", "") .Trim() .ToUpper(); if (text2.Length > 9) { return text2.Substring(0, 9); } return text2; } } internal static class ProceduralHorrorSounds { private const int SR = 44100; public static void Generate() { List customHorrorClips = GhostlyWorldController.CustomHorrorClips; Random rng = new Random(1337); for (int i = 0; i < 8; i++) { customHorrorClips.Add(MakeBreath(rng, i)); } for (int j = 0; j < 12; j++) { customHorrorClips.Add(MakeFootstep(rng, j)); } for (int k = 0; k < 6; k++) { customHorrorClips.Add(MakeCreak(rng, k)); } for (int l = 0; l < 3; l++) { customHorrorClips.Add(MakeHeartbeat(rng, l)); } for (int m = 0; m < 4; m++) { customHorrorClips.Add(MakeRumble(rng, m)); } for (int n = 0; n < 4; n++) { customHorrorClips.Add(MakeWaterDrip(rng, n)); } for (int num = 0; num < 4; num++) { customHorrorClips.Add(MakeStatic(rng, num)); } for (int num2 = 0; num2 < 4; num2++) { customHorrorClips.Add(MakeWind(rng, num2)); } for (int num3 = 0; num3 < 4; num3++) { customHorrorClips.Add(MakeScratch(rng, num3)); } for (int num4 = 0; num4 < 3; num4++) { customHorrorClips.Add(MakeWhisper(rng, num4)); } for (int num5 = 0; num5 < 2; num5++) { customHorrorClips.Add(MakeDistantBang(rng, num5)); } Plugin.Log.LogInfo((object)$"[ProceduralSounds] Сгенерировано {customHorrorClips.Count} хорор-клипов."); } private static float[] Noise(Random rng, int n) { float[] array = new float[n]; for (int i = 0; i < n; i++) { array[i] = (float)(rng.NextDouble() * 2.0 - 1.0); } return array; } private static float[] ASR(int n, float attack, float release) { float[] array = new float[n]; int num = (int)(attack * (float)n); int num2 = (int)(release * (float)n); int num3 = n - num - num2; if (num3 < 0) { num = n / 3; num2 = n / 3; num3 = n - num - num2; } for (int i = 0; i < num; i++) { array[i] = (float)i / (float)Math.Max(num, 1); } for (int j = num; j < num + num3; j++) { array[j] = 1f; } for (int k = num + num3; k < n; k++) { array[k] = Math.Max(0f, 1f - (float)(k - num - num3) / (float)Math.Max(num2, 1)); } return array; } private static float[] ExpDecay(int n, float tau) { float[] array = new float[n]; float num = 1f / (tau * 44100f); for (int i = 0; i < n; i++) { array[i] = (float)Math.Exp((float)(-i) * num); } return array; } private static float[] LowPass(float[] src, float hz) { float num = 1f / (MathF.PI * 2f * hz); float num2 = 2.2675737E-05f; float num3 = num2 / (num + num2); float[] array = new float[src.Length]; array[0] = src[0]; for (int i = 1; i < src.Length; i++) { array[i] = num3 * src[i] + (1f - num3) * array[i - 1]; } return array; } private static float[] HighPass(float[] src, float hz) { float num = 1f / (MathF.PI * 2f * hz); float num2 = 2.2675737E-05f; float num3 = num / (num + num2); float[] array = new float[src.Length]; array[0] = src[0]; for (int i = 1; i < src.Length; i++) { array[i] = num3 * (array[i - 1] + src[i] - src[i - 1]); } return array; } private static float[] BandPass(float[] src, float lowHz, float highHz) { return HighPass(LowPass(src, highHz), lowHz); } private static float[] Apply(float[] sig, float[] env) { float[] array = new float[sig.Length]; for (int i = 0; i < sig.Length; i++) { array[i] = sig[i] * ((i < env.Length) ? env[i] : 0f); } return array; } private static float[] Sine(int n, float hz) { float[] array = new float[n]; for (int i = 0; i < n; i++) { array[i] = Mathf.Sin(MathF.PI * 2f * hz * (float)i / 44100f); } return array; } private static float[] Mix(float[] a, float[] b, float ratio) { int num = Math.Max(a.Length, b.Length); float[] array = new float[num]; for (int i = 0; i < num; i++) { float num2 = ((i < a.Length) ? a[i] : 0f); float num3 = ((i < b.Length) ? b[i] : 0f); array[i] = num2 * (1f - ratio) + num3 * ratio; } return array; } private static float[] Normalize(float[] s, float targetPeak) { float num = 0f; for (int i = 0; i < s.Length; i++) { float num2 = Math.Abs(s[i]); if (num2 > num) { num = num2; } } if (num < 1E-06f) { return s; } float num3 = targetPeak / num; float[] array = new float[s.Length]; for (int j = 0; j < s.Length; j++) { array[j] = s[j] * num3; } return array; } private static AudioClip MakeClip(string name, float[] data) { AudioClip obj = AudioClip.Create(name, data.Length, 1, 44100, false); obj.SetData(data, 0); return obj; } private static AudioClip MakeBreath(Random rng, int v) { int n = (int)((1.8f + (float)rng.NextDouble() * 1.8f) * 44100f); float lowHz = 280f + (float)rng.NextDouble() * 200f; float highHz = 1400f + (float)rng.NextDouble() * 1100f; float[] a = BandPass(Noise(rng, n), lowHz, highHz); float[] b = Sine(n, 90f + (float)rng.NextDouble() * 30f); float[] sig = Mix(a, b, 0.12f); float[] env = ((v % 2 == 0) ? ASR(n, 0.25f, 0.5f) : ASR(n, 0.05f, 0.65f)); float targetPeak = 0.27f + (float)rng.NextDouble() * 0.12f; return MakeClip((v < 4) ? $"breath_in_{v + 1}" : $"breath_out_{v - 3}", Normalize(Apply(sig, env), targetPeak)); } private static AudioClip MakeFootstep(Random rng, int v) { string[] array = new string[12] { "concrete", "concrete", "concrete", "wood", "wood", "wood", "metal", "metal", "metal", "soft", "soft", "soft" }; string text = array[v % array.Length]; int n = (int)(((text == "metal") ? (0.32f + (float)rng.NextDouble() * 0.08f) : (0.14f + (float)rng.NextDouble() * 0.07f)) * 44100f); float[] src = Noise(rng, n); float[] s; switch (text) { case "wood": { float hz2 = 100f + (float)rng.NextDouble() * 60f; float[] a2 = LowPass(src, 900f); float[] b2 = Sine(n, hz2); s = Apply(Mix(a2, b2, 0.38f), ExpDecay(n, 0.038f)); break; } case "metal": { float hz = 340f + (float)rng.NextDouble() * 160f; float[] a = BandPass(src, 150f, 3200f); float[] b = Sine(n, hz); s = Apply(Mix(a, b, 0.52f), ExpDecay(n, 0.065f)); break; } case "soft": s = Apply(LowPass(src, 380f), ExpDecay(n, 0.022f)); break; default: s = Apply(LowPass(src, 1300f), ExpDecay(n, 0.032f)); break; } return MakeClip($"step_{text}_{v % 3 + 1}", Normalize(s, 0.55f)); } private static AudioClip MakeCreak(Random rng, int v) { int num = (int)((0.45f + (float)rng.NextDouble() * 1.2f) * 44100f); float num2 = 75f + (float)rng.NextDouble() * 90f; float num3 = 170f + (float)rng.NextDouble() * 140f; if (v % 2 == 1) { float num4 = num2; num2 = num3; num3 = num4; } float[] array = new float[num]; float num5 = 0f; for (int i = 0; i < num; i++) { float num6 = (float)i / (float)num; float num7 = num2 + (num3 - num2) * num6 * num6; num5 += MathF.PI * 2f * num7 / 44100f; array[i] = Mathf.Sin(num5); } float[] b = BandPass(Noise(rng, num), 60f, 600f); float[] s = Apply(Mix(array, b, 0.3f), ASR(num, 0.08f, 0.35f)); string arg = ((v < 3) ? "creak_floor" : "creak_door"); return MakeClip($"{arg}_{v % 3 + 1}", Normalize(s, 0.48f)); } private static AudioClip MakeHeartbeat(Random rng, int v) { float num = (new float[3] { 52f, 72f, 96f })[v]; float num2 = 60f / num; int num3 = (int)(num2 * 2.5f * 44100f); float[] array = new float[num3]; float[] array2 = new float[4] { 0f, num2 * 0.38f, num2, num2 * 1.38f }; foreach (float num4 in array2) { int num5 = (int)(num4 * 44100f); int num6 = 3969; float num7 = 58f + (float)rng.NextDouble() * 22f; float num8 = (((int)(num4 / num2) % 2 == 0) ? 1f : 0.75f); for (int j = 0; j < num6 && num5 + j < num3; j++) { float num9 = (float)j / (float)num6; array[num5 + j] += num8 * Mathf.Sin(MathF.PI * 2f * num7 * (float)j / 44100f) * (float)Math.Exp((double)(0f - num9) * 9.0); } } return MakeClip((new string[3] { "heartbeat_slow", "heartbeat_normal", "heartbeat_fast" })[v], Normalize(array, 0.52f)); } private static AudioClip MakeRumble(Random rng, int v) { int num = (int)((2.5f + (float)rng.NextDouble() * 1.5f) * 44100f); float hz = 25f + (float)rng.NextDouble() * 35f; float[] a = Sine(num, hz); float[] b = LowPass(Noise(rng, num), 130f); float[] array = Mix(a, b, 0.38f); float num2 = 0.25f + (float)rng.NextDouble() * 0.45f; for (int i = 0; i < num; i++) { array[i] *= 0.65f + 0.35f * Mathf.Sin(MathF.PI * 2f * num2 * (float)i / 44100f); } array = Apply(array, ASR(num, 0.3f, 0.3f)); return MakeClip($"rumble_{v + 1}", Normalize(array, 0.38f)); } private static AudioClip MakeWaterDrip(Random rng, int v) { int num = (int)((0.35f + (float)rng.NextDouble() * 0.2f) * 44100f); float num2 = 550f + (float)rng.NextDouble() * 450f; float num3 = 0.22f + (float)rng.NextDouble() * 0.15f; float[] array = new float[num]; float num4 = 0f; for (int i = 0; i < num; i++) { float num5 = (float)i / (float)num; num4 += MathF.PI * 2f * (num2 * (1f - num5 * num3)) / 44100f; array[i] = Mathf.Sin(num4); } float[] b = BandPass(Noise(rng, num), 400f, 1800f); float[] s = Apply(Mix(array, b, 0.08f), ExpDecay(num, 0.048f)); return MakeClip($"drip_{v + 1}", Normalize(s, 0.44f)); } private static AudioClip MakeStatic(Random rng, int v) { int n = (int)((0.12f + (float)rng.NextDouble() * 0.38f) * 44100f); float lowHz = 180f + (float)rng.NextDouble() * 400f; float highHz = 2800f + (float)rng.NextDouble() * 3500f; float[] s = Apply(BandPass(Noise(rng, n), lowHz, highHz), ASR(n, 0.04f, 0.28f)); return MakeClip($"static_{v + 1}", Normalize(s, 0.33f)); } private static AudioClip MakeWind(Random rng, int v) { int num = (int)((2.2f + (float)rng.NextDouble() * 2f) * 44100f); float hz = 220f + (float)rng.NextDouble() * 240f; float[] array = LowPass(Noise(rng, num), hz); float num2 = 0.12f + (float)rng.NextDouble() * 0.35f; for (int i = 0; i < num; i++) { array[i] *= 0.45f + 0.55f * Mathf.Abs(Mathf.Sin(MathF.PI * num2 * (float)i / 44100f)); } float[] s = Apply(array, ASR(num, 0.28f, 0.28f)); return MakeClip($"wind_{v + 1}", Normalize(s, 0.38f)); } private static AudioClip MakeScratch(Random rng, int v) { int num = (int)((0.18f + (float)rng.NextDouble() * 0.22f) * 44100f); float[] array = HighPass(Noise(rng, num), 1600f + (float)rng.NextDouble() * 900f); float[] array2 = new float[num]; int num2 = 2 + rng.Next(4); int num3 = Math.Max(1, num / num2); for (int i = 0; i < num2; i++) { int num4 = i * num3 + rng.Next(num3 / 2); int num5 = 970; for (int j = 0; j < num5 && num4 + j < num; j++) { float num6 = (float)j / (float)num5; array2[num4 + j] += array[num4 + j] * (float)Math.Exp((double)(0f - num6) * 12.0); } } return MakeClip($"scratch_{v + 1}", Normalize(array2, 0.48f)); } private static AudioClip MakeWhisper(Random rng, int v) { int num = (int)((1.8f + (float)rng.NextDouble() * 1.8f) * 44100f); float lowHz = 480f + (float)rng.NextDouble() * 280f; float highHz = 1400f + (float)rng.NextDouble() * 600f; float[] array = BandPass(Noise(rng, num), lowHz, highHz); float num2 = 2.5f + (float)rng.NextDouble() * 3f; for (int i = 0; i < num; i++) { float num3 = (float)i * num2 / 44100f; float num4 = 0.25f + 0.75f * Mathf.Abs(Mathf.Sin(MathF.PI * num3)); array[i] *= num4; } float[] s = Apply(array, ASR(num, 0.18f, 0.22f)); return MakeClip($"whisper_{v + 1}", Normalize(s, 0.3f)); } private static AudioClip MakeDistantBang(Random rng, int v) { int n = (int)((0.7f + (float)rng.NextDouble() * 0.6f) * 44100f); float[] a = LowPass(Noise(rng, n), 700f); float hz = 75f + (float)rng.NextDouble() * 55f; float[] b = Sine(n, hz); float[] s = Apply(Mix(a, b, 0.28f), ExpDecay(n, 0.14f)); return MakeClip($"distant_bang_{v + 1}", Normalize(s, 0.48f)); } } [HarmonyPatch(typeof(StartOfRound), "IsClientFriendsWithHost")] public static class AlwaysFriendWithHostPatch { [HarmonyPostfix] public static void Postfix(ref bool __result) { __result = true; } } [HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")] public static class FixReviveDeadPlayersModelBug { [HarmonyPostfix] public static void Postfix(StartOfRound __instance) { try { PlayerControllerB[] allPlayerScripts = __instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !val.isPlayerDead) { if ((Object)(object)val.thisPlayerModel != (Object)null) { ((Renderer)val.thisPlayerModel).enabled = true; } if ((Object)(object)val.thisPlayerModelLOD1 != (Object)null) { ((Renderer)val.thisPlayerModelLOD1).enabled = true; } if ((Object)(object)val.thisPlayerModelLOD2 != (Object)null) { ((Renderer)val.thisPlayerModelLOD2).enabled = true; } if (((NetworkBehaviour)val).IsOwner && (Object)(object)val.thisPlayerModelArms != (Object)null) { ((Renderer)val.thisPlayerModelArms).enabled = true; } } } Plugin.Log.LogInfo((object)"[RevivePatches] FixRevive: model renderers restored"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[RevivePatches] FixRevive: " + ex.Message)); } } } internal static class ShellShop { internal const int PricePerShell = 70; internal const int MaxPerOrder = 12; private static Item? _shellItem; private static bool _lookupFailed; private static string _diagnostic = ""; private static readonly string[] _diagFragments = new string[10] { "shot", "shell", "ammo", "bullet", "round", "cartridge", "shotgun", "снаряд", "патрон", "дробов" }; internal static string LastDiagnostic => _diagnostic; internal static Item? FindShellItem() { if ((Object)(object)_shellItem != (Object)null) { return _shellItem; } if (_lookupFailed) { return null; } try { List list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null) { return null; } _shellItem = ((IEnumerable)list).FirstOrDefault((Func)((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName) && string.Equals(it.itemName, "Ammo", StringComparison.OrdinalIgnoreCase))); if ((Object)(object)_shellItem == (Object)null) { _shellItem = ((IEnumerable)list).FirstOrDefault((Func)((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName) && it.itemName.IndexOf("shotgun shell", StringComparison.OrdinalIgnoreCase) >= 0)); } if ((Object)(object)_shellItem == (Object)null) { _shellItem = ((IEnumerable)list).FirstOrDefault((Func)((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName) && (it.itemName.IndexOf("ammo", StringComparison.OrdinalIgnoreCase) >= 0 || it.itemName.IndexOf("shell", StringComparison.OrdinalIgnoreCase) >= 0))); } if ((Object)(object)_shellItem == (Object)null) { _lookupFailed = true; _diagnostic = BuildDiagnostic(list); Plugin.Log.LogWarning((object)("[ShellShop] Предмет патронов не найден в allItemsList. " + _diagnostic)); } else { Plugin.Log.LogInfo((object)("[ShellShop] Найден предмет для магазина: '" + _shellItem.itemName + "'.")); } return _shellItem; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ShellShop] Ошибка поиска предмета: " + ex.Message)); _lookupFailed = true; return null; } } private static string BuildDiagnostic(List items) { try { List list = items.Where((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName)).ToList(); List list2 = (from it in list where _diagFragments.Any((string f) => it.itemName.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0) select it.itemName).Distinct().ToList(); if (list2.Count > 0) { return "Похожие предметы в данных игры: " + string.Join(", ", list2); } List values = list.Select((Item it) => it.itemName).Distinct().Take(25) .ToList(); return $"Совпадений по ключевым словам нет (всего предметов: {list.Count}). Примеры имён: " + string.Join(", ", values); } catch (Exception ex) { return "(диагностика недоступна: " + ex.Message + ")"; } } internal static int EnsureBuyableIndex(Terminal terminal, Item shell) { if ((Object)(object)terminal == (Object)null || (Object)(object)shell == (Object)null) { return -1; } try { Item[] buyableItemsList = terminal.buyableItemsList; if (buyableItemsList != null) { for (int i = 0; i < buyableItemsList.Length; i++) { if ((Object)(object)buyableItemsList[i] == (Object)(object)shell) { return i; } } } Item[] array = buyableItemsList ?? Array.Empty(); Item[] array2 = (Item[])(object)new Item[array.Length + 1]; Array.Copy(array, array2, array.Length); array2[array.Length] = shell; int num = array.Length; BuyableVehicle[] buyableVehicles = terminal.buyableVehicles; int num2 = ((buyableVehicles != null) ? buyableVehicles.Length : 0); int[] itemSalesPercentages = terminal.itemSalesPercentages; int[] array3 = new int[array2.Length + num2]; for (int j = 0; j < array3.Length; j++) { array3[j] = 100; } if (itemSalesPercentages != null && itemSalesPercentages.Length == array.Length + num2) { Array.Copy(itemSalesPercentages, 0, array3, 0, array.Length); if (num2 > 0) { Array.Copy(itemSalesPercentages, array.Length, array3, array2.Length, num2); } } terminal.buyableItemsList = array2; terminal.itemSalesPercentages = array3; Plugin.Log.LogInfo((object)$"[ShellShop] '{shell.itemName}' добавлен в buyableItemsList[{num}] (товаров было {array.Length}, стало {array2.Length})."); return num; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ShellShop] Не удалось зарегистрировать товар в терминале: " + ex.Message)); return -1; } } internal static bool TryParseCommand(string raw, out int quantity) { quantity = 1; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Trim().ToLowerInvariant().Split(new char[4] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return false; } int num = 0; if (array[num] == "buy" || array[num] == "order" || array[num] == "купить" || array[num] == "заказать") { num++; } if (num >= array.Length) { return false; } string text = array[num].Trim('.', ',', '!', '?'); switch (text) { default: if (!(text == "снаряды")) { return false; } break; case "shell": case "shells": case "патрон": case "патрона": case "патронов": case "патроны": case "снаряд": case "снаряда": case "снарядов": break; } num++; if (num < array.Length && int.TryParse(array[num].Trim('.', ',', '!', '?'), out var result) && result > 0) { quantity = result; } return true; } internal static string ProcessPurchase(Terminal terminal, int requestedQty) { try { Item val = FindShellItem(); if ((Object)(object)val == (Object)null) { return "Ошибка: предмет с патронами не найден в данных игры." + (string.IsNullOrEmpty(LastDiagnostic) ? "" : ("\n" + LastDiagnostic + "")); } int num = EnsureBuyableIndex(terminal, val); if (num < 0) { return "Ошибка: не удалось зарегистрировать товар в магазине."; } int num2 = Mathf.Clamp(requestedQty, 1, 12); string text = ((num2 != requestedQty) ? $"\n(максимум {12} шт. за раз — заказ скорректирован)" : ""); int num3 = num2 * 70; if (terminal.groupCredits < num3) { return "Недостаточно кредитов компании.\n" + $"Нужно: {num3} | На счету: {terminal.groupCredits}"; } int[] array = new int[num2]; for (int i = 0; i < num2; i++) { array[i] = num; } int num4 = terminal.groupCredits - num3; int num5 = Mathf.Max(0, terminal.numberOfItemsInDropship) + num2; terminal.BuyItemsServerRpc(array, num4, num5); Plugin.Log.LogInfo((object)$"[ShellShop] Заказ оформлен хостом: {num2}x '{val.itemName}' за {num3} кред. (index={num}, остаток {num4})."); return "ЗАКАЗ ОФОРМЛЕН" + text + "\n" + $"{num2} x {val.itemName} (патроны для дробовика) — списано {num3} кред.\n" + "Прибудет на корабле при следующей посадке (как обычная покупка).\n" + $"Остаток на счету компании: {num4} кред."; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ShellShop] Ошибка при оформлении заказа: " + ex.Message)); return "Ошибка обработки заказа. Подробности — в логах хоста."; } } internal static string BuildInfoText(Terminal terminal, bool isHost) { Item val = FindShellItem(); string text = (((Object)(object)terminal != (Object)null) ? terminal.groupCredits.ToString() : "?"); string text2 = (isHost ? "Введите \"shells <количество>\", например: shells 4" : "Заказ может оформить только хост лобби — попросите его ввести команду."); string text3 = (((Object)(object)val != (Object)null) ? "" : ("\n⚠ Товар не найден в данных игры — заказ временно недоступен." + (string.IsNullOrEmpty(LastDiagnostic) ? "" : ("\n" + LastDiagnostic + "")))); string text4 = (((Object)(object)val != (Object)null) ? val.itemName.ToUpperInvariant() : "ПАТРОНЫ ДЛЯ ДРОБОВИКА"); return "МАГАЗИН: " + text4 + "\n" + $"Цена: {70} кред./шт. (лимит {12} за раз)\n" + text2 + "\nНа счету компании: " + text + " кред." + text3; } } internal static class ShotgunShop { internal const int Price = 1500; internal const int MaxPerOrder = 12; private static Item? _gunItem; private static bool _lookupFailed; private static string _diagnostic = ""; private static readonly string[] _diagFragments = new string[7] { "shotgun", "gun", "weapon", "rifle", "дробов", "ружь", "оруж" }; internal static string LastDiagnostic => _diagnostic; internal static Item? FindGunItem() { if ((Object)(object)_gunItem != (Object)null) { return _gunItem; } if (_lookupFailed) { return null; } try { List list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null) { return null; } _gunItem = ((IEnumerable)list).FirstOrDefault((Func)((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName) && string.Equals(it.itemName, "Shotgun", StringComparison.OrdinalIgnoreCase))); if ((Object)(object)_gunItem == (Object)null) { _gunItem = ((IEnumerable)list).FirstOrDefault((Func)((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName) && it.itemName.IndexOf("shotgun", StringComparison.OrdinalIgnoreCase) >= 0)); } if ((Object)(object)_gunItem == (Object)null) { _lookupFailed = true; _diagnostic = BuildDiagnostic(list); Plugin.Log.LogWarning((object)("[ShotgunShop] Предмет дробовика не найден в allItemsList. " + _diagnostic)); } else { Plugin.Log.LogInfo((object)("[ShotgunShop] Найден предмет для магазина: '" + _gunItem.itemName + "'.")); } return _gunItem; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ShotgunShop] Ошибка поиска предмета: " + ex.Message)); _lookupFailed = true; return null; } } private static string BuildDiagnostic(List items) { try { List list = items.Where((Item it) => (Object)(object)it != (Object)null && !string.IsNullOrEmpty(it.itemName)).ToList(); List list2 = (from it in list where _diagFragments.Any((string f) => it.itemName.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0) select it.itemName).Distinct().ToList(); if (list2.Count > 0) { return "Похожие предметы в данных игры: " + string.Join(", ", list2); } List values = list.Select((Item it) => it.itemName).Distinct().Take(25) .ToList(); return $"Совпадений по ключевым словам нет (всего предметов: {list.Count}). Примеры имён: " + string.Join(", ", values); } catch (Exception ex) { return "(диагностика недоступна: " + ex.Message + ")"; } } internal static bool TryParseCommand(string raw, out int quantity) { quantity = 1; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Trim().ToLowerInvariant().Split(new char[4] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return false; } int num = 0; if (array[num] == "buy" || array[num] == "order" || array[num] == "купить" || array[num] == "заказать") { num++; } if (num >= array.Length) { return false; } string text = array[num].Trim('.', ',', '!', '?'); switch (text) { default: if (!(text == "ружей")) { return false; } break; case "boomstick": case "boomsticks": case "scattergun": case "дробовик": case "дробовика": case "дробовики": case "дробовиков": case "ружье": case "ружьё": case "ружья": break; } num++; if (num < array.Length && int.TryParse(array[num].Trim('.', ',', '!', '?'), out var result) && result > 0) { quantity = result; } return true; } internal static string ProcessPurchase(Terminal terminal, int requestedQty) { try { Item val = FindGunItem(); if ((Object)(object)val == (Object)null) { return "Ошибка: дробовик не найден в данных игры." + (string.IsNullOrEmpty(LastDiagnostic) ? "" : ("\n" + LastDiagnostic + "")); } int num = ShellShop.EnsureBuyableIndex(terminal, val); if (num < 0) { return "Ошибка: не удалось зарегистрировать товар в магазине."; } int num2 = Mathf.Clamp(requestedQty, 1, 12); string text = ((num2 != requestedQty) ? $"\n(максимум {12} шт. за раз — заказ скорректирован)" : ""); int num3 = num2 * 1500; if (terminal.groupCredits < num3) { return "Недостаточно кредитов компании.\n" + $"Нужно: {num3} | На счету: {terminal.groupCredits}"; } int[] array = new int[num2]; for (int i = 0; i < num2; i++) { array[i] = num; } int num4 = terminal.groupCredits - num3; int num5 = Mathf.Max(0, terminal.numberOfItemsInDropship) + num2; terminal.BuyItemsServerRpc(array, num4, num5); Plugin.Log.LogInfo((object)$"[ShotgunShop] Заказ оформлен хостом: {num2}x '{val.itemName}' за {num3} кред. (index={num}, остаток {num4})."); return "ЗАКАЗ ОФОРМЛЕН" + text + "\n" + $"{num2} x {val.itemName} — списано {num3} кред.\n" + "Прибудет на корабле при следующей посадке (как обычная покупка).\n" + $"Остаток на счету компании: {num4} кред."; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ShotgunShop] Ошибка при оформлении заказа: " + ex.Message)); return "Ошибка обработки заказа. Подробности — в логах хоста."; } } internal static string BuildInfoText(Terminal terminal, bool isHost) { Item val = FindGunItem(); string text = (((Object)(object)terminal != (Object)null) ? terminal.groupCredits.ToString() : "?"); string text2 = (isHost ? "Введите \"boomstick <количество>\" или \"купить дробовик N\", например: boomstick 1" : "Заказ может оформить только хост лобби — попросите его ввести команду."); string text3 = (((Object)(object)val != (Object)null) ? "" : ("\n⚠ Товар не найден в данных игры — заказ временно недоступен." + (string.IsNullOrEmpty(LastDiagnostic) ? "" : ("\n" + LastDiagnostic + "")))); string text4 = (((Object)(object)val != (Object)null) ? val.itemName.ToUpperInvariant() : "ДРОБОВИК"); return "МАГАЗИН: " + text4 + "\n" + $"Цена: {1500} кред./шт. (лимит {12} за раз)\n" + text2 + "\nНа счету компании: " + text + " кред." + text3; } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] public static class TerminalShopCommandPatch { private static TerminalNode? _node; [HarmonyPrefix] public static bool Prefix(Terminal __instance, ref TerminalNode __result) { try { TMP_InputField screenText = __instance.screenText; if ((Object)(object)screenText == (Object)null) { return true; } string text = screenText.text ?? ""; int textAdded = __instance.textAdded; if (textAdded <= 0 || textAdded > text.Length) { return true; } string text2 = text.Substring(text.Length - textAdded); int quantity; bool flag = ShellShop.TryParseCommand(text2, out quantity); int quantity2; bool flag2 = ShotgunShop.TryParseCommand(text2, out quantity2); if (!flag && !flag2) { return true; } NetworkManager singleton = NetworkManager.Singleton; bool flag3 = singleton != null && singleton.IsServer; bool flag4 = text2.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).Length <= 1; string text3 = (flag ? (flag4 ? ShellShop.BuildInfoText(__instance, flag3) : ((!flag3) ? "Заказ патронов может оформить только хост лобби — попросите его ввести эту команду." : ShellShop.ProcessPurchase(__instance, quantity))) : (flag4 ? ShotgunShop.BuildInfoText(__instance, flag3) : ((!flag3) ? "Заказ дробовика может оформить только хост лобби — попросите его ввести эту команду." : ShotgunShop.ProcessPurchase(__instance, quantity2)))); __result = BuildResponseNode(text3); return false; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[TerminalShop] Ошибка перехвата команды терминала: " + ex.Message)); } return true; } private static TerminalNode BuildResponseNode(string text) { if ((Object)(object)_node == (Object)null) { _node = ScriptableObject.CreateInstance(); _node.buyItemIndex = -1; _node.buyVehicleIndex = -1; _node.buyRerouteToMoon = -1; _node.displayPlanetInfo = -1; _node.shipUnlockableID = -1; _node.creatureFileID = -1; _node.storyLogFileID = -1; _node.playSyncedClip = -1; _node.itemCost = 0; _node.maxCharactersToType = 0; _node.clearPreviousText = false; _node.isConfirmationNode = false; _node.buyUnlockable = false; _node.returnFromStorage = false; _node.overrideOptions = false; _node.acceptAnything = false; _node.loadImageSlowly = false; _node.persistentImage = false; _node.terminalEvent = ""; _node.creatureName = ""; _node.terminalOptions = Array.Empty(); } _node.displayText = "\n" + text + "\n\n"; return _node; } } }