Decompiled source of RandomDelivery v1.4.5

RandomDelivery.dll

Decompiled a day ago
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<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";
	}
}