using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RandomEvents")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("3.0.1.0")] [assembly: AssemblyInformationalVersion("3.0.1")] [assembly: AssemblyProduct("RandomEvents")] [assembly: AssemblyTitle("RandomEvents")] [assembly: AssemblyVersion("3.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RandomEventsExtended { public enum BountyType { KillEnemies, CollectLoot, SurviveAll, KillSpecific } public static class BountySystem { private static bool _deathOccurred; public static BountyType CurrentType { get; private set; } public static string Description { get; private set; } = ""; public static int TargetAmount { get; private set; } public static int RewardCredits { get; private set; } public static int Progress { get; private set; } public static bool IsCompleted { get; private set; } public static bool IsFailed { get; private set; } public static string SpecificEnemy { get; private set; } = ""; public static void NewRound(Random rng, SelectableLevel? level, bool lootAvailable = true, bool monstersAvailable = true) { Progress = 0; IsCompleted = false; IsFailed = false; _deathOccurred = false; List 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) { IReadOnlyList pool2 = pool; List<(GameEventData, int)> list = new List<(GameEventData, int)>(pool2.Count); int num = _history.TakeLast(12).Count((string id) => pool2.Any((GameEventData e) => e.Id == id && e.Rarity == GameEventRarity.Insane)); foreach (GameEventData ev in pool2) { if (exclude.Any((GameEventData e) => e.Id == ev.Id) || !ModConfig.IsEventEnabled(ev.Id)) { continue; } int num2 = BaseWeights[ev.Rarity]; if (ev.Id.StartsWith("meteor_", StringComparison.Ordinal)) { num2 = Math.Max(1, num2 / 3); } if (ev.Rarity == GameEventRarity.Insane && num >= 1) { continue; } int num3 = LastIndex(ev.Id); if (num3 >= 0) { int num4 = _history.Count - num3; if (num4 <= ev.CooldownRounds) { continue; } if (num4 <= ev.CooldownRounds * 2) { num2 = Math.Max(1, num2 / 2); } } list.Add((ev, num2)); } return list; } private static GameEventData WeightedPick(List<(GameEventData ev, int weight)> candidates, Random random) { int maxValue = candidates.Sum<(GameEventData, int)>(((GameEventData ev, int weight) c) => c.weight); int num = random.Next(0, maxValue); int num2 = 0; foreach (var candidate in candidates) { GameEventData item = candidate.ev; int item2 = candidate.weight; num2 += item2; if (num < num2) { return item; } } return candidates[candidates.Count - 1].ev; } private static int LastIndex(string id) { for (int num = _history.Count - 1; num >= 0; num--) { if (_history[num] == id) { return num; } } return -1; } private static void RecordEvent(string id) { _history.Add(id); while (_history.Count > MaxHistorySize) { _history.RemoveAt(0); } } } public enum GameEventRarity { Good, Neutral, VeryBad, Insane } public sealed class GameEventData { public string Id { get; } public string Name { get; } public string Description { get; } public GameEventRarity Rarity { get; } public Action Effect { get; } public int CooldownRounds { get; } public LevelWeatherType? WeatherOverride { get; } public GameEventData(string id, string name, string description, GameEventRarity rarity, Action effect, int cooldownRounds = -1, LevelWeatherType? weatherOverride = null) { Id = id; Name = name; Description = description; Rarity = rarity; Effect = effect; CooldownRounds = ((cooldownRounds >= 0) ? cooldownRounds : DefaultCooldown(rarity)); WeatherOverride = weatherOverride; } private static int DefaultCooldown(GameEventRarity rarity) { return rarity switch { GameEventRarity.Good => 4, GameEventRarity.Neutral => 5, GameEventRarity.VeryBad => 6, GameEventRarity.Insane => 12, _ => 5, }; } } internal static class EventHelpers { private class LevelSnapshot { public int minScrap; public int maxScrap; public int minTotalScrapValue; public int maxTotalScrapValue; public int maxEnemyPowerCount; public int maxOutsideEnemyPowerCount; public int maxDaytimeEnemyPowerCount; public LevelWeatherType currentWeather; public List 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) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown SelectableLevel level = Level; if (level?.spawnableMapObjects == null) { return; } SpawnableMapObject[] spawnableMapObjects = level.spawnableMapObjects; foreach (SpawnableMapObject val in spawnableMapObjects) { if ((Object)(object)val?.prefabToSpawn == (Object)null) { continue; } string text = ((Object)val.prefabToSpawn).name.ToLower(); if (text.Contains("landmine") || text.Contains("mine")) { AnimationCurve numberToSpawn = val.numberToSpawn; if (numberToSpawn != null) { Keyframe[] keys = numberToSpawn.keys; for (int j = 0; j < keys.Length; j++) { ref Keyframe reference = ref keys[j]; ((Keyframe)(ref reference)).value = ((Keyframe)(ref reference)).value * mineMult; } val.numberToSpawn = new AnimationCurve(keys); } } else { if (!text.Contains("turret")) { continue; } AnimationCurve numberToSpawn2 = val.numberToSpawn; if (numberToSpawn2 != null) { Keyframe[] keys2 = numberToSpawn2.keys; for (int k = 0; k < keys2.Length; k++) { ref Keyframe reference2 = ref keys2[k]; ((Keyframe)(ref reference2)).value = ((Keyframe)(ref reference2)).value * turretMult; } val.numberToSpawn = new AnimationCurve(keys2); } } } } internal static float GetMaxScrapValue(SelectableLevel? level) { if ((Object)(object)level == (Object)null) { return 3.8f; } string text = ((Object)level).name.ToLower(); string text2 = (level.PlanetName ?? "").ToLower(); if (text.Contains("experimentation") || text2.Contains("experimentation") || text.Contains("assurance") || text2.Contains("assurance") || text.Contains("vow") || text2.Contains("vow") || level.riskLevel.Contains("D") || level.riskLevel.Contains("C")) { return 1.8f; } if (text.Contains("march") || text2.Contains("march") || text.Contains("offense") || text2.Contains("offense") || text.Contains("adamance") || text2.Contains("adamance") || text.Contains("embry") || text2.Contains("embry") || level.riskLevel.Contains("B") || level.riskLevel.Contains("A")) { return 3f; } if (text.Contains("artifice") || text2.Contains("artifice")) { return 5f; } return 4f; } internal static float GetScaledScrapMultiplier(float targetMult) { if (targetMult <= 1f) { return targetMult; } float maxScrapValue = GetMaxScrapValue(Level); float num = targetMult - 1f; float num2 = maxScrapValue / 5f; return 1f + num * num2; } internal static void MultiplyScrapValue(float mult) { if (!((Object)(object)RM == (Object)null)) { float scaledScrapMultiplier = GetScaledScrapMultiplier(mult); float num = EventBaseline.ScrapValue * (scaledScrapMultiplier - 1f); RM.scrapValueMultiplier = Mathf.Max(0.05f, RM.scrapValueMultiplier + num); } } internal static void MultiplyScrapAmount(float mult) { if (!((Object)(object)RM == (Object)null)) { float num = EventBaseline.ScrapAmount * (mult - 1f); RM.scrapAmountMultiplier = Mathf.Max(0.05f, RM.scrapAmountMultiplier + num); } } internal static void SetScrap(float valueMult, float amountMult) { if (valueMult >= 0f) { MultiplyScrapValue(valueMult); } if (amountMult >= 0f) { MultiplyScrapAmount(amountMult); } } internal static void FilterScrapToSpecificItems(int minVal, int maxVal, params string[] nameFragments) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown SelectableLevel level = Level; if ((Object)(object)level == (Object)null) { return; } List 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 maxValue = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight); int num = rng.Next(0, maxValue); int num2 = 0; SpawnableEnemyWithRarity val = list[0]; foreach (var item2 in list2) { num2 += item2.Item2; if (num < num2) { (val, _) = item2; break; } } if ((Object)(object)val?.enemyType != (Object)null) { MakeEnemyTypesSpawnEverywhere(new List { 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 maxValue = list2.Sum<(SpawnableEnemyWithRarity, int)>(((SpawnableEnemyWithRarity enemy, int weight) item) => item.weight); int num = rng.Next(0, maxValue); int num2 = 0; SpawnableEnemyWithRarity val = list[0]; foreach (var item2 in list2) { num2 += item2.Item2; if (num < num2) { (val, _) = item2; break; } } if ((Object)(object)val?.enemyType != (Object)null) { MakeEnemyTypesSpawnEverywhere(new List { 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) { string[] nameFragments2 = nameFragments; SelectableLevel level = Level; if (level?.DaytimeEnemies == null || level.DaytimeEnemies.Count == 0 || !level.DaytimeEnemies.Any((SpawnableEnemyWithRarity e) => MatchesName(e.enemyType?.enemyName, nameFragments2))) { return; } foreach (SpawnableEnemyWithRarity daytimeEnemy in level.DaytimeEnemies) { daytimeEnemy.rarity = (MatchesName(daytimeEnemy.enemyType?.enemyName, nameFragments2) ? 100 : 0); } } internal static void FilterInsideToRandomOne(Random rng) { SelectableLevel level = Level; if (level?.Enemies == null || level.Enemies.Count == 0) { return; } List 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) { string name2 = name; if (name2 == null) { return false; } return fragments.Any((string f) => name2.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0); } internal static void ShowTip(string header, string body, bool isWarning = false) { try { HUDManager instance = HUDManager.Instance; if (instance != null) { instance.DisplayTip(header, body, isWarning, false, "LC_Tip1"); } } catch (Exception ex) { Plugin.Log.LogError((object)("[HUD] DisplayTip error: " + ex.Message)); } } } public static class EventPool { private static readonly GameEventData[] _meteor = new GameEventData[10] { E("meteor_fog_invasion", "⚠ Метеоритный туман", "Метеоритный дождь начался! Густой туман скрывает угрозы. Монстры рыщут снаружи в 3× больше, но добыча стоит на 30% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.SetWeather((LevelWeatherType)3); EventHelpers.MultiplyOutsidePower(3f); EventHelpers.MultiplyDaytimePower(2f); EventHelpers.SetScrap(1.3f, -1f); }, -1, (LevelWeatherType)3), E("meteor_golden", "☄ Золотой метеорит", "Метеорит принёс сокровище! На карте ровно ОДИН предмет высокой ценности (Золотой слиток или Касса стоимостью 1500-2200 кредитов). Монстров нет.", GameEventRarity.Insane, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.ZeroAllPower(); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.minScrap = 1; EventHelpers.Level.maxScrap = 1; EventHelpers.FilterScrapToSpecificItems(1500, 2200, "gold", "register"); } if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.scrapAmountMultiplier = 1f; EventHelpers.RM.scrapValueMultiplier = 1f; } }, 14), E("meteor_celestial_shower", "☄ Небесный душ", "Метеориты рассыпали добычу по всей карте! В 3× больше предметов, но каждый стоит дешевле.", GameEventRarity.Neutral, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.SetScrap(0.5f, 3f); }), E("meteor_rain_of_riches", "☄ Золотой дождь", "Метеориты несут богатства! Стоимость добычи на 25% выше, но её меньше. Монстры чуть активнее.", GameEventRarity.Good, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.SetAllPower(1.05f, 1.05f, 1.05f); EventHelpers.SetScrap(1.25f, 0.5f); }), E("meteor_swarm", "⚠ Метеоритный рой", "Рой метеоритов сопровождается ордой снаружи! В 4× больше внешних монстров. Добыча на 8% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.MultiplyOutsidePower(4f); EventHelpers.MultiplyDaytimePower(2f); EventHelpers.SetScrap(1.08f, -1f); }), E("meteor_lockdown", "⚠ Метеоритная блокада", "Метеоритный дождь загнал монстров внутрь! Снаружи безопасно, но в здании тройная угроза. Добыча на 15% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.TriggerMeteorShower(); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 0; EventHelpers.Level.maxDaytimeEnemyPowerCount = 0; } EventHelpers.MultiplyInsidePower(3f); EventHelpers.SetScrap(1.15f, -1f); }), E("meteor_calm", "☄ Мирный метеорит", "Метеоритный дождь прошёл без особых угроз. Небольшой бонус к добыче.", GameEventRarity.Neutral, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.SetScrap(1.07f, 1.04f); }), E("meteor_impact_zone", "‼ Зона удара", "ЭКСТРЕННОЕ ПРЕДУПРЕЖДЕНИЕ! Сильнейший метеоритный шторм! Лимит монстров на улице и в здании увеличен в 3 раза. Добыча на 25% дороже, её немного больше.", GameEventRarity.Insane, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.SetWeather((LevelWeatherType)2); EventHelpers.SetAllPower(3f, 3f, 2f); EventHelpers.SetScrap(1.25f, 1.2f); }, 12, (LevelWeatherType)2), E("meteor_cosmic_treasure", "☄ Космическое сокровище", "Метеоритный дождь принёс дары, а монстры в панике разбежались! Нет никого на улице и внутри. Добыча на 20% дороже.", GameEventRarity.Good, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.ZeroAllPower(); EventHelpers.SetScrap(1.2f, 0.7f); }), E("meteor_star_gift", "☄ Дар звёзд", "Метеориты принесли дары! Добыча немного дороже, внешняя угроза снижена вдвое.", GameEventRarity.Neutral, delegate { EventHelpers.TriggerMeteorShower(); EventHelpers.MultiplyOutsidePower(0.5f); EventHelpers.MultiplyDaytimePower(0.5f); EventHelpers.SetScrap(1.1f, -1f); }) }; private static readonly GameEventData[] _monsters = new GameEventData[10] { E("monster_bracken_party", "⚠ Вечеринка Лозы", "Внутри только Лозы и их в 2.5× больше обычного! Тихо, тихо... Не смотри им в глаза. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Bracken", "Flowerman"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyInsidePower(2.5f); EventHelpers.SetScrap(1.2f, -1f); }), E("monster_spider_nest", "⚠ Паучье гнездо", "Всё здание в паутине! Только пауки внутри в двойном количестве. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Spider", "Bunker"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyInsidePower(2f); EventHelpers.SetScrap(1.2f, -1f); }), E("monster_hoarder_heaven", "⚠ Рай скупердяев", "Жуки-накопители захватили ВСЮ добычу в 2.5× количестве! Победи их, чтобы вернуть своё. Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Hoarding", "Hoarder", "Loot Bug"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyInsidePower(2.5f); EventHelpers.SetScrap(1.3f, -1f); }), E("monster_bird_migration", "✓ Перелёт птиц", "Снаружи только безвредные птицы! Собирай добычу спокойно. Стоимость на 10% выше, нет дневных угроз.", GameEventRarity.Good, delegate { EventHelpers.FilterOutsideEnemies("Manticoil", "Docile Locust"); EventHelpers.FilterDaytimeEnemies("Manticoil", "Docile Locust", "Circuit Bee"); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 2; EventHelpers.Level.maxDaytimeEnemyPowerCount = 2; } EventHelpers.MultiplyInsidePower(0.7f); EventHelpers.SetScrap(1.1f, -1f); }), E("monster_giant_walk", "‼ Прогулка гигантов", "Лесные Великаны заполонили поверхность в 3.5× количестве! Внутри здания монстров в 3 раза меньше. Добыча на 30% дороже, если выживешь.", GameEventRarity.Insane, delegate { EventHelpers.FilterOutsideEnemies("Forest Keeper", "Giant"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyOutsidePower(3.5f); EventHelpers.MultiplyInsidePower(0.33f); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxDaytimeEnemyPowerCount = 0; } EventHelpers.SetScrap(1.3f, -1f); }, 12), E("monster_nutcracker_drill", "⚠ Учения щелкунчиков", "Военные щелкунчики контролируют всё здание! Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Nutcracker"); EventHelpers.BoostSpawnCurve(1.5f); SelectableLevel level3 = EventHelpers.Level; if ((Object)(object)level3 != (Object)null) { level3.maxEnemyPowerCount = Math.Min(level3.maxEnemyPowerCount, 8); } EventHelpers.SetScrap(1.3f, -1f); }), E("monster_snare_flea_farm", "⚠ Ферма членистоногих", "Прыгающие блохи везде и в тройном количестве! Добыча на 7% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Snare Flea", "Centipede"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyInsidePower(3f); EventHelpers.SetScrap(1.07f, -1f); }), E("monster_thumper_rally", "⚠ Ралли стукачей", "Стукачи носятся по коридорам в 3× количестве! Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideEnemies("Crawler", "Thumper"); EventHelpers.BoostSpawnCurve(); EventHelpers.MultiplyInsidePower(3f); EventHelpers.SetScrap(1.25f, -1f); }), E("monster_outside_only", "⚠ Снаружи все", "Все монстры вышли на улицу в 4× количестве! Внутри безопасно, снаружи — орда. Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate { if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxEnemyPowerCount = 0; } EventHelpers.MultiplyOutsidePower(4f); EventHelpers.MultiplyDaytimePower(3f); EventHelpers.SetScrap(1.25f, -1f); }), E("monster_peaceful_planet", "✓ Мирная планета", "Сегодня нет монстров! Наслаждайся спокойным сбором добычи. Добыча на 4% дороже.", GameEventRarity.Good, delegate { EventHelpers.ZeroAllPower(); EventHelpers.SetScrap(1.04f, -1f); }) }; private static readonly GameEventData[] _oneType = new GameEventData[8] { E("one_precious_item", "‼ Единственный артефакт", "На карте ОДИН предмет высокой ценности (1200-2000 кредитов). Найди его — квота закрыта. Монстров нет.", GameEventRarity.Insane, delegate { EventHelpers.ZeroAllPower(); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.minScrap = 1; EventHelpers.Level.maxScrap = 1; EventHelpers.FilterScrapToSingleRandomItem(1200, 2000); } if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.scrapAmountMultiplier = 1f; EventHelpers.RM.scrapValueMultiplier = 1f; } }, 14), E("one_type_inside", "≡ Монотонный бестиарий", "Внутри только один вид монстров (с повышенным шансом средних и опасных)! Можно адаптироваться. Добыча на 5% дороже.", GameEventRarity.Neutral, delegate { EventHelpers.FilterInsideToWeightedRandomOne(new Random(Plugin.RoundSeed + 7)); EventHelpers.BoostSpawnCurve(); EventHelpers.SetScrap(1.05f, -1f); }), E("one_type_outside", "≡ Один снаружи", "Снаружи только один вид монстров (с повышенным шансом средних и опасных). Предсказуемая угроза. Добыча на 5% дороже.", GameEventRarity.Neutral, delegate { EventHelpers.FilterOutsideToWeightedRandomOne(new Random(Plugin.RoundSeed + 13)); EventHelpers.BoostSpawnCurve(); EventHelpers.SetScrap(1.05f, -1f); }), E("one_uniform_pricing", "≡ Единая цена", "Все предметы стоят одинаково (от 67 до 228 кредитов)! Без сюрпризов — гарантированный доход. В 1.3× больше предметов.", GameEventRarity.Neutral, delegate { EventHelpers.SetAllScrapValuesRange(67, 228); if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.scrapValueMultiplier = 1f; RoundManager? rM = EventHelpers.RM; rM.scrapAmountMultiplier *= 1.3f; } }), E("one_gold_rush", "✓ Золотая лихорадка", "Только ценные предметы сегодня! Стоимость увеличена на 25%, но предметов мало.", GameEventRarity.Good, delegate { EventHelpers.SetScrap(1.25f, 0.5f); }), E("one_junk_avalanche", "≡ Горы хлама", "Дешёвого хлама завались! В 3× больше, но стоит дешевле.", GameEventRarity.Neutral, delegate { EventHelpers.SetScrap(0.5f, 3f); }), E("one_artifact_discovery", "✓ Открытие артефактов", "Сегодня только один тип предметов, зато каждый на 25% дороже обычного! Предметов вдвое меньше.", GameEventRarity.Good, delegate { EventHelpers.FilterScrapToOneRandomType(new Random(Plugin.RoundSeed + 55)); EventHelpers.SetScrap(1.25f, 0.5f); }), E("one_same_kind_bonanza", "≡ Монотонность", "Сегодня только один вид предметов — зато их на 40% больше.", GameEventRarity.Neutral, delegate { EventHelpers.FilterScrapToOneRandomType(new Random(Plugin.RoundSeed + 77)); EventHelpers.SetScrap(1f, 1.4f); }) }; private static readonly GameEventData[] _weather = new GameEventData[8] { E("weather_fog_raid", "⚠ Туманный рейд", "Густой туман скрыл орду снаружи! Видимость нулевая, монстров в 3×. Добыча на 30% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.SetWeather((LevelWeatherType)3); EventHelpers.MultiplyOutsidePower(3f); EventHelpers.SetScrap(1.3f, -1f); }, -1, (LevelWeatherType)3), E("weather_thunder_bonus", "⚡ Гроза богатств", "Штормовая погода, но добыча на 12% дороже. Берегись молний!", GameEventRarity.Neutral, delegate { EventHelpers.SetWeather((LevelWeatherType)2); EventHelpers.SetScrap(1.12f, -1f); }, -1, (LevelWeatherType)2), E("weather_flood_gold", "≡ Золотой потоп", "Наводнение! Трудно ходить, но добыча на 10% дороже.", GameEventRarity.Neutral, delegate { EventHelpers.SetWeather((LevelWeatherType)4); EventHelpers.SetScrap(1.1f, -1f); }, -1, (LevelWeatherType)4), E("weather_eclipse_bonus", "⚠ Затмение", "Полное затмение! Монстры снаружи ночью вдвое активнее, зато днём — необычно тихо. Добыча на 40% дороже — если выживешь.", GameEventRarity.Insane, delegate { EventHelpers.SetWeather((LevelWeatherType)5); EventHelpers.SetAllPower(1.2f, 2f, 0.5f); EventHelpers.SetScrap(1.4f, -1f); }, 10, (LevelWeatherType)5), E("weather_clear_harvest", "✓ Ясная жатва", "Отличная погода! Добычи на 8% больше и на 5% дороже.", GameEventRarity.Good, delegate { EventHelpers.SetWeather((LevelWeatherType)(-1)); EventHelpers.SetScrap(1.05f, 1.08f); }, -1, (LevelWeatherType)(-1)), E("weather_rainy_surplus", "✓ Дождливый урожай", "Дождь принёс удачу! В 1.12× больше предметов.", GameEventRarity.Good, delegate { EventHelpers.SetWeather((LevelWeatherType)1); EventHelpers.SetScrap(1.05f, 1.12f); }, -1, (LevelWeatherType)1), E("weather_dust_treasure", "≡ Пыльная буря", "Пыльные облака скрывают ценности! Добыча на 10% дороже.", GameEventRarity.Neutral, delegate { EventHelpers.SetWeather((LevelWeatherType)0); EventHelpers.SetScrap(1.1f, -1f); }, -1, (LevelWeatherType)0), E("weather_calm_storm", "✓ Спокойная гроза", "Буря отпугнула монстров с улицы! Снаружи никого, добыча на 25% дороже.", GameEventRarity.Good, delegate { EventHelpers.SetWeather((LevelWeatherType)2); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 0; EventHelpers.Level.maxDaytimeEnemyPowerCount = 0; } EventHelpers.SetScrap(1.25f, -1f); }, -1, (LevelWeatherType)2) }; private static readonly GameEventData[] _scrap = new GameEventData[12] { E("scrap_rich_day", "✓ Богатый день", "Все предметы стоят на 25% дороже, но их немного меньше.", GameEventRarity.Good, delegate { EventHelpers.SetScrap(1.25f, 0.7f); }), E("scrap_poor_day", "≡ Скудный день", "Дешёвые предметы (полцены), но их вдвое больше. Объём берёт своё.", GameEventRarity.Neutral, delegate { EventHelpers.SetScrap(0.5f, 2f); }), E("scrap_jackpot", "‼ Джекпот", "На 25% дороже, но предметов почти нет — лишь 20% от нормы. Ищи каждый угол.", GameEventRarity.Insane, delegate { EventHelpers.SetScrap(1.25f, 0.2f); }, 10), E("scrap_warehouse", "≡ Планета-склад", "Добычи завались! В 3× больше, но всё стоит дешевле.", GameEventRarity.Neutral, delegate { EventHelpers.SetScrap(0.5f, 3f); }), E("scrap_balanced_harvest", "≡ Сбалансированный урожай", "Обычный день с небольшим плюсом: добыча чуть дороже и чуть больше.", GameEventRarity.Neutral, delegate { EventHelpers.SetScrap(1.08f, 1.08f); }), E("scrap_diamond_field", "‼ Алмазное поле", "Несколько предметов колоссальной цены (~800-1100 кредитов). Найди их.", GameEventRarity.Insane, delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.scrapAmountMultiplier = 0.08f; EventHelpers.RM.scrapValueMultiplier = 10f; } }, 12), E("scrap_feast", "≡ Пир добычи", "Море добычи! В 1.25× больше, но каждый предмет стоит дешевле.", GameEventRarity.Neutral, delegate { EventHelpers.SetScrap(0.6f, 1.25f); }), E("scrap_cursed", "⚠ Проклятая добыча", "Всё проклято! Предметы стоят гроши (50% от обычной цены). Монстров нет — но и смысла мало.", GameEventRarity.VeryBad, delegate { EventHelpers.ZeroAllPower(); EventHelpers.SetScrap(0.5f, 1.2f); }), E("scrap_rush_hour", "‼ Час пик", "ВСЁ максимально! Монстры везде в 3× активнее. Добыча на 35% дороже и в 1.25× больше.", GameEventRarity.Insane, delegate { EventHelpers.SetScrap(1.35f, 1.25f); EventHelpers.SetAllPower(3f, 3f, 2f); }, 10), E("scrap_empty_moon", "⚠ Пустая луна", "Нет монстров, но и нет добычи. Бесполезный визит.", GameEventRarity.VeryBad, delegate { EventHelpers.ZeroAllPower(); if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.scrapAmountMultiplier = 0.03f; } }, 14), E("scrap_double_or_nothing", "‼ Удвоение или ноль", "50 на 50: либо добыча на 30% дороже и в 1.25× больше, либо 50% от обычной цены. Фортуна решает.", GameEventRarity.Insane, delegate { if (new Random(Plugin.RoundSeed + 99).Next(2) == 0) { EventHelpers.SetScrap(1.3f, 1.25f); Plugin.Log.LogInfo((object)"[Event] Удвоение — вам повезло!"); } else { EventHelpers.SetScrap(0.5f, -1f); Plugin.Log.LogInfo((object)"[Event] Ноль — вам не повезло!"); } }, 10), E("scrap_high_stakes", "⚠ Высокие ставки", "Тройные монстры везде, добыча на 25% дороже и в 1.25× больше. Высокий риск — высокая награда.", GameEventRarity.VeryBad, delegate { EventHelpers.SetAllPower(3f, 3f, 2.5f); EventHelpers.SetScrap(1.25f, 1.25f); }) }; private static readonly GameEventData[] _special = new GameEventData[17] { E("special_easy_day", "✓ Лёгкий день", "Монстры слабее сегодня! В 3× меньше мощи. Нормальная добыча.", GameEventRarity.Good, delegate { EventHelpers.SetAllPower(0.35f, 0.35f, 0.35f); }), E("special_hard_day", "⚠ Тяжёлый день", "Монстры втройне опасны везде! Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.SetAllPower(3f, 3f, 2.5f); EventHelpers.SetScrap(1.2f, -1f); }), E("special_extreme_day", "‼ Экстремальный день", "ОПАСНОСТЬ! В 4× больше монстров везде! Добыча на 30% дороже. Удачи.", GameEventRarity.Insane, delegate { EventHelpers.SetAllPower(4f, 4f, 3f); EventHelpers.SetScrap(1.3f, -1f); }, 10), E("special_swarm_outside", "⚠ Рой снаружи", "Снаружи орда в 4×! Внутри пусто. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxEnemyPowerCount = 0; } EventHelpers.MultiplyOutsidePower(4f); EventHelpers.MultiplyDaytimePower(2f); EventHelpers.SetScrap(1.2f, -1f); }), E("special_fortress_inside", "⚠ Крепость внутри", "Внутри настоящая крепость монстров в 4×! Снаружи пусто. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 0; EventHelpers.Level.maxDaytimeEnemyPowerCount = 0; } EventHelpers.MultiplyInsidePower(4f); EventHelpers.SetScrap(1.2f, -1f); }), E("special_daytime_rush", "≡ Дневной час пик", "Дневные монстры в 3× активнее! Ночью снаружи спокойно. Добыча на 12% дороже.", GameEventRarity.Neutral, delegate { EventHelpers.MultiplyDaytimePower(3f); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 0; } EventHelpers.SetScrap(1.12f, -1f); }), E("special_ghost_town", "≡ Город призраков", "Тихо как на кладбище. Нет монстров, но добыча на 30% дешевле, зато её на 40% больше.", GameEventRarity.Neutral, delegate { EventHelpers.ZeroAllPower(); EventHelpers.SetScrap(0.7f, 1.4f); }), E("special_lucky_break", "✓ Счастливый перерыв", "Сегодня вам повезло! Половина монстров, добыча на 12% дороже.", GameEventRarity.Good, delegate { EventHelpers.SetAllPower(0.5f, 0.5f, 0.5f); EventHelpers.SetScrap(1.12f, -1f); }), E("special_company_audit", "✓ Аудит Компании", "Компания проводит аудит на этой луне! Стоимость добычи увеличена на 12%.", GameEventRarity.Good, delegate { EventHelpers.SetScrap(1.12f, -1f); }), E("special_minefield", "⚠ Минное поле", "Внутри здания зафиксировано огромное количество активных мин! Стоимость добычи увеличена на 12%.", GameEventRarity.VeryBad, delegate { EventHelpers.MultiplyMapObjects(6f, 1f); EventHelpers.SetScrap(1.12f, -1f); }), E("special_turret_hell", "⚠ Турельный ад", "Охранные турели заполонили коридоры! Стоимость добычи увеличена на 20%.", GameEventRarity.VeryBad, delegate { EventHelpers.MultiplyMapObjects(1f, 6f); EventHelpers.SetScrap(1.2f, -1f); }), E("special_lights_out", "‼ Отключение питания", "Абсолютное отключение питания в комплексе! Внутри полная темнота, а Coil-head активнее.", GameEventRarity.Insane, delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { EventHelpers.RM.powerOffPermanently = true; } EventHelpers.FilterInsideEnemies("Coil", "Spring"); EventHelpers.MultiplyInsidePower(3f); }, 12), E("monster_coilhead_patrol", "⚠ Стальной дозор", "Катушки вырвались на поверхность! Они патрулируют территорию снаружи. Внутри — спокойно. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideToOutside("Coil", "Spring"); EventHelpers.BoostSpawnCurve(1.5f); EventHelpers.MultiplyOutsidePower(2.5f); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxEnemyPowerCount = 0; } EventHelpers.SetScrap(1.2f, -1f); }, 10), E("monster_spider_outdoors", "⚠ Внешняя паутина", "Пауки оплели всю территорию снаружи! Внутри комплекса безопасно. Добыча на 20% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterInsideToOutside("Spider", "Bunker"); EventHelpers.BoostSpawnCurve(1.5f); EventHelpers.MultiplyOutsidePower(2f); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxEnemyPowerCount = 0; } EventHelpers.SetScrap(1.2f, -1f); }, 10), E("monster_baboon_siege", "⚠ Осада бабуинов", "Стая бабуинов ворвалась в комплекс! Они охотятся стаями в коридорах. Снаружи пусто. Добыча на 25% дороже.", GameEventRarity.VeryBad, delegate { EventHelpers.FilterOutsideToInside("Baboon"); EventHelpers.BoostSpawnCurve(1.5f); EventHelpers.MultiplyInsidePower(2.5f); if ((Object)(object)EventHelpers.Level != (Object)null) { EventHelpers.Level.maxOutsideEnemyPowerCount = 0; } EventHelpers.SetScrap(1.25f, -1f); }, 12), E("monster_mech_incursion", "‼ Вторжение меха", "Механический страж взломал комплекс! Ракеты и огнемёт в коридорах. Добыча на 40% дороже — если выживешь.", GameEventRarity.Insane, delegate { EventHelpers.FilterOutsideToInside("Mech", "Old"); EventHelpers.BoostSpawnCurve(1.5f); SelectableLevel level2 = EventHelpers.Level; if ((Object)(object)level2 != (Object)null) { level2.maxEnemyPowerCount = Math.Min(level2.maxEnemyPowerCount, 8); } EventHelpers.SetScrap(1.4f, -1f); }, 14), E("monster_giant_breach", "‼ Гигант в здании", "Лесной Гигант ломится внутрь! Комплекс содрогается от его шагов. Он хватает всё что движется. Добыча на 40% дороже.", GameEventRarity.Insane, delegate { EventHelpers.FilterOutsideToInside("Giant", "Forest", "Keeper"); EventHelpers.BoostSpawnCurve(1.5f); SelectableLevel level = EventHelpers.Level; if ((Object)(object)level != (Object)null) { level.maxEnemyPowerCount = Math.Min(level.maxEnemyPowerCount, 6); } EventHelpers.SetScrap(1.4f, -1f); }, 14) }; private static IReadOnlyList? _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 readonly Dictionary _playerTimers = new Dictionary(); private static readonly BindingFlags _bfPub = BindingFlags.Instance | BindingFlags.Public; private static readonly string[] _dangerKeywords = new string[13] { "kill", "death", "die", "explod", "damage", "hurt", "attack", "grab", "finish", "sink", "fall", "ragdoll", "drown" }; private static readonly FieldInfo DoorOpenedField = typeof(DoorLock).GetField("isDoorOpened", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo TerminalDoorOpenField = typeof(TerminalAccessibleObject).GetField("isDoorOpen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly Dictionary> _clipCache = new Dictionary>(); private static readonly BindingFlags _bfAll = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private void Start() { Plugin.Log.LogInfo((object)"[GhostlyWorld] Controller created. Waiting for ship to land..."); } private void Update() { if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer || (Object)(object)StartOfRound.Instance == (Object)null) { return; } if (!_hasLanded) { if (!StartOfRound.Instance.inShipPhase) { _hasLanded = true; Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship landed. Paranoia active — per-player distance-based timers started."); } return; } if (StartOfRound.Instance.inShipPhase) { Plugin.Log.LogInfo((object)"[GhostlyWorld] Ship returned to orbit. Destroying GhostlyWorldController."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return; } List active = allPlayerScripts.Where((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled && !p.isPlayerDead).ToList(); for (int i = 0; i < allPlayerScripts.Length; i++) { PlayerControllerB val = allPlayerScripts[i]; if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead) { if (!_playerTimers.ContainsKey(i)) { _playerTimers[i] = GetInterval(val, active); } _playerTimers[i] -= Time.deltaTime; if (_playerTimers[i] <= 0f) { float interval = GetInterval(val, active); _playerTimers[i] = interval; Plugin.Log.LogInfo((object)$"[GhostlyWorld] Player {i} paranoia triggered. Next in {interval:F1}s (dist to team: {GetNearestTeammateDist(val, active):F0}m)"); ((MonoBehaviour)this).StartCoroutine(GhostlyEventForPlayer(val)); } } } } private static float GetInterval(PlayerControllerB player, List active) { int num = Mathf.FloorToInt(GetNearestTeammateDist(player, active) / 20f); float num2 = Mathf.Min(1f + (float)num * 0.5f, 4f); return Random.Range(30f, 60f) / num2; } private static float GetNearestTeammateDist(PlayerControllerB player, List 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 bool TryTriggerViaSpawnedEnemy(PlayerControllerB player) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) List list = RoundManager.Instance?.SpawnedEnemies; if (list == null || list.Count == 0) { return false; } bool isInside = player.isInsideFactory; List list2 = (from e in list where (Object)(object)e != (Object)null && !e.isEnemyDead && e.isOutside == !isInside select e into _ orderby Random.value select _).ToList(); if (list2.Count == 0) { list2 = (from e in list where (Object)(object)e != (Object)null && !e.isEnemyDead select e into _ orderby Random.value select _).ToList(); } if (list2.Count == 0) { return false; } EnemyAI val = list2[0]; Vector3 position = ((Component)val).transform.position; bool flag = TryInvokeAudioClientRpc(val); OpenNearbyDoors(position, 15f, Random.Range(1, 2)); SendPlaySoundMessage(position, val.enemyType?.enemyName ?? "", isInside, isHuntSFX: false); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Real-enemy paranoia: {val.enemyType?.enemyName} at {position}, soundRpc={flag}"); return true; } private static bool TryInvokeAudioClientRpc(EnemyAI enemy) { try { Type type = ((object)enemy).GetType(); List 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 i = m.Name.ToLower(); return (i.Contains("sound") || i.Contains("sfx") || i.Contains("noise") || i.Contains("idle") || i.Contains("stomp") || i.Contains("footstep") || i.Contains("growl") || i.Contains("moan") || i.Contains("roar") || i.Contains("chitter") || i.Contains("chuckle") || i.Contains("hiss") || i.Contains("breathe") || i.Contains("grunt") || i.Contains("step")) && !_dangerKeywords.Any((string bad) => i.Contains(bad)); }).ToList(); if (list.Count == 0) { list = source.Where((MethodInfo m) => !_dangerKeywords.Any((string bad) => m.Name.ToLower().Contains(bad))).ToList(); } if (list.Count == 0) { return false; } MethodInfo methodInfo = list[Random.Range(0, list.Count)]; methodInfo.Invoke(enemy, null); Plugin.Log.LogInfo((object)("[GhostlyWorld] Invoked " + type.Name + "." + methodInfo.Name + " → all clients")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[GhostlyWorld] ClientRpc invoke failed: " + ex.Message)); return false; } } private IEnumerator GhostlyFallback(PlayerControllerB player) { bool isInside = player.isInsideFactory; SelectableLevel val = StartOfRound.Instance?.currentLevel; if ((Object)(object)val == (Object)null) { yield break; } List list = (isInside ? 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 chosen = list2[Random.Range(0, list2.Count)]; Vector3 val2 = Random.onUnitSphere * Random.Range(8f, 18f); val2.y = 0f; Vector3 playPos = ((Component)player).transform.position + val2; NavMeshHit val3 = default(NavMeshHit); if (NavMesh.SamplePosition(playPos, ref val3, 15f, -1)) { playPos = ((NavMeshHit)(ref val3)).position; } SendPlaySoundMessage(playPos, chosen.enemyName, isInside, isHuntSFX: false); yield return (object)new WaitForSeconds(Random.Range(1.5f, 2.5f)); SendPlaySoundMessage(playPos, chosen.enemyName, isInside, isHuntSFX: true); OpenNearbyDoors(playPos, 15f, Random.Range(1, 3)); } } private void SendPlaySoundMessage(Vector3 position, string enemyName, bool isInside, bool isHuntSFX) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(100, (Allocator)2, -1); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((FastBufferWriter)(ref val)).WriteValueSafe(enemyName, false); ((FastBufferWriter)(ref val)).WriteValueSafe(ref isInside, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref isHuntSFX, default(ForPrimitives)); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { singleton.CustomMessagingManager.SendNamedMessage("RandomEvents_PlayGhostlySound", connectedClientsId, val, (NetworkDelivery)3); } } private bool GetIsDoorOpened(DoorLock door) { if ((Object)(object)door != (Object)null && DoorOpenedField != null) { return (bool)DoorOpenedField.GetValue(door); } return false; } private bool GetIsTerminalDoorOpen(TerminalAccessibleObject bDoor) { if ((Object)(object)bDoor != (Object)null && TerminalDoorOpenField != null) { return (bool)TerminalDoorOpenField.GetValue(bDoor); } return false; } private void OpenNearbyDoors(Vector3 center, float radius, int count) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(center, radius); int num = 0; List 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) { //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Expected O, but got Unknown //IL_0355: Unknown result type (might be due to invalid IL or missing references) EnemyType val = FindEnemyTypeByName(enemyName); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("[GhostlyWorld] Could not find EnemyType for name: " + enemyName)); return; } List list = GetEnemyAudioClips(val); if (list.Count == 0) { SelectableLevel val2 = StartOfRound.Instance?.currentLevel; List list2 = new List(); if (val2?.Enemies != null) { list2.AddRange(val2.Enemies); } if (val2?.OutsideEnemies != null) { list2.AddRange(val2.OutsideEnemies); } Random rng = new Random(); list2 = list2.OrderBy((SpawnableEnemyWithRarity _) => rng.Next()).ToList(); foreach (SpawnableEnemyWithRarity item in list2) { if (!((Object)(object)item?.enemyType == (Object)null) && !((Object)(object)item.enemyType == (Object)(object)val)) { List enemyAudioClips = GetEnemyAudioClips(item.enemyType); if (enemyAudioClips.Count > 0) { list = enemyAudioClips; val = item.enemyType; Plugin.Log.LogInfo((object)("[GhostlyWorld] Clip fallback: using " + val.enemyName + " instead.")); break; } } } } if (list.Count == 0) { return; } AudioClip val3 = null; AudioClip val4 = null; foreach (AudioClip item2 in list) { if (!((Object)(object)item2 == (Object)null)) { string text = ((Object)item2).name.ToLower(); if (text.Contains("chase") || text.Contains("hunt") || text.Contains("scream") || text.Contains("growl") || text.Contains("lunge") || text.Contains("charge")) { val4 = item2; } else if (text.Contains("foot") || text.Contains("step") || text.Contains("walk") || text.Contains("run") || text.Contains("move") || text.Contains("crawl") || text.Contains("skitter")) { val3 = item2; } } } if ((Object)(object)val3 == (Object)null && list.Count > 0) { val3 = list[0]; } if ((Object)(object)val4 == (Object)null && list.Count > 1) { val4 = list[1]; } if ((Object)(object)val4 == (Object)null) { val4 = val3; } AudioClip val5 = (isHuntSFX ? val4 : val3); if (!((Object)(object)val5 == (Object)null)) { Plugin.Log.LogInfo((object)$"[GhostlyWorld] Playing clip '{((Object)val5).name}' for {enemyName} at {playPos}"); GameObject val6 = new GameObject("TempGhostlyAudio"); val6.transform.position = playPos; AudioSource val7 = val6.AddComponent(); val7.clip = val5; val7.spatialBlend = 1f; val7.minDistance = 6f; val7.maxDistance = 50f; val7.volume = 1f; val7.Play(); GhostlyAudioTrigger.Attach(val6, val7, val5.length + 0.5f, playPos); if (!isHuntSFX) { GhostlyVisual.Spawn(playPos, val); } } } private static EnemyType? FindEnemyTypeByName(string name) { string name2 = name; SelectableLevel val = StartOfRound.Instance?.currentLevel; if ((Object)(object)val != (Object)null) { if (val.Enemies != null) { SpawnableEnemyWithRarity val2 = ((IEnumerable)val.Enemies).FirstOrDefault((Func)((SpawnableEnemyWithRarity e) => e.enemyType?.enemyName == name2)); if (val2 != null) { return val2.enemyType; } } if (val.OutsideEnemies != null) { SpawnableEnemyWithRarity val3 = ((IEnumerable)val.OutsideEnemies).FirstOrDefault((Func)((SpawnableEnemyWithRarity e) => e.enemyType?.enemyName == name2)); if (val3 != null) { return val3.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; } } 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) { ConfigFile cfg2 = cfg; BaselineInside = cfg2.Bind("Сила монстров", "Множитель внутри", 1.8f, "Во сколько раз увеличить максимальное количество монстров ВНУТРИ здания каждый раунд. По умолчанию: 1.8"); BaselineOutside = cfg2.Bind("Сила монстров", "Множитель снаружи", 1.6f, "Во сколько раз увеличить максимальное количество монстров СНАРУЖИ каждый раунд. По умолчанию: 1.6"); BaselineDaytime = cfg2.Bind("Сила монстров", "Множитель дневных", 1.4f, "Во сколько раз увеличить максимальное количество ДНЕВНЫХ монстров каждый раунд. По умолчанию: 1.4"); EventPowerScale = cfg2.Bind("Сила монстров", "Сила баффов событий", 1f, "Насколько сильно ОТДЕЛЬНЫЕ СОБЫТИЯ (Вечеринка Лозы ×2.5, Метеоритная блокада ×3 и т.п.) меняют число монстров относительно базового уровня. 1.0 = как задумано автором событий, 0.5 = эффект событий вдвое мягче, 0 = события вообще не меняют число монстров (только баф/нерф добычи и т.п.). Сам базовый уровень не трогает — для него ползунки выше. По умолчанию: 1.0"); WeightGood = cfg2.Bind("Веса событий", "Вес хорошего", 15, "Частота выпадения хороших событий (✓). Чем выше — тем чаще. По умолчанию: 15"); WeightNeutral = cfg2.Bind("Веса событий", "Вес нейтрального", 23, "Частота выпадения нейтральных событий (≡). По умолчанию: 23"); WeightVeryBad = cfg2.Bind("Веса событий", "Вес плохого", 52, "Частота выпадения плохих событий (⚠). По умолчанию: 52"); WeightInsane = cfg2.Bind("Веса событий", "Вес хаоса", 10, "Частота выпадения событий хаоса (‼). По умолчанию: 10"); MaxHistorySize = cfg2.Bind("История событий", "Размер истории", 14, "Сколько последних событий помнит система (защита от повторов). Чем больше — тем реже повторения. По умолчанию: 14"); BindEvents(cfg2, "Метеориты", 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(cfg2, "Монстры", 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(cfg2, "Лут", 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(cfg2, "Погода", 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(cfg2, "Специальные", 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(); } cfg2.SettingChanged += delegate { cfg2.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)[7] { ("Разведка Компании", "Обнаружены дополнительные запасы. Ценность добычи +15%.", "#4DFF6E", delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { RoundManager? rM3 = EventHelpers.RM; rM3.scrapValueMultiplier *= 1.15f; } }, false, false), ("Аномальная активность", "Сенсоры фиксируют усиление активности монстров!", "#FF7A35", delegate { SelectableLevel level4 = EventHelpers.Level; if (!((Object)(object)level4 == (Object)null)) { int val3 = Math.Max(1, EventBaseline.InsidePower * 4); int val4 = Math.Max(1, EventBaseline.OutsidePower * 4); level4.maxEnemyPowerCount = Math.Min((int)((float)level4.maxEnemyPowerCount * 1.3f), val3); level4.maxOutsideEnemyPowerCount = Math.Min((int)((float)level4.maxOutsideEnemyPowerCount * 1.3f), val4); } }, true, false), ("Затишье перед бурей", "Монстры временно отступили. Действуй быстро!", "#AEFF6E", delegate { SelectableLevel level3 = EventHelpers.Level; if (!((Object)(object)level3 == (Object)null)) { level3.maxEnemyPowerCount = Math.Max(0, (int)((float)level3.maxEnemyPowerCount * 0.65f)); level3.maxOutsideEnemyPowerCount = Math.Max(0, (int)((float)level3.maxOutsideEnemyPowerCount * 0.65f)); } }, true, false), ("Ценная находка", "Дополнительные предметы обнаружены в здании. Количество добычи +20%.", "#4DFF6E", delegate { if ((Object)(object)EventHelpers.RM != (Object)null) { RoundManager? rM2 = EventHelpers.RM; rM2.scrapAmountMultiplier *= 1.2f; } }, false, true), ("Сигнал тревоги", "Система безопасности активирована! Монстры внутри усиливаются.", "#FF4444", delegate { SelectableLevel level2 = EventHelpers.Level; if (!((Object)(object)level2 == (Object)null)) { int val2 = Math.Max(1, EventBaseline.InsidePower * 4); level2.maxEnemyPowerCount = Math.Min((int)((float)level2.maxEnemyPowerCount * 1.45f), val2); } }, true, false), ("Ионный шторм", "Электромагнитная буря временно нарушила работу турелей.", "#FFE44D", delegate { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown SelectableLevel level = EventHelpers.Level; if (level?.spawnableMapObjects != null) { SpawnableMapObject[] spawnableMapObjects = level.spawnableMapObjects; foreach (SpawnableMapObject val in spawnableMapObjects) { if (val != null) { GameObject prefabToSpawn = val.prefabToSpawn; if (((prefabToSpawn == null) ? null : ((Object)prefabToSpawn).name?.ToLower().Contains("turret")).GetValueOrDefault()) { AnimationCurve numberToSpawn = val.numberToSpawn; Keyframe[] array2 = ((numberToSpawn != null) ? numberToSpawn.keys : null); if (array2 != null) { for (int k = 0; k < array2.Length; k++) { ref Keyframe reference = ref array2[k]; ((Keyframe)(ref reference)).value = ((Keyframe)(ref reference)).value * 0.5f; } val.numberToSpawn = new AnimationCurve(array2); } } } } } }, false, 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 i = 0; i < array.Length; i++) { (string, string, string, Action, bool, bool) item = array[i]; 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.0.1")] [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 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("daz.randomeventsextended"); ModConfig.Init(((BaseUnityPlugin)this).Config); SafePatchAll(_harmony); try { MethodInfo methodInfo = AccessTools.Method("Steamworks.Data.Lobby:SetData", (Type[])null, (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(LobbyTagPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"RandomEvents: patched Lobby.SetData (hide modded tag)."); } MethodInfo methodInfo2 = AccessTools.Method("ModdedCheck:GetModdedState", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ModdedStatePatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"RandomEvents: patched ModdedCheck.GetModdedState -> Vanilla."); } } catch (Exception ex) { Log.LogWarning((object)("RandomEvents: failed to apply lobby-hider patches: " + ex.Message)); } Log.LogInfo((object)"RandomEventsExtended plugin loaded successfully!"); } 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; } public static class LobbyTagPatch { public static bool Prefix(string key, string value) { if (key == "tag" && value == "modded") { Plugin.Log.LogInfo((object)"RandomEvents: blocked SetData(tag, modded)."); return false; } return true; } } public static class ModdedStatePatch { public static bool Prefix(ref ModdedState __result) { __result = (ModdedState)1; return false; } } [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) { try { NavMeshAgent component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.areaMask = -1; } } catch { } } } [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 { 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__0_0; public static HandleNamedMessageDelegate <>9__0_2; public static Action <>9__0_3; internal void b__0_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__0_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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) 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)); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Client received sound play request: {text} at {val}, hunt: {flag}"); GhostlyWorldController.PlaySoundLocally(val, text, isInside, flag); } internal void b__0_3(ulong clientId) { Plugin.RegisteredModdedClients.Remove(clientId); Plugin.Log.LogInfo((object)$"[Network] Client disconnected. Cleaned up clientId: {clientId}"); } } [HarmonyPatch("Start")] [HarmonyPostfix] public static void OnStart() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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__0_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__0_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 val4 = default(FastBufferWriter); ((FastBufferWriter)(ref val4))..ctor(4, (Allocator)2, -1); netManager.CustomMessagingManager.SendNamedMessage("RandomEvents_Pong", senderClientId, val4, (NetworkDelivery)3); }); CustomMessagingManager customMessagingManager2 = netManager.CustomMessagingManager; object obj2 = <>c.<>9__0_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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Vector3 val3 = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val3); 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)); Plugin.Log.LogInfo((object)$"[GhostlyWorld] Client received sound play request: {text} at {val3}, hunt: {flag}"); GhostlyWorldController.PlaySoundLocally(val3, text, isInside, flag); }; <>c.<>9__0_2 = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("RandomEvents_PlayGhostlySound", (HandleNamedMessageDelegate)obj2); if (netManager.IsServer) { netManager.OnClientDisconnectCallback += delegate(ulong clientId) { Plugin.RegisteredModdedClients.Remove(clientId); Plugin.Log.LogInfo((object)$"[Network] Client disconnected. Cleaned up clientId: {clientId}"); }; } } } [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.0.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" } }, { "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_nerf" } }, { "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[3] { "loot_buff", "loot_jackpot", "monsters_zero" } }, { "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" } }, { "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" } } }; 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[3] { "outdoor_buff", "indoor_buff", "monsters_nerf" } }, { "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_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_0467: 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 list3 = (CurrentRoundEvents = new List { gameEventData, gameEventData2, gameEventData3 }); if (((NetworkBehaviour)__instance).IsServer) { foreach (GameEventData item in list3) { Plugin.Log.LogInfo((object)("[RandomEventsExtended] Applying gameplay modifications for: " + item.Id)); item.Effect?.Invoke(); } ClampFinalMultipliers(list3); SendEventsToChat(list3); DangerRating.SendToChat(val2, gameEventData, gameEventData2, gameEventData3); bool lootAvailable = !EventConflictSystem.AnyHasTag(list3, "loot_zero"); bool monstersAvailable = !EventConflictSystem.AnyHasTag(list3, "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)); 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."); ((MonoBehaviour)__instance).StartCoroutine(SendEventNotificationsAfterDelay(list3)); } if (gameEventData2.WeatherOverride.HasValue) { EventHelpers.SetWeather(gameEventData2.WeatherOverride.Value); Plugin.Log.LogInfo((object)$"[RandomEventsExtended] Weather synced locally from event '{gameEventData2.Id}' → {gameEventData2.WeatherOverride.Value} (IsServer={((NetworkBehaviour)__instance).IsServer})"); } ShowEventCard(list3); } catch (Exception arg) { Plugin.Log.LogError((object)$"[RandomEventsExtended] Error applying events: {arg}"); } } 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 > 10f) { num = 10f; } 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 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); 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); } } } 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}"); } 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 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; } } }