Decompiled source of PeakZombieLoot v1.0.1

plugins/PeakZombieLoot.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
using UnityEngine.SceneManagement;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Arman Ossi Loko")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Fake Game Developers")]
[assembly: AssemblyDescription("Killed PEAK zombies have a chance to drop an item.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+02a5339c2a0ea634199ac2c1945d7bd5bb46363a")]
[assembly: AssemblyProduct("PeakZombieLoot")]
[assembly: AssemblyTitle("PeakZombieLoot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Peak.ZombieLoot
{
	internal static class GameCompat
	{
		internal const string ZombieTypeName = "MushroomZombie";

		internal const string DieMethodName = "Die";

		internal const string SyncStateRpc = "RPC_SyncState";

		internal const string PassOutRpc = "RPCA_PassOut";

		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		internal static Type? ZombieType => FindType("MushroomZombie");

		internal static int DeadStateValue
		{
			get
			{
				Type type = ZombieType?.GetNestedType("State", BindingFlags.Public | BindingFlags.NonPublic);
				if (type == null || !Enum.IsDefined(type, "Dead"))
				{
					return -1;
				}
				return (int)Enum.Parse(type, "Dead");
			}
		}

		internal static void PatchAll(Harmony harmony)
		{
			int num = 0;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				if (!type.IsDefined(typeof(HarmonyPatch), inherit: true))
				{
					continue;
				}
				try
				{
					List<MethodInfo> list3 = harmony.CreateClassProcessor(type).Patch();
					if (list3 != null && list3.Count > 0)
					{
						num++;
					}
					else
					{
						list.Add(type.Name);
					}
				}
				catch (Exception e)
				{
					list2.Add(type.Name);
					Plugin.Log.LogWarning((object)("Patch " + type.Name + " was not applied, so that feature is off: " + Unwrap(e).Message));
				}
			}
			string text = $"Harmony patches: {num} applied";
			if (list.Count > 0)
			{
				text += string.Format(", {0} skipped ({1})", list.Count, string.Join(", ", list));
			}
			if (list2.Count > 0)
			{
				text += string.Format(", {0} failed ({1}). A PEAK update may have changed the game", list2.Count, string.Join(", ", list2));
				Plugin.Log.LogWarning((object)(text + "."));
			}
			else
			{
				Plugin.Log.LogInfo((object)(text + "."));
			}
		}

		internal static void CheckNamedMembers()
		{
			List<string> list = new List<string>();
			Type type = FindType("MushroomZombie");
			if (type == null)
			{
				list.Add("MushroomZombie (zombie loot drop)");
			}
			else
			{
				if (type.GetMethod("Die", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) == null)
				{
					list.Add("MushroomZombie.Die (drop on zombie death)");
				}
				if (type.GetMethod("RPC_SyncState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) == null)
				{
					list.Add("MushroomZombie.RPC_SyncState (drop when a remote owner kills a zombie)");
				}
				Type nestedType = type.GetNestedType("State", BindingFlags.Public | BindingFlags.NonPublic);
				if (nestedType == null || !Enum.IsDefined(nestedType, "Dead"))
				{
					list.Add("MushroomZombie.State.Dead (recognize a dead zombie)");
				}
			}
			if (AccessTools.Method(typeof(Character), "RPCA_PassOut", (Type[])null, (Type[])null) == null)
			{
				list.Add("Character.RPCA_PassOut (drop on zombie knockout)");
			}
			if (list.Count > 0)
			{
				Plugin.Log.LogWarning((object)("These PEAK names were not found; a game update may have changed them: " + string.Join("; ", list) + "."));
			}
		}

		internal static bool IsGameMismatch(Exception e)
		{
			Exception ex = Unwrap(e);
			if (ex is MissingMemberException || ex is TypeLoadException || ex is BadImageFormatException)
			{
				return true;
			}
			return false;
		}

		internal static Type? FindType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType(name);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		internal static bool IsZombie(Component component)
		{
			if (ZombieType != null && (Object)(object)component != (Object)null)
			{
				return (Object)(object)component.GetComponent(ZombieType) != (Object)null;
			}
			return false;
		}

		private static Exception Unwrap(Exception e)
		{
			while (true)
			{
				bool flag = ((e is TargetInvocationException || e is TypeInitializationException || e is HarmonyException) ? true : false);
				if (!flag || e.InnerException == null)
				{
					break;
				}
				e = e.InnerException;
			}
			return e;
		}
	}
	internal static class ItemDropper
	{
		private static readonly HashSet<int> DroppedKeys = new HashSet<int>();

		private static readonly List<string> NamedItems = new List<string>();

		internal static void ClearRunState()
		{
			DroppedKeys.Clear();
		}

		internal static void ParseItems(string raw)
		{
			NamedItems.Clear();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			string[] array = raw.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length > 0)
				{
					NamedItems.Add(text2);
				}
			}
		}

		internal static void OnZombieDied(Component? source)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || !Plugin.DropsEnabled || (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient))
			{
				return;
			}
			Component val = ((GameCompat.ZombieType != null) ? (source.GetComponent(GameCompat.ZombieType) ?? source.GetComponentInParent(GameCompat.ZombieType)) : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = DropKey(val);
			if (num != 0 && !DroppedKeys.Add(num))
			{
				return;
			}
			if (!Plugin.AlwaysDrop && Random.value * 100f >= Plugin.ChancePercent)
			{
				Plugin.Log.LogInfo((object)("Zombie " + Describe(val) + " died; no drop (rolled miss)."));
				return;
			}
			Item val2 = PickItem();
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogWarning((object)"A zombie died but no drop item could be resolved. Check Items / LootPool.");
				return;
			}
			Vector3 val3 = DropOrigin(val);
			int dropCount = Plugin.DropCount;
			for (int i = 0; i < dropCount; i++)
			{
				Vector3 val4 = val3;
				if (dropCount > 1 || i > 0)
				{
					Vector2 val5 = Random.insideUnitCircle * 0.45f;
					val4 += new Vector3(val5.x, 0f, val5.y);
				}
				SpawnItem(val2, GroundPoint(val4));
			}
			Plugin.Log.LogInfo((object)$"Zombie {Describe(val)} dropped {dropCount}× {((Object)val2).name}.");
		}

		private static int DropKey(Component zombie)
		{
			PhotonView component = zombie.GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && component.ViewID != 0)
			{
				return component.ViewID;
			}
			return ((Object)zombie).GetInstanceID();
		}

		private static string Describe(Component zombie)
		{
			PhotonView component = zombie.GetComponent<PhotonView>();
			if (!((Object)(object)component != (Object)null) || component.ViewID == 0)
			{
				return ((Object)zombie).name;
			}
			return $"#{component.ViewID}";
		}

		private static Vector3 DropOrigin(Component zombie)
		{
			//IL_001d: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Character component = zombie.GetComponent<Character>();
			if ((Object)(object)component != (Object)null)
			{
				return component.Center;
			}
			return zombie.transform.position + Vector3.up * 0.6f;
		}

		private static Vector3 GroundPoint(Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = point + Vector3.up * 30f;
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(val, Vector3.down, ref val2, 80f, LayerMask.op_Implicit(HelperFunctions.terrainMapMask), (QueryTriggerInteraction)1))
			{
				return ((RaycastHit)(ref val2)).point + Vector3.up * 0.12f;
			}
			return point;
		}

		private static Item? PickItem()
		{
			if (NamedItems.Count > 0)
			{
				return PickNamedItem();
			}
			return PickFromLootPool();
		}

		private static Item? PickNamedItem()
		{
			ItemDatabase instance = SingletonAsset<ItemDatabase>.Instance;
			if (((DatabaseAsset<ItemDatabase, Item>)(object)instance)?.Objects == null || ((DatabaseAsset<ItemDatabase, Item>)(object)instance).Objects.Count == 0)
			{
				return null;
			}
			List<Item> list = new List<Item>();
			for (int i = 0; i < NamedItems.Count; i++)
			{
				Item val = FindItem(instance, NamedItems[i]);
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
				else
				{
					Plugin.Log.LogWarning((object)("Drop item '" + NamedItems[i] + "' was not found in the item catalog."));
				}
			}
			if (list.Count == 0)
			{
				return null;
			}
			return list[Random.Range(0, list.Count)];
		}

		private static Item? FindItem(ItemDatabase database, string needle)
		{
			Item val = null;
			for (int i = 0; i < ((DatabaseAsset<ItemDatabase, Item>)(object)database).Objects.Count; i++)
			{
				Item val2 = ((DatabaseAsset<ItemDatabase, Item>)(object)database).Objects[i];
				if (!((Object)(object)val2 == (Object)null))
				{
					string text = ((Object)val2).name ?? "";
					string text2 = ((val2.UIData != null) ? (val2.UIData.itemName ?? "") : "");
					if (text.Equals(needle, StringComparison.OrdinalIgnoreCase) || text2.Equals(needle, StringComparison.OrdinalIgnoreCase))
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null && (ContainsIgnoreCase(text, needle) || ContainsIgnoreCase(text2, needle)))
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static bool ContainsIgnoreCase(string haystack, string needle)
		{
			return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static Item? PickFromLootPool()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			if (!Enum.TryParse<SpawnPool>(Plugin.LootPoolName.Trim(), ignoreCase: true, out SpawnPool result) || (int)result == 0)
			{
				Plugin.Log.LogWarning((object)("LootPool '" + Plugin.LootPoolName + "' is not a PEAK SpawnPool name."));
				return null;
			}
			List<Item> allItemsInPool;
			try
			{
				allItemsInPool = LootData.GetAllItemsInPool(result);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)$"Could not read loot pool {result}: {ex.Message}");
				return null;
			}
			if (allItemsInPool == null || allItemsInPool.Count == 0)
			{
				Plugin.Log.LogWarning((object)$"Loot pool {result} is empty.");
				return null;
			}
			List<(Item, int)> list = new List<(Item, int)>();
			int num = 0;
			for (int i = 0; i < allItemsInPool.Count; i++)
			{
				Item val = allItemsInPool[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				try
				{
					if (!val.IsValidToSpawn())
					{
						continue;
					}
				}
				catch (Exception)
				{
					continue;
				}
				int num2 = 1;
				LootData component = ((Component)val).GetComponent<LootData>();
				if ((Object)(object)component != (Object)null && LootData.RarityWeights != null && LootData.RarityWeights.TryGetValue(component.Rarity, out var value) && value > 0)
				{
					num2 = value;
				}
				list.Add((val, num2));
				num += num2;
			}
			if (list.Count == 0 || num <= 0)
			{
				return allItemsInPool[Random.Range(0, allItemsInPool.Count)];
			}
			int num3 = Random.Range(0, num);
			for (int j = 0; j < list.Count; j++)
			{
				num3 -= list[j].Item2;
				if (num3 < 0)
				{
					return list[j].Item1;
				}
			}
			return list[list.Count - 1].Item1;
		}

		private static void SpawnItem(Item prefab, Vector3 position)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			string name = ((Object)((Component)prefab).gameObject).name;
			GameObject val = ((!PhotonNetwork.InRoom) ? Object.Instantiate<GameObject>(((Component)prefab).gameObject, position, Quaternion.identity) : PhotonNetwork.InstantiateItemRoom(name, position, Quaternion.identity, false));
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Could not spawn drop '" + name + "'."));
			}
		}
	}
	[BepInPlugin("PeakZombieLoot", "PeakZombieLoot", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		private static ConfigEntry<bool>? dropsEnabled;

		private static ConfigEntry<float>? chancePercent;

		private static ConfigEntry<string>? items;

		private static ConfigEntry<string>? lootPool;

		private static ConfigEntry<int>? dropCount;

		private static ConfigEntry<bool>? dropOnPassOut;

		private static ConfigEntry<bool>? alwaysDrop;

		private static ConfigEntry<bool>? debugMode;

		private DateTime lastConfigWriteUtc;

		private float nextConfigCheck;

		private Coroutine? shoreSpawnRoutine;

		private static readonly HashSet<string> reportedGuardFailures = new HashSet<string>();

		public const string Id = "PeakZombieLoot";

		internal static ManualLogSource Log { get; private set; } = null;

		internal static Plugin Instance { get; private set; } = null;

		internal static bool DropsEnabled => dropsEnabled?.Value ?? true;

		internal static float ChancePercent => Mathf.Clamp(chancePercent?.Value ?? 25f, 0f, 100f);

		internal static string ItemsRaw => items?.Value ?? "FirstAidKit,Bandages";

		internal static string LootPoolName => lootPool?.Value ?? "LuggageRoots";

		internal static int DropCount => Mathf.Clamp(dropCount?.Value ?? 1, 1, 8);

		internal static bool DropOnPassOut => dropOnPassOut?.Value ?? true;

		internal static bool AlwaysDrop => alwaysDrop?.Value ?? false;

		internal static bool DebugMode => debugMode?.Value ?? false;

		public static string Name => "PeakZombieLoot";

		public static string Version => "1.0.1";

		private void Awake()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			dropsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Drops", "Enabled", true, "Host only: dead zombies can drop an item.");
			chancePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Drops", "ChancePercent", 25f, new ConfigDescription("Percent chance a dead zombie drops loot (0–100). Ignored when AlwaysDrop is true. Save the .cfg and start a new map (no need to quit PEAK).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			items = ((BaseUnityPlugin)this).Config.Bind<string>("Drops", "Items", "FirstAidKit,Bandages", "Comma-separated item names to pick from (prefab or UI name). Default is First Aid Kit and Bandages. Empty = roll from LootPool, the same way Roots luggage does.");
			lootPool = ((BaseUnityPlugin)this).Config.Bind<string>("Drops", "LootPool", "LuggageRoots", "PEAK SpawnPool used when Items is empty. Examples: LuggageRoots, LuggageBeach, LuggageJungle, LuggageMesa, LuggageGloom.");
			dropCount = ((BaseUnityPlugin)this).Config.Bind<int>("Drops", "Count", 1, new ConfigDescription("How many items to spawn when a drop succeeds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			dropOnPassOut = ((BaseUnityPlugin)this).Config.Bind<bool>("Drops", "DropOnPassOut", true, "Also roll when a zombie is knocked out (shotgun, fall, drowsy). Each zombie still drops at most once. Off = wait until PEAK marks the zombie Dead.");
			alwaysDrop = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "AlwaysDrop", false, "Host only: every dead zombie drops loot (skips ChancePercent).");
			debugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugMode", false, "Host spawns test zombies near the player on the Shore. Off = spawn nothing.");
			GameCompat.PatchAll(new Harmony(Name ?? "PeakZombieLoot"));
			RunStartupCheck("game name check", GameCompat.CheckNamedMembers);
			ItemDropper.ParseItems(ItemsRaw);
			shoreSpawnRoutine = ((MonoBehaviour)this).StartCoroutine(ShoreTestSpawns.WhenTheShoreIsReady());
			SceneManager.sceneLoaded += OnSceneLoaded;
			lastConfigWriteUtc = SafeConfigWriteTime();
			Log.LogInfo((object)("Plugin " + Name + " is loaded."));
		}

		internal static void Guard(string what, Action action)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				if (reportedGuardFailures.Add(what))
				{
					Log.LogError((object)$"{what} failed and was skipped: {arg}");
				}
			}
		}

		private static void RunStartupCheck(string what, Action check)
		{
			try
			{
				check();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Startup " + what + " failed (" + ex.GetType().Name + ": " + ex.Message + ")."));
			}
		}

		private DateTime SafeConfigWriteTime()
		{
			try
			{
				return File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
			}
			catch (Exception)
			{
				return DateTime.MinValue;
			}
		}

		private void Update()
		{
			if (!(Time.unscaledTime < nextConfigCheck))
			{
				nextConfigCheck = Time.unscaledTime + 0.5f;
				DateTime dateTime = SafeConfigWriteTime();
				if (!(dateTime == lastConfigWriteUtc))
				{
					ReloadConfigFromDisk("cfg changed");
				}
			}
		}

		private void ReloadConfigFromDisk(string reason)
		{
			try
			{
				lastConfigWriteUtc = SafeConfigWriteTime();
				((BaseUnityPlugin)this).Config.Reload();
				ItemDropper.ParseItems(ItemsRaw);
				Log.LogInfo((object)$"Config reloaded ({reason}); drop chance {ChancePercent:0.#}%, items='{ItemsRaw}', pool={LootPoolName}.");
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Config reload failed (" + reason + "): " + ex.Message));
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			ReloadConfigFromDisk("map load");
			if ((int)mode == 0)
			{
				ItemDropper.ClearRunState();
			}
			if (shoreSpawnRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(shoreSpawnRoutine);
			}
			shoreSpawnRoutine = ((MonoBehaviour)this).StartCoroutine(ShoreTestSpawns.WhenTheShoreIsReady());
		}
	}
	internal static class ShoreTestSpawns
	{
		private const int ZombieCount = 3;

		internal static IEnumerator WhenTheShoreIsReady()
		{
			if (!Plugin.DebugMode)
			{
				yield break;
			}
			WaitForSecondsRealtime wait = new WaitForSecondsRealtime(1f);
			bool spawnedShore = false;
			while (!spawnedShore)
			{
				yield return wait;
				if (!Plugin.DebugMode)
				{
					break;
				}
				if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && !LoadingScreenHandler.loading)
				{
					Character localCharacter = Character.localCharacter;
					if (!((Object)(object)localCharacter == (Object)null) && MapHandler.ExistsAndInitialized && (int)Singleton<MapHandler>.Instance.GetCurrentBiome() == 0)
					{
						SpawnNear(localCharacter);
						spawnedShore = true;
					}
				}
			}
		}

		private static void SpawnNear(Character player)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0045: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			Vector3 center = player.Center;
			int num = 0;
			RaycastHit val4 = default(RaycastHit);
			for (int i = 0; i < 3; i++)
			{
				float num2 = (float)i * 120f;
				Vector3 val = Quaternion.Euler(0f, num2, 0f) * (((Component)player).transform.forward * 14f);
				Vector3 val2 = center + val;
				Vector3 val3 = val2 + Vector3.up * 30f;
				if (Physics.Raycast(val3, Vector3.down, ref val4, 80f, LayerMask.op_Implicit(HelperFunctions.terrainMapMask), (QueryTriggerInteraction)1))
				{
					val2 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.5f;
				}
				try
				{
					PhotonNetwork.Instantiate("MushroomZombie", val2, Quaternion.identity, (byte)0, (object[])null);
					num++;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Could not spawn a Shore test zombie: " + ex.Message));
					return;
				}
			}
			Plugin.Log.LogInfo((object)$"Spawned {num} test zombies on the Shore.");
		}
	}
	[HarmonyPatch]
	internal static class ZombieDiePatch
	{
		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase? TargetMethod()
		{
			return GameCompat.ZombieType?.GetMethod("Die", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static void Postfix(Component __instance)
		{
			Plugin.Guard("zombie die drop", delegate
			{
				ItemDropper.OnZombieDied(__instance);
			});
		}
	}
	[HarmonyPatch]
	internal static class ZombieSyncStatePatch
	{
		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase? TargetMethod()
		{
			return GameCompat.ZombieType?.GetMethod("RPC_SyncState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static void Postfix(Component __instance, int state)
		{
			if (state == GameCompat.DeadStateValue)
			{
				Plugin.Guard("zombie dead-state drop", delegate
				{
					ItemDropper.OnZombieDied(__instance);
				});
			}
		}
	}
	[HarmonyPatch]
	internal static class ZombiePassOutPatch
	{
		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method(typeof(Character), "RPCA_PassOut", (Type[])null, (Type[])null);
		}

		private static void Postfix(Character __instance)
		{
			if (Plugin.DropOnPassOut && !((Object)(object)__instance == (Object)null) && GameCompat.IsZombie((Component)(object)__instance))
			{
				Plugin.Guard("zombie pass-out drop", delegate
				{
					ItemDropper.OnZombieDied((Component?)(object)__instance);
				});
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}