using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [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("RandomDelivery")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.5.0")] [assembly: AssemblyInformationalVersion("1.4.5")] [assembly: AssemblyProduct("RandomDelivery")] [assembly: AssemblyTitle("RandomDelivery")] [assembly: AssemblyVersion("1.4.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RandomDelivery { public class DeliveryConfig { private readonly ConfigEntry _enabled; private readonly ConfigEntry _enableLogging; private readonly ConfigEntry _deliveryTimes; private readonly ConfigEntry _maxDeliveriesPerDay; private readonly ConfigEntry _minItems; private readonly ConfigEntry _maxItems; private readonly ConfigEntry _itemSelectionMode; private readonly ConfigEntry _priceWeightFactor; private readonly ConfigEntry _discountBoost; private readonly ConfigEntry _dropshipAutoOpen; private readonly ConfigEntry _chanceForTrap; private readonly ConfigEntry _chanceForMonster; private readonly ConfigEntry _priority; private readonly ConfigEntry _chanceForAllTraps; private readonly ConfigEntry _chanceForAllMonsters; private readonly ConfigEntry _allowedTraps; private readonly ConfigEntry _blockedTraps; private readonly ConfigEntry _allowedMonsters; private readonly ConfigEntry _blockedMonsters; private readonly ConfigEntry _allowedItems; private readonly ConfigEntry _blockedItems; public bool Enabled => _enabled.Value; public bool EnableLogging => _enableLogging.Value; public int MaxDeliveriesPerDay => Math.Max(0, _maxDeliveriesPerDay.Value); public List DeliveryTimes => SplitCsv(_deliveryTimes.Value).Cast().ToList(); public int MinItems => Math.Max(0, _minItems.Value); public int MaxItems => Math.Max(MinItems, _maxItems.Value); public float PriceWeightFactor => Math.Max(0f, _priceWeightFactor.Value); public bool DiscountBoost => _discountBoost.Value; public bool DropshipAutoOpen => _dropshipAutoOpen.Value; public float ChanceForTrap => Clamp01to100(_chanceForTrap.Value); public float ChanceForMonster => Clamp01to100(_chanceForMonster.Value); public float ChanceForAllTraps => Clamp01to100(_chanceForAllTraps.Value); public float ChanceForAllMonsters => Clamp01to100(_chanceForAllMonsters.Value); public List AllowedTraps => SplitCsv(_allowedTraps.Value); public List BlockedTraps => SplitCsv(_blockedTraps.Value); public List AllowedMonsters => SplitCsv(_allowedMonsters.Value); public List BlockedMonsters => SplitCsv(_blockedMonsters.Value); public List AllowedItems => SplitCsv(_allowedItems.Value); public List BlockedItems => SplitCsv(_blockedItems.Value); public bool MonsterHasPriority => string.Equals(_priority.Value?.Trim(), "Monster", StringComparison.OrdinalIgnoreCase); public bool IsPriceWeighted => string.Equals(_itemSelectionMode.Value?.Trim(), "PriceWeighted", StringComparison.OrdinalIgnoreCase); public DeliveryConfig(ConfigFile cfg) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: 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_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Expected O, but got Unknown //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown _enabled = cfg.Bind("General", "Enabled", true, "Enable or disable the whole mod."); _enableLogging = cfg.Bind("General", "EnableLogging", true, "Verbose per-delivery logging to the BepInEx console."); _deliveryTimes = cfg.Bind("Schedule", "DeliveryTimes", "08:30", "Comma-separated list of times the dropship is dispatched (in-game clock, day starts 06:00). Each entry is an 'HH:MM' time, 'StartOfDay' (right after landing), or a number of seconds after the day starts. The dropship then descends and touches down a short while later, like a normal order — so the default 08:30 lands it in the morning. List several for multiple deliveries, e.g. 08:30,13:00."); _maxDeliveriesPerDay = cfg.Bind("Schedule", "MaxDeliveriesPerDay", 1, new ConfigDescription("Hard cap on deliveries per day.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 20), Array.Empty())); _minItems = cfg.Bind("Items", "MinItems", 2, new ConfigDescription("Minimum items per delivery.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 10), Array.Empty())); _maxItems = cfg.Bind("Items", "MaxItems", 4, new ConfigDescription("Maximum items per delivery.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 10), Array.Empty())); _itemSelectionMode = cfg.Bind("Items", "ItemSelectionMode", "Random", new ConfigDescription("How items are chosen.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Random", "PriceWeighted" }), Array.Empty())); _priceWeightFactor = cfg.Bind("Items", "PriceWeightFactor", 1f, new ConfigDescription("PriceWeighted steepness: 0 = flat, 1 = inverse price, 2 = strongly favour cheap.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); _discountBoost = cfg.Bind("Items", "DiscountBoost", true, "In PriceWeighted mode, boost the chance of items that are on sale."); _dropshipAutoOpen = cfg.Bind("Items", "DropshipAutoOpen", false, "If true, the dropship hatch opens by itself when it lands (items drop automatically). If false (default), it stays closed like a normal order and a player must walk up and open it. Traps/monsters that ride the delivery appear once the hatch is opened."); _chanceForTrap = cfg.Bind("Replacements", "ChanceForTrap", 15f, new ConfigDescription("Per-slot % chance to replace the item with a trap.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); _chanceForMonster = cfg.Bind("Replacements", "ChanceForMonster", 10f, new ConfigDescription("Per-slot % chance to replace the item with a monster.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); _priority = cfg.Bind("Replacements", "Priority", "Monster", new ConfigDescription("Winner when both a trap and a monster roll hit the same slot.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Monster", "Trap" }), Array.Empty())); _chanceForAllTraps = cfg.Bind("Replacements", "ChanceForAllTraps", 0f, new ConfigDescription("Chance (0-100) that the WHOLE delivery is nothing but traps (every slot). Rolled once per delivery, before the per-slot chances.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); _chanceForAllMonsters = cfg.Bind("Replacements", "ChanceForAllMonsters", 0f, new ConfigDescription("Chance (0-100) that the WHOLE delivery is nothing but monsters (every slot). Rolled once per delivery. If both all-traps and all-monsters hit, Priority decides.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); _allowedTraps = cfg.Bind("Traps", "AllowedTraps", "Turret, Landmine", "Comma-separated whitelist of traps. Non-empty = only these; empty = allow all except BlockedTraps."); _blockedTraps = cfg.Bind("Traps", "BlockedTraps", "", "Comma-separated blacklist of traps (used only when AllowedTraps is empty)."); _allowedMonsters = cfg.Bind("Monsters", "AllowedMonsters", "Manticoil, RoamingLocust, RedLocust, HoardingBug, GunkFish, Slime, TulipSnake, Maneater", "Comma-separated whitelist of small monsters. Only these may be delivered (and only if the current moon has them)."); _blockedMonsters = cfg.Bind("Monsters", "BlockedMonsters", "", "Comma-separated blacklist of monsters, removed on top of the whitelist."); _allowedItems = cfg.Bind("ItemFilters", "AllowedItems", "", "Comma-separated whitelist of shop items. Non-empty = only these; empty = allow all except BlockedItems."); _blockedItems = cfg.Bind("ItemFilters", "BlockedItems", "Clipboard, ToyCube", "Comma-separated blacklist of shop items (used only when AllowedItems is empty)."); } private static float Clamp01to100(float v) { if (!(v < 0f)) { if (!(v > 100f)) { return v; } return 100f; } return 0f; } private static List SplitCsv(string raw) { List list = new List(); if (string.IsNullOrWhiteSpace(raw)) { return list; } string[] array = raw.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { list.Add(text); } } return list; } } internal static class DeliveryManager { private enum Cat { Item, Trap, Monster } private static readonly Random Rng = new Random(); internal static bool IsHost { get { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { return ((NetworkBehaviour)instance).IsServer; } return false; } } internal static void OnNewDay() { ItemListProvider.Reset(); } internal static bool RunDelivery(string reason) { //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) DeliveryConfig cfg = Plugin.Cfg; if (cfg == null || !cfg.Enabled) { return false; } if (!IsHost) { return false; } try { List list = ItemListProvider.BuildPool(); if (list.Count == 0) { Plugin.Log.LogWarning((object)("[Delivery] (" + reason + ") cancelled: the shop item pool is empty (no buyable items, or everything was filtered out by Allowed/BlockedItems).")); return false; } List list2 = TrapMonsterProvider.BuildTrapPool(); List list3 = TrapMonsterProvider.BuildMonsterPool(); int num = Rng.Next(cfg.MinItems, cfg.MaxItems + 1); if (num <= 0) { Plugin.Log.LogInfo((object)("[Delivery] (" + reason + ") rolled 0 slots — nothing delivered.")); return false; } Cat? cat = RollDeliveryMode(cfg, list2, list3); if (cat.HasValue && cfg.EnableLogging) { Plugin.Log.LogInfo((object)$"[Delivery] ({reason}) whole-delivery mode: all {cat}s."); } List list4 = new List(); List list5 = new List(); List list6 = new List(); List list7 = new List(num); for (int i = 0; i < num; i++) { Cat cat2 = cat ?? Roll(cfg); string text = null; if (cat2 == Cat.Monster && list3.Count > 0) { EnemyType val = list3[Rng.Next(list3.Count)]; list6.Add(val); text = "Monster:" + val.enemyName; } else if (cat2 == Cat.Trap && list2.Count > 0) { TrapPrefab trapPrefab = list2[Rng.Next(list2.Count)]; list5.Add(trapPrefab); text = "Trap:" + trapPrefab.Name; } if (text == null) { DeliverableItem deliverableItem = ItemListProvider.PickWeighted(list, Rng); if (deliverableItem != null) { list4.Add(deliverableItem); text = "Item:" + deliverableItem.Name; } } list7.Add(text ?? "(failed)"); } bool flag = false; if (list4.Count > 0 || list5.Count > 0 || list6.Count > 0) { List list8 = new List(list4.Count); foreach (DeliverableItem item in list4) { list8.Add(item.Index); } flag = SpawnHelper.QueueDropshipDelivery(list8, list5, list6); } string text2; if (flag) { text2 = "dropship"; } else { int val2 = list4.Count + list5.Count + list6.Count; List anchorPositions = SpawnHelper.GetAnchorPositions(Math.Max(1, val2)); int num2 = 0; foreach (DeliverableItem item2 in list4) { SpawnHelper.SpawnItem(item2.Item, anchorPositions[num2++ % anchorPositions.Count]); } foreach (TrapPrefab item3 in list5) { SpawnHelper.SpawnTrap(item3, anchorPositions[num2++ % anchorPositions.Count]); } foreach (EnemyType item4 in list6) { SpawnHelper.SpawnMonster(item4, anchorPositions[num2++ % anchorPositions.Count]); } text2 = "direct"; } Plugin.Log.LogInfo((object)($"[Delivery] ({reason}) {num} slot(s) via {text2}: {list4.Count} item(s), " + $"{list5.Count} trap(s), {list6.Count} monster(s).")); if (cfg.EnableLogging) { Plugin.Log.LogInfo((object)("[Delivery] -> " + string.Join(" | ", list7))); } return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"[Delivery] ({reason}) failed: {arg}"); return false; } } private static Cat? RollDeliveryMode(DeliveryConfig cfg, List trapPool, List monsterPool) { bool flag = trapPool.Count > 0 && Rng.NextDouble() * 100.0 < (double)cfg.ChanceForAllTraps; bool flag2 = monsterPool.Count > 0 && Rng.NextDouble() * 100.0 < (double)cfg.ChanceForAllMonsters; if (flag && flag2) { return (!cfg.MonsterHasPriority) ? Cat.Trap : Cat.Monster; } if (flag2) { return Cat.Monster; } if (flag) { return Cat.Trap; } return null; } private static Cat Roll(DeliveryConfig cfg) { bool flag = Rng.NextDouble() * 100.0 < (double)cfg.ChanceForTrap; bool flag2 = Rng.NextDouble() * 100.0 < (double)cfg.ChanceForMonster; if (flag && flag2) { if (!cfg.MonsterHasPriority) { return Cat.Trap; } return Cat.Monster; } if (flag2) { return Cat.Monster; } if (flag) { return Cat.Trap; } return Cat.Item; } } internal sealed class DeliveryScheduler : MonoBehaviour { private enum TriggerKind { StartOfDay, Seconds, ClockTime } private sealed class Trigger { public TriggerKind Kind; public float Seconds; public float Normalized; public bool Fired; public string Label; } private const float PollInterval = 0.25f; private const float StartOfDayDelay = 3f; private float _nextPoll; private bool _dayActive; private int _deliveredToday; private float _landedElapsed; private readonly List _triggers = new List(); private void Update() { if (Time.unscaledTime < _nextPoll) { return; } _nextPoll = Time.unscaledTime + 0.25f; try { Poll(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Delivery] scheduler poll failed: {arg}"); } } private void Poll() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { _dayActive = false; return; } bool flag = instance.shipHasLanded && !instance.shipIsLeaving && !instance.inShipPhase; if (flag && !_dayActive) { OnDayStart(); } else if (!flag && _dayActive) { _dayActive = false; } if (!_dayActive) { return; } _landedElapsed += 0.25f; DeliveryConfig cfg = Plugin.Cfg; if (cfg == null || !cfg.Enabled || !DeliveryManager.IsHost || _deliveredToday >= cfg.MaxDeliveriesPerDay) { return; } float dayTime = (((Object)(object)TimeOfDay.Instance != (Object)null) ? TimeOfDay.Instance.currentDayTime : _landedElapsed); float normalized = (((Object)(object)TimeOfDay.Instance != (Object)null) ? TimeOfDay.Instance.normalizedTimeOfDay : 0f); foreach (Trigger trigger in _triggers) { if (!trigger.Fired && IsDue(trigger, dayTime, normalized)) { trigger.Fired = true; if (DeliveryManager.RunDelivery(trigger.Label)) { _deliveredToday++; } break; } } } private bool IsDue(Trigger t, float dayTime, float normalized) { return t.Kind switch { TriggerKind.StartOfDay => _landedElapsed >= 3f, TriggerKind.Seconds => dayTime >= t.Seconds, TriggerKind.ClockTime => normalized >= t.Normalized, _ => false, }; } private void OnDayStart() { _dayActive = true; _deliveredToday = 0; _landedElapsed = 0f; Plugin.ReloadConfigFile(); DeliveryManager.OnNewDay(); BuildTriggers(); if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)($"[Delivery] New day: {_triggers.Count} scheduled trigger(s), " + $"max/day={Plugin.Cfg.MaxDeliveriesPerDay}.")); } } private void BuildTriggers() { _triggers.Clear(); DeliveryConfig cfg = Plugin.Cfg; if (cfg.DeliveryTimes == null) { return; } int numberOfHours = (((Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.numberOfHours > 0) ? TimeOfDay.Instance.numberOfHours : 18); foreach (object deliveryTime in cfg.DeliveryTimes) { if (deliveryTime != null) { Trigger trigger = ParseTrigger(deliveryTime, numberOfHours); if (trigger != null) { _triggers.Add(trigger); } } } } private Trigger ParseTrigger(object raw, int numberOfHours) { if (raw is long num) { return SecondsTrigger(num); } if (raw is int num2) { return SecondsTrigger(num2); } if (raw is double num3) { return SecondsTrigger((float)num3); } if (raw is float seconds) { return SecondsTrigger(seconds); } string text = raw.ToString().Trim(); if (text.Length == 0) { return null; } if (text.Equals("StartOfDay", StringComparison.OrdinalIgnoreCase)) { return new Trigger { Kind = TriggerKind.StartOfDay, Label = "StartOfDay" }; } if (text.Contains(":")) { string[] array = text.Split(':'); if (array.Length >= 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2)) { float normalized = Mathf.Clamp01(((float)result + (float)result2 / 60f - 6f) / (float)numberOfHours); return new Trigger { Kind = TriggerKind.ClockTime, Normalized = normalized, Label = "clock " + text }; } Plugin.Log.LogWarning((object)("[Delivery] Could not parse time '" + text + "' — ignored.")); return null; } if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var result3)) { return SecondsTrigger(result3); } Plugin.Log.LogWarning((object)("[Delivery] Unrecognised DeliveryTimes entry '" + text + "' — ignored.")); return null; } private static Trigger SecondsTrigger(float seconds) { return new Trigger { Kind = TriggerKind.Seconds, Seconds = Mathf.Max(0f, seconds), Label = $"{Mathf.RoundToInt(seconds)}s" }; } } internal class DeliverableItem { public Item Item; public int Index; public double Weight; public string Name { get { if (!((Object)(object)Item != (Object)null)) { return ""; } return Item.itemName; } } } internal static class ItemListProvider { private static Terminal _terminal; private static Terminal FindTerminal() { if ((Object)(object)_terminal == (Object)null) { _terminal = Object.FindObjectOfType(); } return _terminal; } internal static Terminal GetTerminal() { return FindTerminal(); } internal static void Reset() { _terminal = null; } internal static List BuildPool() { DeliveryConfig cfg = Plugin.Cfg; List list = new List(); Terminal val = FindTerminal(); if ((Object)(object)val == (Object)null || val.buyableItemsList == null) { Plugin.Log.LogWarning((object)"[Delivery] No Terminal / buyableItemsList found — cannot build item pool."); return list; } HashSet hashSet = Names.NormalizedSet(cfg.AllowedItems); HashSet set = Names.NormalizedSet(cfg.BlockedItems); int[] itemSalesPercentages = val.itemSalesPercentages; Item[] buyableItemsList = val.buyableItemsList; for (int i = 0; i < buyableItemsList.Length; i++) { Item val2 = buyableItemsList[i]; if ((Object)(object)val2 == (Object)null || (Object)(object)val2.spawnPrefab == (Object)null || (Object)(object)val2.spawnPrefab.GetComponent() == (Object)null) { continue; } string[] candidatesRaw = new string[2] { val2.itemName, ((Object)val2.spawnPrefab).name }; if (hashSet.Count > 0) { if (!Names.NameMatchesSet(candidatesRaw, hashSet)) { continue; } } else if (Names.NameMatchesSet(candidatesRaw, set)) { continue; } list.Add(new DeliverableItem { Item = val2, Index = i, Weight = ComputeWeight(val2, i, itemSalesPercentages, cfg) }); } return list; } private static double ComputeWeight(Item item, int index, int[] sales, DeliveryConfig cfg) { if (!cfg.IsPriceWeighted) { return 1.0; } double x = Math.Max(1, item.creditsWorth); double num = 1.0 / Math.Pow(x, cfg.PriceWeightFactor); if (cfg.DiscountBoost && sales != null && index < sales.Length) { int num2 = 100 - sales[index]; if (num2 > 0) { num *= 1.0 + (double)num2 / 100.0; } } if (!(num <= 0.0)) { return num; } return double.Epsilon; } internal static DeliverableItem PickWeighted(List pool, Random rng) { if (pool == null || pool.Count == 0) { return null; } double num = 0.0; foreach (DeliverableItem item in pool) { num += item.Weight; } if (num <= 0.0) { return pool[rng.Next(pool.Count)]; } double num2 = rng.NextDouble() * num; foreach (DeliverableItem item2 in pool) { num2 -= item2.Weight; if (num2 < 0.0) { return item2; } } return pool[pool.Count - 1]; } } internal static class Names { public static string Normalize(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(s.Length); foreach (char c in s) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } public static HashSet NormalizedSet(IEnumerable raw) { HashSet hashSet = new HashSet(); if (raw == null) { return hashSet; } foreach (string item in raw) { string text = Normalize(item); if (text.Length > 0) { hashSet.Add(text); } } return hashSet; } public static IEnumerable MonsterAliases(string normalized) { yield return normalized; if (normalized == null) { yield break; } switch (normalized.Length) { case 9: switch (normalized[0]) { default: yield break; case 'm': if (normalized == "manticoil") { yield return "doublewing"; } yield break; case 'r': break; } if (!(normalized == "redlocust")) { break; } goto IL_01fb; case 8: switch (normalized[0]) { default: yield break; case 'g': if (!(normalized == "gunkfish")) { yield break; } break; case 's': if (!(normalized == "stingray")) { yield break; } break; case 'm': if (normalized == "maneater") { yield return "cavedweller"; yield return "caveddweller"; } yield break; } goto IL_028c; case 13: if (!(normalized == "roaminglocust")) { break; } goto IL_01fb; case 11: if (normalized == "hoardingbug") { yield return "hoarderbug"; yield return "hoarding"; } break; case 17: if (!(normalized == "backwatergunkfish")) { break; } goto IL_028c; case 5: if (normalized == "slime") { yield return "hygrodere"; yield return "blob"; } break; case 10: { if (normalized == "tulipsnake") { yield return "flowersnake"; yield return "snake"; } break; } IL_01fb: yield return "redlocustbees"; yield return "docilelocust"; yield return "locust"; break; IL_028c: yield return "stingray"; yield return "gunkfish"; yield return "backwatergunkfish"; yield return "backwater"; yield return "fish"; break; } } public static bool MonsterMatchesSet(IEnumerable candidatesRaw, HashSet set) { if (set == null || set.Count == 0) { return false; } List list = new List(); foreach (string item in candidatesRaw) { string text = Normalize(item); if (text.Length > 0) { list.Add(text); } } foreach (string item2 in set) { foreach (string item3 in MonsterAliases(item2)) { if (item3.Length == 0) { continue; } foreach (string item4 in list) { if (item4 == item3 || item4.Contains(item3) || item3.Contains(item4)) { return true; } } } } return false; } public static bool NameMatchesSet(IEnumerable candidatesRaw, HashSet set) { if (set == null || set.Count == 0) { return false; } foreach (string item in candidatesRaw) { string text = Normalize(item); if (text.Length == 0) { continue; } foreach (string item2 in set) { if (item2.Length > 0 && (text == item2 || text.Contains(item2) || item2.Contains(text))) { return true; } } } return false; } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPatch("StartGame")] [HarmonyPostfix] private static void OnStartGame() { DeliveryManager.OnNewDay(); } } [BepInPlugin("Timofey.RandomDelivery", "RandomDelivery", "1.4.5")] [BepInProcess("Lethal Company.exe")] public class Plugin : BaseUnityPlugin { public const string GUID = "Timofey.RandomDelivery"; public const string NAME = "RandomDelivery"; public const string VERSION = "1.4.5"; private readonly Harmony _harmony = new Harmony("Timofey.RandomDelivery"); public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static DeliveryConfig Cfg { get; internal set; } public static void ReloadConfigFile() { try { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Config.Reload(); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Config reload failed: " + ex.Message)); } } } private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg = new DeliveryConfig(((BaseUnityPlugin)this).Config); _harmony.PatchAll(Assembly.GetExecutingAssembly()); GameObject val = new GameObject("RandomDelivery_Scheduler") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); val.AddComponent(); Log.LogInfo((object)"RandomDelivery v1.4.5 loaded."); } } internal static class SpawnHelper { private static readonly Random Rng = new Random(); private static MethodInfo _landShipMethod; private static MethodInfo LandShipMethod => _landShipMethod ?? (_landShipMethod = typeof(ItemDropship).GetMethod("LandShipOnServer", BindingFlags.Instance | BindingFlags.NonPublic)); internal static List GetAnchorPositions(int count) { //IL_0012: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_0163: Unknown result type (might be due to invalid IL or missing references) List list = new List(count); Vector3 pad; bool flag = TryGetDropshipPad(out pad); if (!flag) { pad = PadCenterNearShip(); } for (int i = 0; i < count; i++) { Vector3 item; if (flag) { item = pad + new Vector3((float)(Rng.NextDouble() - 0.5) * 2.4f, 1f, (float)(Rng.NextDouble() - 0.5) * 2.4f); } else { float num = MathF.PI / 180f * (360f / (float)Mathf.Max(1, count) * (float)i + (float)(Rng.NextDouble() * 40.0)); float num2 = 3.5f + (float)(Rng.NextDouble() * 2.5); item = pad + new Vector3(Mathf.Cos(num) * num2, 1f, Mathf.Sin(num) * num2); } list.Add(item); } if (Plugin.Cfg.EnableLogging) { StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < list.Count; j++) { if (j > 0) { stringBuilder.Append(" ; "); } stringBuilder.Append(Fmt(list[j])); } Plugin.Log.LogInfo((object)("[Delivery] anchors via " + (flag ? "dropship-pad" : "ship-ring") + " " + $"center={Fmt(pad)} -> {stringBuilder}")); } return list; } private static bool TryGetDropshipPad(out Vector3 pad) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) pad = Vector3.zero; ItemDropship val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)"[Delivery] No ItemDropship in scene."); } return false; } Transform[] itemSpawnPositions = val.itemSpawnPositions; Vector3 val2 = ((itemSpawnPositions != null && itemSpawnPositions.Length != 0 && (Object)(object)itemSpawnPositions[0] != (Object)null) ? itemSpawnPositions[0].position : ((Component)val).transform.position); Vector3 val3 = ShipPoint(); float num = Vector2.Distance(new Vector2(val2.x, val2.z), new Vector2(val3.x, val3.z)); float num2 = Mathf.Abs(val2.y - val3.y); if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)("[Delivery] ItemDropship spot=" + Fmt(val2) + " ship=" + Fmt(val3) + " " + $"horiz={num:F1} vert={num2:F1}")); } if (num <= 30f && num2 <= 12f) { pad = val2; return true; } return false; } private static Vector3 PadCenterNearShip() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ShipPoint(); NavMeshHit val2 = default(NavMeshHit); if (NavMesh.SamplePosition(val, ref val2, 40f, -1)) { return ((NavMeshHit)(ref val2)).position; } return val; } private static Vector3 ShipPoint() { //IL_004c: 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_0046: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.elevatorTransform != (Object)null) { return instance.elevatorTransform.position; } if ((Object)(object)instance != (Object)null && (Object)(object)instance.shipLandingPosition != (Object)null) { return instance.shipLandingPosition.position; } return Vector3.zero; } private static string Fmt(Vector3 v) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return $"({v.x:F1},{v.y:F1},{v.z:F1})"; } internal static bool SpawnItem(Item item, Vector3 anchor) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null || (Object)(object)item.spawnPrefab == (Object)null) { return false; } NavMeshHit val = default(NavMeshHit); string text; Vector3 val2; if (NavMesh.SamplePosition(anchor, ref val, 15f, -1)) { val2 = ((NavMeshHit)(ref val)).position; text = "navmesh"; } else { int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-1)); RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(anchor + Vector3.up * 2f, Vector3.down, ref val3, 60f, num, (QueryTriggerInteraction)1)) { val2 = ((RaycastHit)(ref val3)).point; text = "raycast:" + ((Object)((RaycastHit)(ref val3)).collider).name; } else { val2 = anchor; text = "anchor(no-ground)"; } } val2 += Vector3.up * Mathf.Max(0.05f, item.verticalOffset); if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)("[Delivery] item '" + item.itemName + "' anchor=" + Fmt(anchor) + " via " + text + " rest=" + Fmt(val2))); } Transform val4 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.propsContainer : null); Quaternion val5 = Quaternion.Euler(item.restingRotation.x, item.restingRotation.y, item.restingRotation.z); GameObject val6 = Object.Instantiate(item.spawnPrefab, val2, val5, val4); GrabbableObject component = val6.GetComponent(); NetworkObject component2 = val6.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { Plugin.Log.LogWarning((object)("[Delivery] Item '" + item.itemName + "' prefab missing GrabbableObject/NetworkObject; skipped.")); Object.Destroy((Object)(object)val6); return false; } try { if ((Object)(object)component.itemProperties != (Object)null && component.itemProperties.isScrap) { component.SetScrapValue(Mathf.Max(0, item.creditsWorth)); } component.fallTime = 1f; component.hasHitGround = true; component.reachedFloorTarget = true; Transform parent = val6.transform.parent; component.targetFloorPosition = (((Object)(object)parent != (Object)null) ? parent.InverseTransformPoint(val2) : val2); component.startFallingPosition = component.targetFloorPosition; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Delivery] Floor placement for '" + item.itemName + "' failed: " + ex.Message)); } component2.Spawn(false); return true; } internal static bool QueueDropshipDelivery(List itemIndices, List traps, List monsters) { Terminal terminal = ItemListProvider.GetTerminal(); if ((Object)(object)terminal == (Object)null || terminal.orderedItemsFromTerminal == null || terminal.buyableItemsList == null) { return false; } ItemDropship val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null || (Object)(object)Plugin.Instance == (Object)null) { return false; } int num = 0; if (itemIndices != null) { foreach (int itemIndex in itemIndices) { if (itemIndex >= 0 && itemIndex < terminal.buyableItemsList.Length) { terminal.orderedItemsFromTerminal.Add(itemIndex); num++; } } } terminal.numberOfItemsInDropship = Mathf.Clamp(terminal.numberOfItemsInDropship + num, 0, 12); if (!val.deliveringOrder && !val.shipLanded && LandShipMethod != null) { try { LandShipMethod.Invoke(val, null); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Delivery] Starting dropship descent failed: " + ex.Message)); } } ((MonoBehaviour)Plugin.Instance).StartCoroutine(HandleDropshipDelivery(val, traps, monsters)); if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)($"[Delivery] Dropship dispatched: carrying {num} item(s), " + $"{traps?.Count ?? 0} trap(s) + {monsters?.Count ?? 0} monster(s) to drop on opening.")); } return true; } private static IEnumerator HandleDropshipDelivery(ItemDropship dropship, List traps, List monsters) { bool hasExtras = (traps != null && traps.Count > 0) || (monsters != null && monsters.Count > 0); bool log = Plugin.Cfg.EnableLogging; float t = 0f; while ((Object)(object)dropship != (Object)null && !dropship.shipLanded && t < 180f) { t += 0.25f; yield return (object)new WaitForSeconds(0.25f); } if ((Object)(object)dropship == (Object)null) { yield break; } if (log) { Plugin.Log.LogInfo((object)"[Delivery] Dropship landed."); } if (Plugin.Cfg.DropshipAutoOpen && !dropship.shipDoorsOpened) { try { dropship.TryOpeningShip(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Delivery] Auto-open failed: " + ex.Message)); } } if (!hasExtras) { yield break; } t = 0f; while ((Object)(object)dropship != (Object)null && !dropship.shipDoorsOpened && t < 35f) { t += 0.25f; yield return (object)new WaitForSeconds(0.25f); } if ((Object)(object)dropship == (Object)null || !dropship.shipDoorsOpened) { if (log) { Plugin.Log.LogInfo((object)"[Delivery] Dropship never opened — traps/monsters not deployed."); } yield break; } if (log) { Plugin.Log.LogInfo((object)"[Delivery] Dropship open — deploying traps/monsters at its drop spots."); } yield return (object)new WaitForSeconds(0.2f); SpawnAtDropship(dropship, traps, monsters); } private static void SpawnAtDropship(ItemDropship dropship, List traps, List monsters) { //IL_0020: 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) Transform[] itemSpawnPositions = dropship.itemSpawnPositions; int idx = 0; if (traps != null) { foreach (TrapPrefab trap in traps) { SpawnTrap(trap, DropSpot(dropship, itemSpawnPositions, ref idx)); } } if (monsters == null) { return; } foreach (EnemyType monster in monsters) { SpawnMonster(monster, DropSpot(dropship, itemSpawnPositions, ref idx)); } } private static Vector3 DropSpot(ItemDropship dropship, Transform[] spots, ref int idx) { //IL_001d: 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_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((spots != null && spots.Length != 0 && (Object)(object)spots[idx % spots.Length] != (Object)null) ? spots[idx % spots.Length].position : ((Component)dropship).transform.position); idx++; return val + new Vector3((float)(Rng.NextDouble() - 0.5) * 1.6f, 0.5f, (float)(Rng.NextDouble() - 0.5) * 1.6f); } internal static bool SpawnTrap(TrapPrefab trap, Vector3 anchor) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (trap == null || (Object)(object)trap.Prefab == (Object)null) { return false; } if (!TryGetGroundPosition(anchor, out var position, out var normal)) { Plugin.Log.LogWarning((object)$"[Delivery] No walkable ground near {anchor} for trap '{trap.Name}'."); return false; } Quaternion val = Quaternion.FromToRotation(Vector3.up, normal) * Quaternion.Euler(0f, (float)(Rng.NextDouble() * 360.0), 0f); GameObject val2 = Object.Instantiate(trap.Prefab, position, val); NetworkObject component = val2.GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)("[Delivery] Trap '" + trap.Name + "' prefab has no NetworkObject; skipped.")); Object.Destroy((Object)(object)val2); return false; } component.Spawn(true); return true; } internal static bool SpawnMonster(EnemyType type, Vector3 anchor) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)RoundManager.Instance == (Object)null || (Object)(object)type == (Object)null) { return false; } TryGetMonsterPosition(anchor, out var position); float num = (float)(Rng.NextDouble() * 360.0); try { NetworkObjectReference val = RoundManager.Instance.SpawnEnemyGameObject(position, num, -1, type); NetworkObject val2 = default(NetworkObject); bool flag = ((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null); if (Plugin.Cfg.EnableLogging) { Plugin.Log.LogInfo((object)$"[Delivery] Spawned monster '{type.enemyName}' at {Fmt(position)} (ok={flag})."); } return flag; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Delivery] Monster '" + type.enemyName + "' failed to spawn: " + ex.Message)); return false; } } internal static bool TryGetGroundPosition(Vector3 origin, out Vector3 position, out Vector3 normal) { //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) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) position = origin; normal = Vector3.up; NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(origin, ref val, 15f, -1)) { return false; } position = ((NavMeshHit)(ref val)).position + Vector3.up * 0.05f; int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-1)); RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(position + Vector3.up * 1f, Vector3.down, ref val2, 3f, num, (QueryTriggerInteraction)1)) { normal = ((RaycastHit)(ref val2)).normal; } return true; } internal static bool TryGetMonsterPosition(Vector3 origin, out Vector3 position) { //IL_0000: 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_000f: Unknown result type (might be due to invalid IL or missing references) if (TryGetGroundPosition(origin, out position, out var _)) { return true; } position = origin; return true; } } internal class TrapPrefab { public string Name; public GameObject Prefab; } internal static class TrapMonsterProvider { private static bool _dumpedAllEnemies; internal static List BuildTrapPool() { DeliveryConfig cfg = Plugin.Cfg; List list = new List(); HashSet allow = Names.NormalizedSet(cfg.AllowedTraps); HashSet block = Names.NormalizedSet(cfg.BlockedTraps); GameObject turret = null; GameObject mine = null; ScanTrapPrefabs(ref turret, ref mine); TryAdd(list, "Turret", turret, allow, block); TryAdd(list, "Landmine", mine, allow, block); if (cfg.EnableLogging) { Plugin.Log.LogInfo((object)($"[Delivery] trap pool: turretPrefab={(Object)(object)turret != (Object)null} " + $"minePrefab={(Object)(object)mine != (Object)null} -> {list.Count} allowed after filters")); } return list; } private static void ScanTrapPrefabs(ref GameObject turret, ref GameObject mine) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return; } List list = new List(); if ((Object)(object)instance.currentLevel != (Object)null && instance.currentLevel.spawnableMapObjects != null) { list.Add(instance.currentLevel.spawnableMapObjects); } if (instance.levels != null) { SelectableLevel[] levels = instance.levels; foreach (SelectableLevel val in levels) { if ((Object)(object)val != (Object)null && val.spawnableMapObjects != null) { list.Add(val.spawnableMapObjects); } } } foreach (SpawnableMapObject[] item in list) { for (int i = 0; i < item.Length; i++) { GameObject val2 = item[i]?.prefabToSpawn; if (!((Object)(object)val2 == (Object)null)) { if ((Object)(object)turret == (Object)null && (Object)(object)val2.GetComponentInChildren() != (Object)null) { turret = val2; } if ((Object)(object)mine == (Object)null && (Object)(object)val2.GetComponentInChildren() != (Object)null) { mine = val2; } } } if ((Object)(object)turret != (Object)null && (Object)(object)mine != (Object)null) { break; } } } private static void TryAdd(List list, string name, GameObject prefab, HashSet allow, HashSet block) { if ((Object)(object)prefab == (Object)null) { return; } string[] candidatesRaw = new string[1] { name }; if (allow.Count > 0) { if (!Names.NameMatchesSet(candidatesRaw, allow)) { return; } } else if (Names.NameMatchesSet(candidatesRaw, block)) { return; } list.Add(new TrapPrefab { Name = name, Prefab = prefab }); } internal static List BuildMonsterPool() { DeliveryConfig cfg = Plugin.Cfg; List list = new List(); HashSet hashSet = Names.NormalizedSet(cfg.AllowedMonsters); HashSet set = Names.NormalizedSet(cfg.BlockedMonsters); if (hashSet.Count == 0) { if (cfg.EnableLogging) { Plugin.Log.LogInfo((object)"[Delivery] monster pool: AllowedMonsters is empty — no monsters."); } return list; } HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); List list2 = new List(); EnemyType[] array = Resources.FindObjectsOfTypeAll(); foreach (EnemyType val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.enemyPrefab == (Object)null) && !string.IsNullOrWhiteSpace(val.enemyName) && hashSet2.Add(val.enemyName)) { list2.Add(val.enemyName); string[] candidatesRaw = new string[3] { val.enemyName, ((Object)val.enemyPrefab).name, ((Object)val).name }; if (Names.MonsterMatchesSet(candidatesRaw, hashSet) && !Names.MonsterMatchesSet(candidatesRaw, set)) { list.Add(val); } } } if (cfg.EnableLogging && !_dumpedAllEnemies) { _dumpedAllEnemies = true; list2.Sort(StringComparer.OrdinalIgnoreCase); Plugin.Log.LogInfo((object)string.Format("[Delivery] all loaded enemies ({0}): {1}", list2.Count, string.Join(", ", list2))); } if (cfg.EnableLogging) { List list3 = new List(list.Count); foreach (EnemyType item in list) { list3.Add(item.enemyName); } Plugin.Log.LogInfo((object)($"[Delivery] monster pool ({list.Count}): " + ((list3.Count > 0) ? string.Join(", ", list3) : ""))); } return list; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Timofey.RandomDelivery"; public const string PLUGIN_NAME = "RandomDelivery"; public const string PLUGIN_VERSION = "1.4.5"; } }