Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RandomDelivery v1.4.5
RandomDelivery.dll
Decompiled a day agousing 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<bool> _enabled; private readonly ConfigEntry<bool> _enableLogging; private readonly ConfigEntry<string> _deliveryTimes; private readonly ConfigEntry<int> _maxDeliveriesPerDay; private readonly ConfigEntry<int> _minItems; private readonly ConfigEntry<int> _maxItems; private readonly ConfigEntry<string> _itemSelectionMode; private readonly ConfigEntry<float> _priceWeightFactor; private readonly ConfigEntry<bool> _discountBoost; private readonly ConfigEntry<bool> _dropshipAutoOpen; private readonly ConfigEntry<float> _chanceForTrap; private readonly ConfigEntry<float> _chanceForMonster; private readonly ConfigEntry<string> _priority; private readonly ConfigEntry<float> _chanceForAllTraps; private readonly ConfigEntry<float> _chanceForAllMonsters; private readonly ConfigEntry<string> _allowedTraps; private readonly ConfigEntry<string> _blockedTraps; private readonly ConfigEntry<string> _allowedMonsters; private readonly ConfigEntry<string> _blockedMonsters; private readonly ConfigEntry<string> _allowedItems; private readonly ConfigEntry<string> _blockedItems; public bool Enabled => _enabled.Value; public bool EnableLogging => _enableLogging.Value; public int MaxDeliveriesPerDay => Math.Max(0, _maxDeliveriesPerDay.Value); public List<object> DeliveryTimes => SplitCsv(_deliveryTimes.Value).Cast<object>().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<string> AllowedTraps => SplitCsv(_allowedTraps.Value); public List<string> BlockedTraps => SplitCsv(_blockedTraps.Value); public List<string> AllowedMonsters => SplitCsv(_allowedMonsters.Value); public List<string> BlockedMonsters => SplitCsv(_blockedMonsters.Value); public List<string> AllowedItems => SplitCsv(_allowedItems.Value); public List<string> 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<bool>("General", "Enabled", true, "Enable or disable the whole mod."); _enableLogging = cfg.Bind<bool>("General", "EnableLogging", true, "Verbose per-delivery logging to the BepInEx console."); _deliveryTimes = cfg.Bind<string>("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<int>("Schedule", "MaxDeliveriesPerDay", 1, new ConfigDescription("Hard cap on deliveries per day.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); _minItems = cfg.Bind<int>("Items", "MinItems", 2, new ConfigDescription("Minimum items per delivery.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); _maxItems = cfg.Bind<int>("Items", "MaxItems", 4, new ConfigDescription("Maximum items per delivery.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); _itemSelectionMode = cfg.Bind<string>("Items", "ItemSelectionMode", "Random", new ConfigDescription("How items are chosen.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Random", "PriceWeighted" }), Array.Empty<object>())); _priceWeightFactor = cfg.Bind<float>("Items", "PriceWeightFactor", 1f, new ConfigDescription("PriceWeighted steepness: 0 = flat, 1 = inverse price, 2 = strongly favour cheap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); _discountBoost = cfg.Bind<bool>("Items", "DiscountBoost", true, "In PriceWeighted mode, boost the chance of items that are on sale."); _dropshipAutoOpen = cfg.Bind<bool>("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<float>("Replacements", "ChanceForTrap", 15f, new ConfigDescription("Per-slot % chance to replace the item with a trap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); _chanceForMonster = cfg.Bind<float>("Replacements", "ChanceForMonster", 10f, new ConfigDescription("Per-slot % chance to replace the item with a monster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); _priority = cfg.Bind<string>("Replacements", "Priority", "Monster", new ConfigDescription("Winner when both a trap and a monster roll hit the same slot.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Monster", "Trap" }), Array.Empty<object>())); _chanceForAllTraps = cfg.Bind<float>("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<float>(0f, 100f), Array.Empty<object>())); _chanceForAllMonsters = cfg.Bind<float>("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<float>(0f, 100f), Array.Empty<object>())); _allowedTraps = cfg.Bind<string>("Traps", "AllowedTraps", "Turret, Landmine", "Comma-separated whitelist of traps. Non-empty = only these; empty = allow all except BlockedTraps."); _blockedTraps = cfg.Bind<string>("Traps", "BlockedTraps", "", "Comma-separated blacklist of traps (used only when AllowedTraps is empty)."); _allowedMonsters = cfg.Bind<string>("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<string>("Monsters", "BlockedMonsters", "", "Comma-separated blacklist of monsters, removed on top of the whitelist."); _allowedItems = cfg.Bind<string>("ItemFilters", "AllowedItems", "", "Comma-separated whitelist of shop items. Non-empty = only these; empty = allow all except BlockedItems."); _blockedItems = cfg.Bind<string>("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<string> SplitCsv(string raw) { List<string> list = new List<string>(); 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<DeliverableItem> 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<TrapPrefab> list2 = TrapMonsterProvider.BuildTrapPool(); List<EnemyType> 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<DeliverableItem> list4 = new List<DeliverableItem>(); List<TrapPrefab> list5 = new List<TrapPrefab>(); List<EnemyType> list6 = new List<EnemyType>(); List<string> list7 = new List<string>(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<int> list8 = new List<int>(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<Vector3> 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<TrapPrefab> trapPool, List<EnemyType> 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<Trigger> _triggers = new List<Trigger>(); 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 "<null>"; } return Item.itemName; } } } internal static class ItemListProvider { private static Terminal _terminal; private static Terminal FindTerminal() { if ((Object)(object)_terminal == (Object)null) { _terminal = Object.FindObjectOfType<Terminal>(); } return _terminal; } internal static Terminal GetTerminal() { return FindTerminal(); } internal static void Reset() { _terminal = null; } internal static List<DeliverableItem> BuildPool() { DeliveryConfig cfg = Plugin.Cfg; List<DeliverableItem> list = new List<DeliverableItem>(); 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<string> hashSet = Names.NormalizedSet(cfg.AllowedItems); HashSet<string> 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<GrabbableObject>() == (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<DeliverableItem> 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<string> NormalizedSet(IEnumerable<string> raw) { HashSet<string> hashSet = new HashSet<string>(); 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<string> 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<string> candidatesRaw, HashSet<string> set) { if (set == null || set.Count == 0) { return false; } List<string> list = new List<string>(); 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<string> candidatesRaw, HashSet<string> 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<DeliveryScheduler>(); 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<Vector3> 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<Vector3> list = new List<Vector3>(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<ItemDropship>(); 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<GameObject>(item.spawnPrefab, val2, val5, val4); GrabbableObject component = val6.GetComponent<GrabbableObject>(); NetworkObject component2 = val6.GetComponent<NetworkObject>(); 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<int> itemIndices, List<TrapPrefab> traps, List<EnemyType> monsters) { Terminal terminal = ItemListProvider.GetTerminal(); if ((Object)(object)terminal == (Object)null || terminal.orderedItemsFromTerminal == null || terminal.buyableItemsList == null) { return false; } ItemDropship val = Object.FindObjectOfType<ItemDropship>(); 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<TrapPrefab> traps, List<EnemyType> 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<TrapPrefab> traps, List<EnemyType> 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<GameObject>(trap.Prefab, position, val); NetworkObject component = val2.GetComponent<NetworkObject>(); 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<TrapPrefab> BuildTrapPool() { DeliveryConfig cfg = Plugin.Cfg; List<TrapPrefab> list = new List<TrapPrefab>(); HashSet<string> allow = Names.NormalizedSet(cfg.AllowedTraps); HashSet<string> 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<SpawnableMapObject[]> list = new List<SpawnableMapObject[]>(); 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<Turret>() != (Object)null) { turret = val2; } if ((Object)(object)mine == (Object)null && (Object)(object)val2.GetComponentInChildren<Landmine>() != (Object)null) { mine = val2; } } } if ((Object)(object)turret != (Object)null && (Object)(object)mine != (Object)null) { break; } } } private static void TryAdd(List<TrapPrefab> list, string name, GameObject prefab, HashSet<string> allow, HashSet<string> 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<EnemyType> BuildMonsterPool() { DeliveryConfig cfg = Plugin.Cfg; List<EnemyType> list = new List<EnemyType>(); HashSet<string> hashSet = Names.NormalizedSet(cfg.AllowedMonsters); HashSet<string> 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<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> list2 = new List<string>(); EnemyType[] array = Resources.FindObjectsOfTypeAll<EnemyType>(); 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<string> list3 = new List<string>(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) : "<empty>"))); } 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"; } }